git: b13a7008282a - main - posixshm: Fix a TOCTOU race in the FIOSSHMLPGCNF handler
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 25 Aug 2026 15:59:25 UTC
The branch main has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=b13a7008282aaa34e7021ac7b961600dbbc28bf3
commit b13a7008282aaa34e7021ac7b961600dbbc28bf3
Author: Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-08-24 18:15:38 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-08-25 15:46:24 +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: 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 04b6f4f6e07a..067a628fdab0 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;
@@ -1571,7 +1576,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;
@@ -1579,6 +1584,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);
@@ -1754,7 +1761,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;
};