aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2026-08-24 18:15:38 +0000
committerMark Johnston <markj@FreeBSD.org>2026-08-25 15:47:13 +0000
commit5990860ca841fddaef9acbb7e2cbbd372ce10081 (patch)
tree0afb67f7f61e2ef403e175ba170c642c134e48cc
parentc7cec6fa6e4aeca7488130e17f4fd1ad2c476fa0 (diff)
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: 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
-rw-r--r--sys/kern/uipc_shm.c27
-rw-r--r--sys/sys/mman.h2
2 files changed, 18 insertions, 11 deletions
diff --git a/sys/kern/uipc_shm.c b/sys/kern/uipc_shm.c
index 8341364f4d16..fa80eabc39a3 100644
--- a/sys/kern/uipc_shm.c
+++ b/sys/kern/uipc_shm.c
@@ -512,8 +512,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) {
/*
@@ -535,7 +533,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;
@@ -592,18 +592,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;
@@ -1575,7 +1580,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;
@@ -1583,6 +1588,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);
@@ -1758,7 +1765,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 ba478bc71c7c..e15cc4380b5b 100644
--- a/sys/sys/mman.h
+++ b/sys/sys/mman.h
@@ -293,7 +293,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;
};