From nobody Tue Jun 30 17:21:52 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4gqVLn4zwPz6jZ32 for ; Tue, 30 Jun 2026 17:21:53 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gqVLn0Zygz3sZd for ; Tue, 30 Jun 2026 17:21:53 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782840113; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=DSXQbMn6HWn9VWBwVdng/+enIiVwVVnVkBe7v5NzGno=; b=LB1AlBLIKeXYTF0ToUa5r291CLGdtMAV+aYLtCgV5SsILISFBYAG6CYbiAAwKHFAGaSU8C gJlsH5NkhZsk50vwjISZHAYKLsVaHGjz543u11TJMcxrjFtBTPBo7xWZxTAoEiqW8CayOC FsP1yZFpinGSvgDWCQb0YCUA81Qhf76CsEyCBI4eWhQ8vtbbqOXV0oiu9xIof+HSN8x6vU RIe7LEpg1NkO7qkciwjxriDBooMMdG8fI4tftmEykNctnku3QqUcQ6+4lKCNVfCVziheHA I6DqiIV1FVNUZuebhBkcAmSESs3qOwrIb274hbvRo9i+yHrEwQHS7RviOsgPzQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1782840113; a=rsa-sha256; cv=none; b=CD5dk5/wo8SGk2xTF2CAk/tDLCpFSqW9EFfUrdzTSCqA5NlOmhlSbUmq3XFA43xoMLlNvj EQFKd3haln2URtx+WbGk0aYVzcyskmXAY3KdjhX+oI/eSa5vo976tZIn8Aivnx2N38bC2L WVB+2N/IR8kewVTeJz8YHjJF0u2kz4vZ3RCw7XKx20dSShkxE9A7LCX+IGSg8qACg+75vz c/OEesqM7I+ON3QRdlohxImd2wUbz3BXuLoAftci9/tEp6tbCF/uHVxkRAfyZFIZptcNZM gTZPdFsLbJkRf8iGPJNYuLEyA4O4t/+FhcfF7LHuJWcNt/XwZ90shBQqxto8nw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782840113; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=DSXQbMn6HWn9VWBwVdng/+enIiVwVVnVkBe7v5NzGno=; b=i4xP57u/uda3L5+4rScGKkjCnJk6AIX7R7JVulmEbSLjJWm6lC3wYm3gzQUkKxiLFUbob3 pocs/gJfklP5XqdebaZoeJ6xVwp5GEXSMs+l/tKyiB0Q/cPpNmTNw8RhZltX2GR6VhZ6Sc 8QI7ZGZlzs1iEtL8MZiqJ99Tp1UbWopPRFMLnZhfvGrg/nfoVRPQtXoWS7ipBREXemdw/3 NCwUDlOHLG1SY7rYoBgta0JDQMYeyOs5RBI7n0fhKmTMuZ/jdYEWeNhwQyzDfJs8eHaPzu CrSQ0l022wyn9u992FFo9muclkrj/SfMzowyL39gVgXtD1dxi94NXAJ+oiQvsg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gqVLm6gQxztnY for ; Tue, 30 Jun 2026 17:21:52 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 45e16 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 30 Jun 2026 17:21:52 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Mark Johnston Subject: git: 958de92ab2dc - releng/15.1 - device_pager: Avoid double-insertion of pages into the pager list List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/releng/15.1 X-Git-Reftype: branch X-Git-Commit: 958de92ab2dc00498681d0ed8b11b2956c317c6d Auto-Submitted: auto-generated Date: Tue, 30 Jun 2026 17:21:52 +0000 Message-Id: <6a43fb30.45e16.7b790645@gitrepo.freebsd.org> The branch releng/15.1 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=958de92ab2dc00498681d0ed8b11b2956c317c6d commit 958de92ab2dc00498681d0ed8b11b2956c317c6d Author: Mark Johnston AuthorDate: 2026-06-22 14:40:22 +0000 Commit: Mark Johnston CommitDate: 2026-06-29 19:16:40 +0000 device_pager: Avoid double-insertion of pages into the pager list Unmanaged device pager objects maintain a linked list of pages that were returned from the fault handler. Initially, such pages are valid, but msync(MS_INVALIDATE) can mark them invalid (and clean). They are not removed from page tables (since they are unmanaged), but a subsequent mlock() call can trigger a page fault that is handled by the pager. dev_pager_getpages() then re-inserts the page into the linked list even though it's already present there. This patch fixes the problem by removing the linked list. OBJ_PG_DTOR is set, so vm_object_terminate_pages() does nothing, and dev_pager_dealloc() instead handles cleanup of the object. Add a regression test case which triggers a queue.h assertion failure in unpatched kernels. Note, in stable branches we should avoid changing the layout of struct vm_object. Approved by: so Security: FreeBSD-SA-26:37.vm Security: CVE-2026-49418 Reported by: slidybat Reviewed by: kib Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D57743 --- sys/vm/device_pager.c | 36 ++++++++++++++++++++---------------- sys/vm/vm_object.h | 2 +- sys/vm/vm_page.c | 6 ++++-- tests/sys/vm/mmap_test.c | 30 ++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 19 deletions(-) diff --git a/sys/vm/device_pager.c b/sys/vm/device_pager.c index 221969307ba4..9277d6255418 100644 --- a/sys/vm/device_pager.c +++ b/sys/vm/device_pager.c @@ -213,7 +213,8 @@ again: object1 = NULL; object->handle = handle; object->un_pager.devp.ops = ops; - TAILQ_INIT(&object->un_pager.devp.devp_pglist); + if (object->type == OBJT_DEVICE) + vm_object_set_flag(object, OBJ_PG_DTOR); TAILQ_INSERT_TAIL(&dev_pager_object_list, object, pager_object_list); mtx_unlock(&dev_pager_mtx); @@ -325,15 +326,12 @@ dev_pager_free_page(vm_object_t object, vm_page_t m) KASSERT((object->type == OBJT_DEVICE && (m->oflags & VPO_UNMANAGED) != 0), ("Managed device or page obj %p m %p", object, m)); - TAILQ_REMOVE(&object->un_pager.devp.devp_pglist, m, plinks.q); vm_page_putfake(m); } static void dev_pager_dealloc(vm_object_t object) { - vm_page_t m; - VM_OBJECT_WUNLOCK(object); object->un_pager.devp.ops->cdev_pg_dtor(object->un_pager.devp.handle); @@ -343,15 +341,25 @@ dev_pager_dealloc(vm_object_t object) VM_OBJECT_WLOCK(object); if (object->type == OBJT_DEVICE) { - /* - * Free up our fake pages. - */ - while ((m = TAILQ_FIRST(&object->un_pager.devp.devp_pglist)) - != NULL) { - if (vm_page_busy_acquire(m, VM_ALLOC_WAITFAIL) == 0) - continue; + struct pctrie_iter pages; + vm_page_t m; - dev_pager_free_page(object, m); + vm_page_iter_init(&pages, object); +restart: + VM_RADIX_FOREACH(m, &pages) { + if (!vm_page_busy_acquire(m, VM_ALLOC_WAITFAIL)) { + pctrie_iter_reset(&pages); + goto restart; + } + if (vm_page_iter_remove(&pages, m)) { + /* + * We could end up with invalid pages installed + * by the generic page fault handler. Typically + * these are replaced by the device pager. + */ + vm_page_free(m); + } else if ((m->flags & PG_FICTITIOUS) != 0) + dev_pager_free_page(object, m); } } object->handle = NULL; @@ -380,10 +388,6 @@ dev_pager_getpages(vm_object_t object, vm_page_t *ma, int count, int *rbehind, (object->type == OBJT_MGTDEVICE && (ma[0]->oflags & VPO_UNMANAGED) == 0), ("Wrong page type %p %p", ma[0], object)); - if (object->type == OBJT_DEVICE) { - TAILQ_INSERT_TAIL(&object->un_pager.devp.devp_pglist, - ma[0], plinks.q); - } if (rbehind) *rbehind = 0; if (rahead) diff --git a/sys/vm/vm_object.h b/sys/vm/vm_object.h index b056ebe9a4b7..694b9d77a12d 100644 --- a/sys/vm/vm_object.h +++ b/sys/vm/vm_object.h @@ -134,7 +134,7 @@ struct vm_object { * devp_pglist - list of allocated pages */ struct { - TAILQ_HEAD(, vm_page) devp_pglist; + void *spare[2]; const struct cdev_pager_ops *ops; void *handle; } devp; diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c index b39d665f9e0f..3753079f2fde 100644 --- a/sys/vm/vm_page.c +++ b/sys/vm/vm_page.c @@ -1363,8 +1363,10 @@ vm_page_putfake(vm_page_t m) KASSERT((m->oflags & VPO_UNMANAGED) != 0, ("managed %p", m)); KASSERT((m->flags & PG_FICTITIOUS) != 0, ("vm_page_putfake: bad page %p", m)); - vm_page_assert_xbusied(m); - vm_page_busy_free(m); + if (m->object != NULL) { + vm_page_assert_xbusied(m); + vm_page_busy_free(m); + } uma_zfree(fakepg_zone, m); } diff --git a/tests/sys/vm/mmap_test.c b/tests/sys/vm/mmap_test.c index 6bc30f73ca95..a94fb56c46cf 100644 --- a/tests/sys/vm/mmap_test.c +++ b/tests/sys/vm/mmap_test.c @@ -362,6 +362,35 @@ ATF_TC_BODY(mmap__maxprot_shm, tc) ATF_REQUIRE(close(fd) == 0); } +/* A regression test for a bug in the device pager. */ +ATF_TC_WITHOUT_HEAD(mmap__device_reinsert); +ATF_TC_BODY(mmap__device_reinsert, tc) +{ + void *p; + int fd; + + fd = open("/dev/devstat", O_RDONLY); + ATF_REQUIRE(fd >= 0); + + p = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd, 0); + ATF_REQUIRE(p != MAP_FAILED); + + /* + * Use mlock() to trigger a fault, since msync() will not actually + * remove the page from the process page tables. + */ + ATF_REQUIRE(mlock(p, getpagesize()) == 0); + ATF_REQUIRE(munlock(p, getpagesize()) == 0); + ATF_REQUIRE(msync(p, getpagesize(), MS_INVALIDATE) == 0); + ATF_REQUIRE(mlock(p, getpagesize()) == 0); + ATF_REQUIRE(munlock(p, getpagesize()) == 0); + ATF_REQUIRE(msync(p, getpagesize(), MS_INVALIDATE) == 0); + ATF_REQUIRE(mlock(p, getpagesize()) == 0); + ATF_REQUIRE(munlock(p, getpagesize()) == 0); + + ATF_REQUIRE(munmap(p, getpagesize()) == 0); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, mmap__map_at_zero); @@ -371,6 +400,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, mmap__write_only); ATF_TP_ADD_TC(tp, mmap__maxprot_basic); ATF_TP_ADD_TC(tp, mmap__maxprot_shm); + ATF_TP_ADD_TC(tp, mmap__device_reinsert); return (atf_no_error()); }