untrusted comment: verify with openbsd-78-base.pub RWS3/nvFmk4SWdNGJZ6HUvDrkWIhpPcs35uzzZSWgUA332/CGcH6lt7SMNTlRe1MuLflBzNwuBR35+51E/QTzsRpdlua9ESlAw8= OpenBSD 7.8 errata 062, September 30, 2026: A reference counting bug could lead to use-after-free in the kernel. The ipcs(1) command stopped working after ABI change. Apply by doing: signify -Vep /etc/signify/openbsd-78-base.pub -x 062_sysvipc.patch.sig \ -m - | (cd /usr/src && patch -p0) And then rebuild and install ipcs and a new kernel: cd /usr/src/usr.bin/ipcs make obj make make install KK=`sysctl -n kern.osversion | cut -d# -f1` cd /usr/src/sys/arch/`machine`/compile/$KK make obj make config make make install Index: usr.bin/ipcs/ipcs.c =================================================================== RCS file: /cvs/src/usr.bin/ipcs/ipcs.c,v diff -u -p -r1.27 ipcs.c --- usr.bin/ipcs/ipcs.c 28 Jun 2019 13:35:01 -0000 1.27 +++ usr.bin/ipcs/ipcs.c 20 Sep 2026 01:21:41 -0000 @@ -558,7 +558,7 @@ shm_sysctl(void) show_shminfo_hdr(); for (i = 0; i < shmsi->shminfo.shmmni; i++) { struct shmid_ds *shmptr = &shmsi->shmids[i]; - if (shmptr->shm_internal) + if (shmptr->shm_cpid) show_shminfo(shmptr->shm_atime, shmptr->shm_dtime, shmptr->shm_ctime, @@ -630,7 +630,7 @@ sem_sysctl(void) for (i = 0; i < semsi->seminfo.semmni; i++) { struct semid_ds *semaptr = &semsi->semids[i]; - if (semaptr->sem_base != NULL) + if (semaptr->sem_ctime != 0) show_seminfo(semaptr->sem_otime, semaptr->sem_ctime, IXSEQ_TO_IPCID(i, semaptr->sem_perm), Index: sys/kern/kern_sysctl.c =================================================================== RCS file: /cvs/src/sys/kern/kern_sysctl.c,v diff -u -p -r1.483.2.1 kern_sysctl.c --- sys/kern/kern_sysctl.c 14 Jul 2026 12:12:33 -0000 1.483.2.1 +++ sys/kern/kern_sysctl.c 20 Sep 2026 01:21:41 -0000 @@ -2686,6 +2686,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) { @@ -2790,9 +2809,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.81.2.1 sysv_shm.c --- sys/kern/sysv_shm.c 14 Jul 2026 12:12:33 -0000 1.81.2.1 +++ sys/kern/sysv_shm.c 20 Sep 2026 01:21:41 -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,11 +342,12 @@ 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 inbuf, *shmseg; + struct shmid_ds_kern *shmseg; + struct shmid_ds shmbuf; int error; if (cmd == IPC_SET) { - error = copyin(buf, &inbuf, sizeof(inbuf)); + error = copyin(buf, &shmbuf, sizeof(shmbuf)); if (error) return (error); } @@ -339,18 +359,36 @@ sys_shmctl(struct proc *p, void *v, regi case IPC_STAT: if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_R)) != 0) return (error); - error = copyout(shmseg, buf, sizeof(inbuf)); + + 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); break; case IPC_SET: if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_M)) != 0) return (error); - shmseg->shm_perm.uid = inbuf.shm_perm.uid; - shmseg->shm_perm.gid = inbuf.shm_perm.gid; + shmseg->shm_perm.uid = shmbuf.shm_perm.uid; + shmseg->shm_perm.gid = shmbuf.shm_perm.gid; shmseg->shm_perm.mode = (shmseg->shm_perm.mode & ~ACCESSPERMS) | - (inbuf.shm_perm.mode & ACCESSPERMS); + (shmbuf.shm_perm.mode & ACCESSPERMS); shmseg->shm_ctime = gettime(); break; case IPC_RMID: @@ -358,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: @@ -381,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; @@ -410,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; @@ -507,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; @@ -553,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); @@ -570,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);