From nobody Tue Jun 30 17:20:14 2026 X-Original-To: dev-commits-src-branches@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 4gqVJv1z5Yz6jXnn for ; Tue, 30 Jun 2026 17:20:15 +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 4gqVJv0sbCz3q7L for ; Tue, 30 Jun 2026 17:20:15 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782840015; 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=rlpyocW0gHdxlbKsnhrc3TVXQBiUjZOM+rR8QFZi0Fs=; b=rumn78FDQH56AItLfyxw0+AgM6J/do7utfkZaPzrYB05GcYCps9WBQcPtJv3c8nmwKTeon k7K80RXGJg6436u+7fG0/7G/7GY/ymRcKvGu7zPzlPixkhnov5jyHxNE9DMyggf9caBrHI TNp1eOYhUl6nZj0Z28ESp1Tpk5USI11E7RJ+uGkUD65oA5x5kGzxeCDuAyGQ2kqynK6oF4 5YecBjKfYveYV6X+sgUEFvuKGHOgJzJ8ep7qojkhuvabfufd5qv/qtmXw87M0tbjIRkvrM Zb60F0v/X1nSUe83nzs2N5rsx2l8VeJgSvhNeK/dCZZt3Nu0U2Fd8BwcxdB1dA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1782840015; a=rsa-sha256; cv=none; b=o7te8Ro/vojD+wpxEugBXwP54zgHGgNL1f5R28sJyqt96IzNbZ9YL3JZbVEjOUYAtjBO1u 6ghpAvtF9wIopbksvagLQi9Bj5yJx1DVIe+gdEtcmfs6W2pzFYe/9yQ1quIGYTBqjVretM NyeI2LaYkJZg45fID6ORitN2jUGscZKWSvDdqiFtZusgGvURZ7e1XngKhaZSnDAORiOy85 Px9y9v8wplo0XOvnjmtQLwi0jweT7w1lB4J1WakD9mfIUZ8e9TTexxADZzhGPzkZI5d9pa G7JcWL9JB5ZSgjybgmMr/wiPQzueWVVCs9PdLcxlA0a9o8Zn4x6dIu/XN7zMOA== 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=1782840015; 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=rlpyocW0gHdxlbKsnhrc3TVXQBiUjZOM+rR8QFZi0Fs=; b=Tn7UiZR7CvLivsD9aaz/OlQ4Kr+b0yO+4Mu8z5bAG2/oASOmD+fETO7oHQnvNe1ctYRub1 o2XHSgnKEQHC1TR9gTEXp4eQCoMzeDZC0XzhyepwLO0PzRZ3eFT79DLtfU2KDCjXkFtmDe xnU45s/FzDBgH68WFtx2izS/8KjFCy6e8V/3ItO8Tw3pXKNovAtSMtq2NkrGVksyWX6zFx KiQpOt8r7yOSXqo6CeQc7Lg7DHzohAlx1lAAizAoxrmEy6WN/6jOFwwzlFBgSQW+nXXejm 8JXGHkyfCOKMfgHXt6Yt8kOSi/aZV+J/KJvU8ICRGHa2tYMgJHvRRPXBT7WOLA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gqVJt6xpvzsyd for ; Tue, 30 Jun 2026 17:20:14 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 45a17 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 30 Jun 2026 17:20:14 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Mark Johnston Subject: git: 702f4c829c17 - stable/15 - posixshm: Fix handling of sendfile() with largepage objects List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@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/stable/15 X-Git-Reftype: branch X-Git-Commit: 702f4c829c171b9d63f23e0d6753c80572315bc3 Auto-Submitted: auto-generated Date: Tue, 30 Jun 2026 17:20:14 +0000 Message-Id: <6a43face.45a17.1033b74@gitrepo.freebsd.org> The branch stable/15 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=702f4c829c171b9d63f23e0d6753c80572315bc3 commit 702f4c829c171b9d63f23e0d6753c80572315bc3 Author: Mark Johnston AuthorDate: 2026-06-24 19:57:00 +0000 Commit: Mark Johnston CommitDate: 2026-06-30 17:00:27 +0000 posixshm: Fix handling of sendfile() with largepage objects sendfile(2) can transmit POSIX shared memory objects. Typically it will look up and wire each page before sending it to a socket; once transmission is complete, the page is unwired and typically released back into the page queues. sendfile() has an advisory flag, SF_NOCACHE, which means, "try to free the page once transmission is complete." This is implemented in vm_page_release(), which expects to operate on managed pages. Pages belonging a largepage object are de-facto wired not explicitly so. Thus, vm_page_release() will unwire and, having found no additional references, free the page. Because mappings of largepage objects are unmanaged, userspace can still access the now freed page. Fix the problem by explicitly wiring largepage pages. Make the VM object destructor responsible for unwiring and freeing them. Add a regression test. Approved by: so Security: FreeBSD-SA-26:44.posixshm Security: CVE-2026-49427 Reported by: Chris Jarrett-Davies Reviewed by: kib Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D57832 --- sys/kern/uipc_shm.c | 22 ++++++++-- sys/vm/vm_page.c | 23 +++++----- tests/sys/posixshm/posixshm_test.c | 89 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 13 deletions(-) diff --git a/sys/kern/uipc_shm.c b/sys/kern/uipc_shm.c index b1b6a8657d69..8341364f4d16 100644 --- a/sys/kern/uipc_shm.c +++ b/sys/kern/uipc_shm.c @@ -326,6 +326,7 @@ static void shm_largepage_phys_ctor(vm_object_t object, vm_prot_t prot, vm_ooffset_t foff, struct ucred *cred) { + object->flags |= OBJ_PG_DTOR; } static void @@ -333,11 +334,27 @@ shm_largepage_phys_dtor(vm_object_t object) { int psind; + VM_OBJECT_ASSERT_WLOCKED(object); + psind = object->un_pager.phys.data_val; if (psind != 0) { + struct pctrie_iter pages; + vm_page_t m; + bool removed __diagused; + + 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; + } + removed = vm_page_iter_remove(&pages, m); + KASSERT(!removed, ("%s: page %p not wired", __func__, m)); + vm_page_unwire(m, PQ_NONE); + } atomic_subtract_long(&count_largepages[psind], object->size / (pagesizes[psind] / PAGE_SIZE)); - vm_wire_sub(object->size); } else { KASSERT(object->size == 0, ("largepage phys obj %p not initialized bit size %#jx > 0", @@ -818,7 +835,7 @@ shm_dotruncate_largepage(struct shmfd *shmfd, off_t length, void *rl_cookie) if ((shmfd->shm_seals & F_SEAL_GROW) != 0) return (EPERM); - aflags = VM_ALLOC_NORMAL | VM_ALLOC_ZERO; + aflags = VM_ALLOC_NORMAL | VM_ALLOC_ZERO | VM_ALLOC_WIRED; if (shmfd->shm_lp_alloc_policy == SHM_LARGEPAGE_ALLOC_NOWAIT) aflags |= VM_ALLOC_WAITFAIL; try = 0; @@ -874,7 +891,6 @@ shm_dotruncate_largepage(struct shmfd *shmfd, off_t length, void *rl_cookie) object->size += OFF_TO_IDX(pagesizes[psind]); shmfd->shm_size += pagesizes[psind]; atomic_add_long(&count_largepages[psind], 1); - vm_wire_add(atop(pagesizes[psind])); } return (0); } diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c index 3753079f2fde..cd95cc620ad4 100644 --- a/sys/vm/vm_page.c +++ b/sys/vm/vm_page.c @@ -4272,6 +4272,9 @@ vm_page_unwire_managed(vm_page_t m, uint8_t nqueue, bool noreuse) { u_int old; + KASSERT(nqueue < PQ_COUNT, + ("vm_page_unwire: invalid queue %u request for page %p", + nqueue, m)); KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("%s: page %p is unmanaged", __func__, m)); @@ -4330,17 +4333,15 @@ vm_page_unwire_managed(vm_page_t m, uint8_t nqueue, bool noreuse) void vm_page_unwire(vm_page_t m, uint8_t nqueue) { - - KASSERT(nqueue < PQ_COUNT, - ("vm_page_unwire: invalid queue %u request for page %p", - nqueue, m)); + KASSERT(nqueue < PQ_COUNT || nqueue == PQ_NONE, + ("%s: invalid queue %u request for page %p", __func__, nqueue, m)); if ((m->oflags & VPO_UNMANAGED) != 0) { if (vm_page_unwire_noq(m) && m->ref_count == 0) vm_page_free(m); - return; + } else { + vm_page_unwire_managed(m, nqueue, false); } - vm_page_unwire_managed(m, nqueue, false); } /* @@ -4514,13 +4515,15 @@ vm_page_release_toq(vm_page_t m, uint8_t nqueue, const bool noreuse) void vm_page_release(vm_page_t m, int flags) { - vm_object_t object; - - KASSERT((m->oflags & VPO_UNMANAGED) == 0, - ("vm_page_release: page %p is unmanaged", m)); + if ((m->oflags & VPO_UNMANAGED) != 0) { + vm_page_unwire(m, PQ_NONE); + return; + } if ((flags & VPR_TRYFREE) != 0) { for (;;) { + vm_object_t object; + object = atomic_load_ptr(&m->object); if (object == NULL) break; diff --git a/tests/sys/posixshm/posixshm_test.c b/tests/sys/posixshm/posixshm_test.c index e816e2d1efa7..e4b57dec6311 100644 --- a/tests/sys/posixshm/posixshm_test.c +++ b/tests/sys/posixshm/posixshm_test.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -2148,6 +2149,93 @@ ATF_TC_BODY(largepage_reopen, tc) "close failed; errno=%d", errno); } +static unsigned char +largepage_sendfile_expected(size_t off) +{ + + return ((unsigned char)(off * 131 + (off >> 8))); +} + +ATF_TC_WITHOUT_HEAD(largepage_sendfile); +ATF_TC_BODY(largepage_sendfile, tc) +{ + static const int flags[] = { 0, SF_NOCACHE }; + char *addr; + off_t sbytes; + size_t ps[MAXPAGESIZES]; + int error, fd, pscnt, sd[2], status; + pid_t child; + + pscnt = pagesizes(ps, true); + + for (int i = 1; i < pscnt; i++) { + for (int fi = 0; fi < (int)nitems(flags); fi++) { + fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, + ps[i]); + addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + ATF_REQUIRE_MSG(addr != MAP_FAILED, + "mmap(%zu bytes) failed; error=%d", ps[i], errno); + + /* Fill with a verifiable pattern. */ + for (size_t j = 0; j < ps[i]; j++) + addr[j] = largepage_sendfile_expected(j); + + ATF_REQUIRE(socketpair(PF_LOCAL, SOCK_STREAM, 0, + sd) == 0); + + child = fork(); + ATF_REQUIRE_MSG(child != -1, + "fork() failed; error=%d", errno); + if (child == 0) { + char buf[BUFSIZ]; + ssize_t len; + size_t off, resid; + + (void)close(sd[0]); + off = 0; + for (resid = ps[i]; resid > 0; resid -= len) { + len = read(sd[1], buf, sizeof(buf)); + if (len <= 0) + _exit(1); + for (ssize_t k = 0; k < len; k++) { + if ((unsigned char)buf[k] != + largepage_sendfile_expected( + off + k)) + _exit(2); + } + off += len; + } + _exit(0); + } + ATF_REQUIRE(close(sd[1]) == 0); + + sbytes = 0; + error = sendfile(fd, sd[0], 0, ps[i], NULL, &sbytes, + flags[fi]); + ATF_REQUIRE_MSG(error == 0, + "sendfile() failed; error=%d flags=%#x", + errno, flags[fi]); + ATF_REQUIRE_MSG(sbytes == (off_t)ps[i], + "sendfile() short; sbytes=%jd expected=%zu flags=%#x", + (intmax_t)sbytes, ps[i], flags[fi]); + + ATF_REQUIRE(close(sd[0]) == 0); + + ATF_REQUIRE_MSG(waitpid(child, &status, 0) == child, + "waitpid() failed; error=%d", errno); + ATF_REQUIRE_MSG(WIFEXITED(status), + "child killed by signal %d", WTERMSIG(status)); + ATF_REQUIRE_MSG(WEXITSTATUS(status) == 0, + "child exited with status %d (flags=%#x)", + WEXITSTATUS(status), flags[fi]); + + ATF_REQUIRE(munmap(addr, ps[i]) == 0); + ATF_REQUIRE(close(fd) == 0); + } + } +} + ATF_TC_WITHOUT_HEAD(largepage_truncate); ATF_TC_BODY(largepage_truncate, tc) { @@ -2236,6 +2324,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, largepage_pkru); #endif ATF_TP_ADD_TC(tp, largepage_reopen); + ATF_TP_ADD_TC(tp, largepage_sendfile); ATF_TP_ADD_TC(tp, largepage_truncate); return (atf_no_error());