The branch releng/14.5 has been updated by markj:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=9c7f4633c57a3c13b2c0b60090ffa9b14d1c1ce9

commit 9c7f4633c57a3c13b2c0b60090ffa9b14d1c1ce9
Author:     Mark Johnston <[email protected]>
AuthorDate: 2026-08-24 18:15:38 +0000
Commit:     Mark Johnston <[email protected]>
CommitDate: 2026-08-24 19:04:38 +0000

    posixshm: Fix a TOCTOU race in the FIOSSHMLPGCNF handler
    
    The check for whether shm_lp_psind was assigned was unlocked.  This race
    can be exploited to create an object with psind==2 but with only
    pagesizes[1] worth of pages populated.  This in turn can be used to
    escalate privileges.
    
    Fix this by acquiring the rangelock earlier.  In shm_mmap_large(),
    assert that we hold the rangelock.  In shm_write(), annotate an unlocked
    load of shm_lp_psind.
    
    Approved by:    re (cperciva)
    Approved by:    so
    Security:       FreeBSD-SA-26:63.posixshm
    Security:       CVE-2026-58094
    Reported by:    tsune of GMO Cybersecurity by Ierae, Inc. working with 
TrendAI Zero Day Initiative
    Reviewed by:    kib
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D59104
---
 sys/kern/uipc_shm.c | 27 +++++++++++++++++----------
 sys/sys/mman.h      |  2 +-
 2 files changed, 18 insertions(+), 11 deletions(-)

diff --git a/sys/kern/uipc_shm.c b/sys/kern/uipc_shm.c
index 9c2f91ffe347..3cb4b9734066 100644
--- a/sys/kern/uipc_shm.c
+++ b/sys/kern/uipc_shm.c
@@ -506,8 +506,6 @@ shm_write(struct file *fp, struct uio *uio, struct ucred 
*active_cred,
        if (error)
                return (error);
 #endif
-       if (shm_largepage(shmfd) && shmfd->shm_lp_psind == 0)
-               return (EINVAL);
        foffset_lock_uio(fp, uio, flags);
        if (uio->uio_resid > OFF_MAX - uio->uio_offset) {
                /*
@@ -529,7 +527,9 @@ shm_write(struct file *fp, struct uio *uio, struct ucred 
*active_cred,
        else
                rl_cookie = shm_rangelock_wlock(shmfd, uio->uio_offset,
                    MAX(newsize, uio->uio_offset));
-       if ((shmfd->shm_seals & F_SEAL_WRITE) != 0) {
+       if (shm_largepage(shmfd) && shmfd->shm_lp_psind == 0) {
+               error = EINVAL;
+       } else if ((shmfd->shm_seals & F_SEAL_WRITE) != 0) {
                error = EPERM;
        } else {
                error = 0;
@@ -586,18 +586,23 @@ shm_ioctl(struct file *fp, u_long com, void *data, struct 
ucred *active_cred,
                if (!shm_largepage(shmfd))
                        return (ENOTTY);
                conf = data;
+               rl_cookie = shm_rangelock_wlock(shmfd, 0, OFF_MAX);
                if (shmfd->shm_lp_psind != 0 &&
-                   conf->psind != shmfd->shm_lp_psind)
+                   conf->psind != shmfd->shm_lp_psind) {
+                       shm_rangelock_unlock(shmfd, rl_cookie);
                        return (EINVAL);
+               }
                if (conf->psind <= 0 || conf->psind >= MAXPAGESIZES ||
-                   pagesizes[conf->psind] == 0)
+                   pagesizes[conf->psind] == 0) {
+                       shm_rangelock_unlock(shmfd, rl_cookie);
                        return (EINVAL);
+               }
                if (conf->alloc_policy != SHM_LARGEPAGE_ALLOC_DEFAULT &&
                    conf->alloc_policy != SHM_LARGEPAGE_ALLOC_NOWAIT &&
-                   conf->alloc_policy != SHM_LARGEPAGE_ALLOC_HARD)
+                   conf->alloc_policy != SHM_LARGEPAGE_ALLOC_HARD) {
+                       shm_rangelock_unlock(shmfd, rl_cookie);
                        return (EINVAL);
-
-               rl_cookie = shm_rangelock_wlock(shmfd, 0, OFF_MAX);
+               }
                shmfd->shm_lp_psind = conf->psind;
                shmfd->shm_lp_alloc_policy = conf->alloc_policy;
                shmfd->shm_object->un_pager.phys.data_val = conf->psind;
@@ -1591,7 +1596,7 @@ out:
 static int
 shm_mmap_large(struct shmfd *shmfd, vm_map_t map, vm_offset_t *addr,
     vm_size_t size, vm_prot_t prot, vm_prot_t max_prot, int flags,
-    vm_ooffset_t foff, struct thread *td)
+    vm_ooffset_t foff, struct thread *td, void *rl_cookie)
 {
        struct vmspace *vms;
        vm_map_entry_t next_entry, prev_entry;
@@ -1599,6 +1604,8 @@ shm_mmap_large(struct shmfd *shmfd, vm_map_t map, 
vm_offset_t *addr,
        int docow, error, rv, try;
        bool curmap;
 
+       rangelock_cookie_assert(rl_cookie, RA_LOCKED);
+
        if (shmfd->shm_lp_psind == 0)
                return (EINVAL);
 
@@ -1763,7 +1770,7 @@ shm_mmap(struct file *fp, vm_map_t map, vm_offset_t 
*addr, vm_size_t objsize,
        if (shm_largepage(shmfd)) {
                writecnt = false;
                error = shm_mmap_large(shmfd, map, addr, objsize, prot,
-                   maxprot, flags, foff, td);
+                   maxprot, flags, foff, td, rl_cookie);
        } else {
                if (writecnt) {
                        vm_pager_update_writecount(shmfd->shm_object, 0,
diff --git a/sys/sys/mman.h b/sys/sys/mman.h
index d444f02d3c89..0717edf6a926 100644
--- a/sys/sys/mman.h
+++ b/sys/sys/mman.h
@@ -291,7 +291,7 @@ struct shmfd {
        int             shm_flags;
        int             shm_seals;
 
-       /* largepage config */
+       /* largepage config, synchronized by the rangelock */
        int             shm_lp_psind;
        int             shm_lp_alloc_policy;
 };

Reply via email to