From nobody Tue Jun 30 17:20:37 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 4gqVKM1xCcz6jXxc for ; Tue, 30 Jun 2026 17:20:39 +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 4gqVKK68l4z3qht for ; Tue, 30 Jun 2026 17:20:37 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782840037; 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=rIM2X2eK+dyFSsVWxLHoQsRXpk7SGOEeO5h39BHCSYU=; b=WEdide1+2IyNWZSErMi0vnqP9GI+UYcS6eVb0cqSsahuNhi25O2222IQ2UfuVz4ZCs3JSd YGY95GL5wMSeskPuR0kal7FK/UJQ8JpkBX0YPwAQYXp35fKUlk3ggJ2P20BiNg3POUgzD4 BgIwiBP0F//VEh2pq06F9GRBBqWgUb2Acfu1yWM1ekZaIuZfWnfSF2xfqAOMmQt+AVPbdw qy14dhKrFxWrdCPf18oxG4HVso5N6z8HZA3St85F9fMiYTNLdWdgzyLr2n+Vm2q+3zTTz2 rdlT+XHqv90Sf+qVuvuUnTXh6yCOrQ7KZduZxgJP7sX2BbxOo+ytBH5+KMnw7Q== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1782840037; a=rsa-sha256; cv=none; b=V4c5bExktYXUc7xruynm66HqgA1DzknmFIkbd+gnvZTcK5i8XPzr8UueAQ6do3kiO0aICG s1PXvEMG9KWi9PWOMakbCHGCTw8XwyEqhe6TqiK3vbKlYeZ2Qod6yBS/KvySGd7GWypq0G L6ijfXE6Z0F4ZrbjznbGFYM/d9DmkhXsYQaSUbwJNHhopmWXBD5kNWnoYgobVXTi7QOcza o2OBPeT51gGWCCjcG+ARt4qcMFyQtXfNj6WaS+px0EKRSsSimNzaKuutwGg38/I86/6EqW 8oaZzzpNl1LS8CF5lAzy+8tm0yKFG6wEp5Tsvwr7nGY9rzSXJHOLkwC7Dkbmhw== 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=1782840037; 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=rIM2X2eK+dyFSsVWxLHoQsRXpk7SGOEeO5h39BHCSYU=; b=ULa57a/v6r0v6MTNvuVLfluCWjmKT3GpPz0Xkt6Q+oTYUFaw197paZvvqLZ/D+FshmDAdg g6yhZgRNDApFs89pq1n3NJv7VcIUTY4SiBg3+PGgJdFLARcyv/H2YbFzXLpyDtq9VnyLkr hCDcDi7A5ZH2JawtYKDCH7O9zYA5Md6fcHA0q/l9Z2JzTjL9p9xmWqr8m5tnr3/AURlAnN sqdpHtxJvseuKfsEHc6ogFy2w+IUDIrAIxHfZ/bsN/QCo5PVo6UKNIqWQ7eddlmvB+ubtD mDepJ2VJTZHlURzFBTXZx0Cfm4UEY0k6/+2kJkDpnQKNrGqiDETi73yu9cgVjA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gqVKK585bztDW for ; Tue, 30 Jun 2026 17:20:37 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 45d43 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 30 Jun 2026 17:20:37 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Mark Johnston Subject: git: 5272af920126 - releng/14.3 - posixshm: Fix handling of sendfile() with largepage objects 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/14.3 X-Git-Reftype: branch X-Git-Commit: 5272af92012650787a4a3c2a403faa500adf7e89 Auto-Submitted: auto-generated Date: Tue, 30 Jun 2026 17:20:37 +0000 Message-Id: <6a43fae5.45d43.39d91527@gitrepo.freebsd.org> The branch releng/14.3 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=5272af92012650787a4a3c2a403faa500adf7e89 commit 5272af92012650787a4a3c2a403faa500adf7e89 Author: Mark Johnston AuthorDate: 2026-06-24 19:57:00 +0000 Commit: Mark Johnston CommitDate: 2026-06-30 02:32:01 +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 | 19 ++++++-- sys/vm/vm_page.c | 23 +++++----- tests/sys/posixshm/posixshm_test.c | 89 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 13 deletions(-) diff --git a/sys/kern/uipc_shm.c b/sys/kern/uipc_shm.c index b3dd7a91d473..6a8614aa7d8a 100644 --- a/sys/kern/uipc_shm.c +++ b/sys/kern/uipc_shm.c @@ -323,6 +323,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 @@ -330,11 +331,24 @@ 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) { + vm_page_t m, mtmp; + bool removed __diagused; + +restart: + TAILQ_FOREACH_SAFE(m, &object->memq, listq, mtmp) { + if (!vm_page_busy_acquire(m, VM_ALLOC_WAITFAIL)) { + goto restart; + } + removed = vm_page_remove(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", @@ -856,7 +870,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; @@ -903,7 +917,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 181429488c93..f20de33810bf 100644 --- a/sys/vm/vm_page.c +++ b/sys/vm/vm_page.c @@ -4077,6 +4077,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)); @@ -4135,17 +4138,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); } /* @@ -4319,13 +4320,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 c792a85de9e3..dccc5add12a9 100644 --- a/tests/sys/posixshm/posixshm_test.c +++ b/tests/sys/posixshm/posixshm_test.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -2151,6 +2152,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); + + 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) { @@ -2239,6 +2327,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());