untrusted comment: verify with openbsd-79-base.pub RWTSdNN9A3yvWAEtBH1JFvUe5DEOWY1R2QrWpaWok7V2RL/1oSGknGNlH5q2uHmanokz15qQdTgTGr1yGgj9Q6c8V6bSRZO5fwI= OpenBSD 7.9 errata 026, September 30, 2026: A reference counting bug could lead to use-after-free in the kernel. Apply by doing: signify -Vep /etc/signify/openbsd-79-base.pub -x 026_sysvipc.patch.sig \ -m - | (cd /usr/src && patch -p0) And then rebuild and install a new kernel: KK=`sysctl -n kern.osversion | cut -d# -f1` cd /usr/src/sys/arch/`machine`/compile/$KK make obj make config make make install Index: sys/kern/kern_sysctl.c =================================================================== RCS file: /cvs/src/sys/kern/kern_sysctl.c,v diff -u -p -r1.491.2.1 kern_sysctl.c --- sys/kern/kern_sysctl.c 14 Jul 2026 12:13:54 -0000 1.491.2.1 +++ sys/kern/kern_sysctl.c 20 Sep 2026 01:02:08 -0000 @@ -2698,6 +2698,25 @@ struct semid_ds_kern { extern struct semid_ds_kern **sema_kern; #define sema sema_kern +struct shmid_ds_kern { + struct ipc_perm shm_perm; /* operation permission structure */ + int shm_segsz; /* size of segment in bytes */ + pid_t shm_lpid; /* process ID of last shm op */ + pid_t shm_cpid; /* process ID of creator */ + uint64_t shm_nattch; /* number of current attaches */ + time_t shm_atime; /* time of last shmat() */ + long __shm_atimensec; + time_t shm_dtime; /* time of last shmdt() */ + long __shm_dtimensec; + time_t shm_ctime; /* time of last change by shmctl() */ + long __shm_ctimensec; + void *shm_internal; /* implementation specific data */ +}; + +/* rename shmsegs for stable as it is declared as struct shmid_ds in sem.h */ +extern struct shmid_ds_kern **shmsegs_kern; +#define shmsegs shmsegs_kern + int sysctl_sysvipc(int *name, u_int namelen, void *where, size_t *sizep) { @@ -2803,9 +2822,32 @@ sysctl_sysvipc(int *name, u_int namelen, #ifdef SYSVSHM case KERN_SYSVIPC_SHM_INFO: if (shmsegs[i] != NULL) { - memcpy(&shmsi->shmids[i], shmsegs[i], - dssize); - shmsi->shmids[i].shm_internal = NULL; + shmsi->shmids[i].shm_perm = + shmsegs[i]->shm_perm; + shmsi->shmids[i].shm_segsz = + shmsegs[i]->shm_segsz; + shmsi->shmids[i].shm_lpid = + shmsegs[i]->shm_lpid; + shmsi->shmids[i].shm_cpid = + shmsegs[i]->shm_cpid; + if (shmsegs[i]->shm_nattch > SHRT_MAX) + shmsi->shmids[i].shm_nattch = + SHRT_MAX; + else + shmsi->shmids[i].shm_nattch = + shmsegs[i]->shm_nattch; + shmsi->shmids[i].shm_atime = + shmsegs[i]->shm_atime; + shmsi->shmids[i].__shm_atimensec = + shmsegs[i]->__shm_atimensec; + shmsi->shmids[i].shm_dtime = + shmsegs[i]->shm_dtime; + shmsi->shmids[i].__shm_dtimensec = + shmsegs[i]->__shm_dtimensec; + shmsi->shmids[i].shm_ctime = + shmsegs[i]->shm_ctime; + shmsi->shmids[i].__shm_ctimensec = + shmsegs[i]->__shm_ctimensec; } break; Index: sys/kern/sysv_shm.c =================================================================== RCS file: /cvs/src/sys/kern/sysv_shm.c,v diff -u -p -r1.82.2.1 sysv_shm.c --- sys/kern/sysv_shm.c 14 Jul 2026 12:13:54 -0000 1.82.2.1 +++ sys/kern/sysv_shm.c 20 Sep 2026 01:02:08 -0000 @@ -66,12 +66,31 @@ #include +struct shmid_ds_kern { + struct ipc_perm shm_perm; /* operation permission structure */ + int shm_segsz; /* size of segment in bytes */ + pid_t shm_lpid; /* process ID of last shm op */ + pid_t shm_cpid; /* process ID of creator */ + uint64_t shm_nattch; /* number of current attaches */ + time_t shm_atime; /* time of last shmat() */ + long __shm_atimensec; + time_t shm_dtime; /* time of last shmdt() */ + long __shm_dtimensec; + time_t shm_ctime; /* time of last change by shmctl() */ + long __shm_ctimensec; + void *shm_internal; /* implementation specific data */ +}; + +/* rename shmsegs for stable as it is declared as struct shmid_ds in sem.h */ +extern struct shmid_ds_kern **shmsegs_kern; +#define shmsegs shmsegs_kern + extern struct shminfo shminfo; -struct shmid_ds **shmsegs; /* linear mapping of shmid -> shmseg */ +struct shmid_ds_kern **shmsegs; /* linear mapping of shmid -> shmseg */ struct pool shm_pool; unsigned short *shmseqs; /* array of shm sequence numbers */ -struct shmid_ds *shm_find_segment_by_shmid(int); +struct shmid_ds_kern *shm_find_segment_by_shmid(int); /* * Provides the following externally accessible functions: @@ -82,7 +101,7 @@ struct shmid_ds *shm_find_segment_by_shm * shmsys(arg1, arg2, arg3, arg4); shm{at,ctl,dt,get}(arg2, arg3, arg4) * * Structures: - * shmsegs (an array of 'struct shmid_ds *') + * shmsegs (an array of 'struct shmid_ds_kern *') * per proc 'struct shmmap_head' with an array of 'struct shmmap_state' */ @@ -109,7 +128,7 @@ struct shmmap_head { }; int shm_find_segment_by_key(key_t); -void shm_deallocate_segment(struct shmid_ds *); +void shm_deallocate_segment(struct shmid_ds_kern *); int shm_delete_mapping(struct vmspace *, struct shmmap_state *); int shmget_existing(struct proc *, struct sys_shmget_args *, int, int, register_t *); @@ -119,7 +138,7 @@ int shmget_allocate_segment(struct proc int shm_find_segment_by_key(key_t key) { - struct shmid_ds *shmseg; + struct shmid_ds_kern *shmseg; int i; for (i = 0; i < shminfo.shmmni; i++) { @@ -130,11 +149,11 @@ shm_find_segment_by_key(key_t key) return (-1); } -struct shmid_ds * +struct shmid_ds_kern * shm_find_segment_by_shmid(int shmid) { int segnum; - struct shmid_ds *shmseg; + struct shmid_ds_kern *shmseg; segnum = IPCID_TO_IX(shmid); if (segnum < 0 || segnum >= shminfo.shmmni || @@ -145,7 +164,7 @@ shm_find_segment_by_shmid(int shmid) } void -shm_deallocate_segment(struct shmid_ds *shmseg) +shm_deallocate_segment(struct shmid_ds_kern *shmseg) { struct shm_handle *shm_handle; size_t size; @@ -161,7 +180,7 @@ shm_deallocate_segment(struct shmid_ds * int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s) { - struct shmid_ds *shmseg; + struct shmid_ds_kern *shmseg; int segnum, deallocate = 0; vaddr_t end; @@ -169,7 +188,7 @@ shm_delete_mapping(struct vmspace *vm, s if (segnum < 0 || segnum >= shminfo.shmmni || (shmseg = shmsegs[segnum]) == NULL) return (EINVAL); - if ((--shmseg->shm_nattch <= 0) && + if ((--shmseg->shm_nattch == 0) && (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { deallocate = 1; shm_last_free = segnum; @@ -218,7 +237,7 @@ sys_shmat(struct proc *p, void *v, regis } */ *uap = v; int error, i, flags = 0; struct ucred *cred = p->p_ucred; - struct shmid_ds *shmseg; + struct shmid_ds_kern *shmseg; struct shmmap_head *shmmap_h; struct shmmap_state *shmmap_s; struct shm_handle *shm_handle; @@ -292,11 +311,11 @@ allocated: shm_handle->shm_object, 0, 0, UVM_MAPFLAG(prot, prot, MAP_INHERIT_SHARE, MADV_RANDOM, flags)); if (error) { - if ((--shmseg->shm_nattch <= 0) && + if ((--shmseg->shm_nattch == 0) && (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { - shm_deallocate_segment(shmseg); shm_last_free = IPCID_TO_IX(SCARG(uap, shmid)); shmsegs[shm_last_free] = NULL; + shm_deallocate_segment(shmseg); } else { uao_detach(shm_handle->shm_object); } @@ -323,7 +342,8 @@ sys_shmctl(struct proc *p, void *v, regi int cmd = SCARG(uap, cmd); void *buf = SCARG(uap, buf); struct ucred *cred = p->p_ucred; - struct shmid_ds shmbuf, *shmseg; + struct shmid_ds_kern *shmseg; + struct shmid_ds shmbuf; int error; if (cmd == IPC_SET) { @@ -339,8 +359,24 @@ sys_shmctl(struct proc *p, void *v, regi case IPC_STAT: if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_R)) != 0) return (error); - memcpy(&shmbuf, shmseg, sizeof(shmbuf)); - shmbuf.shm_internal = NULL; + + memset(&shmbuf, 0, sizeof(shmbuf)); + shmbuf.shm_perm = shmseg->shm_perm; + shmbuf.shm_lpid = shmseg->shm_lpid; + shmbuf.shm_segsz = shmseg->shm_segsz; + shmbuf.shm_lpid = shmseg->shm_lpid; + shmbuf.shm_cpid = shmseg->shm_cpid; + if (shmseg->shm_nattch > SHRT_MAX) + shmbuf.shm_nattch = SHRT_MAX; + else + shmbuf.shm_nattch = shmseg->shm_nattch; + shmbuf.shm_atime = shmseg->shm_atime; + shmbuf.__shm_atimensec = shmseg->__shm_atimensec; + shmbuf.shm_dtime = shmseg->shm_dtime; + shmbuf.__shm_dtimensec = shmseg->__shm_dtimensec; + shmbuf.shm_ctime = shmseg->shm_ctime; + shmbuf.__shm_ctimensec = shmseg->__shm_ctimensec; + error = copyout(&shmbuf, buf, sizeof(shmbuf)); if (error) return (error); @@ -360,10 +396,10 @@ sys_shmctl(struct proc *p, void *v, regi return (error); shmseg->shm_perm.key = IPC_PRIVATE; shmseg->shm_perm.mode |= SHMSEG_REMOVED; - if (shmseg->shm_nattch <= 0) { - shm_deallocate_segment(shmseg); + if (shmseg->shm_nattch == 0) { shm_last_free = IPCID_TO_IX(shmid); shmsegs[shm_last_free] = NULL; + shm_deallocate_segment(shmseg); } break; case SHM_LOCK: @@ -383,7 +419,7 @@ shmget_existing(struct proc *p, } */ *uap, int mode, int segnum, register_t *retval) { - struct shmid_ds *shmseg; + struct shmid_ds_kern *shmseg; struct ucred *cred = p->p_ucred; int error; @@ -412,7 +448,7 @@ shmget_allocate_segment(struct proc *p, key_t key; int segnum; struct ucred *cred = p->p_ucred; - struct shmid_ds *shmseg; + struct shmid_ds_kern *shmseg; struct shm_handle *shm_handle; int error = 0; @@ -509,7 +545,7 @@ shmfork(struct vmspace *vm1, struct vmsp { struct shmmap_head *shmmap_h; struct shmmap_state *shmmap_s; - struct shmid_ds *shmseg; + struct shmid_ds_kern *shmseg; size_t size; int i; @@ -555,9 +591,9 @@ shminit(void) { pool_init(&shm_pool, - sizeof(struct shmid_ds) + sizeof(struct shm_handle), 0, + sizeof(struct shmid_ds_kern) + sizeof(struct shm_handle), 0, IPL_NONE, PR_WAITOK, "shmpl", NULL); - shmsegs = mallocarray(shminfo.shmmni, sizeof(struct shmid_ds *), + shmsegs = mallocarray(shminfo.shmmni, sizeof(struct shmid_ds_kern *), M_SHM, M_WAITOK|M_ZERO); shmseqs = mallocarray(shminfo.shmmni, sizeof(unsigned short), M_SHM, M_WAITOK|M_ZERO); @@ -572,15 +608,15 @@ shminit(void) void shm_reallocate(int val) { - struct shmid_ds **newsegs; + struct shmid_ds_kern **newsegs; unsigned short *newseqs; - newsegs = mallocarray(val, sizeof(struct shmid_ds *), + newsegs = mallocarray(val, sizeof(struct shmid_ds_kern *), M_SHM, M_WAITOK | M_ZERO); memcpy(newsegs, shmsegs, - shminfo.shmmni * sizeof(struct shmid_ds *)); + shminfo.shmmni * sizeof(struct shmid_ds_kern *)); free(shmsegs, M_SHM, - shminfo.shmmni * sizeof(struct shmid_ds *)); + shminfo.shmmni * sizeof(struct shmid_ds_kern *)); shmsegs = newsegs; newseqs = mallocarray(val, sizeof(unsigned short), M_SHM, M_WAITOK | M_ZERO);