untrusted comment: verify with openbsd-79-base.pub
RWTSdNN9A3yvWBJqB2ihf3w3aBGu2myAIRwsSnMls24ZuFR89K9KGXD/lRTW68QyHGCWzae6TPTn8/tnuzio41kemVb8mMnD0As=

OpenBSD 7.9 errata 005, July 16, 2026:

Fix numerous problems in the System V (sem, msg, ipc) system call
layers.

Apply by doing:
    signify -Vep /etc/signify/openbsd-79-base.pub -x 005_sysv.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 kern_sysctl.c
--- sys/kern/kern_sysctl.c	16 Apr 2026 20:03:14 -0000	1.491
+++ sys/kern/kern_sysctl.c	13 Jul 2026 18:27:03 -0000
@@ -2669,6 +2669,24 @@ sysctl_diskinit(int update, struct proc 
 }
 
 #if defined(SYSVMSG) || defined(SYSVSEM) || defined(SYSVSHM)
+/*
+ * In-kernel semaphore implementation
+ */
+struct semid_ds_kern {
+	struct ipc_perm	sem_perm;	/* operation permission struct */
+	struct sem	*sem_base;	/* pointer to first semaphore in set */
+	unsigned short	sem_nsems;	/* number of sems in set */
+	time_t		sem_otime;	/* last operation time */
+	time_t		sem_ctime;	/* last change time */
+	    				/* Times measured in secs since */
+	    				/* 00:00:00 GMT, Jan. 1, 1970 */
+	struct refcnt	sem_refcnt;
+};
+
+/* rename sema for stable as it is declared as struct semid_ds in sem.h */
+extern struct semid_ds_kern **sema_kern;
+#define sema sema_kern
+
 int
 sysctl_sysvipc(int *name, u_int namelen, void *where, size_t *sizep)
 {
@@ -2758,22 +2776,27 @@ sysctl_sysvipc(int *name, u_int namelen,
 			switch (*name) {
 #ifdef SYSVSEM
 			case KERN_SYSVIPC_SEM_INFO:
-				if (sema[i] != NULL)
-					memcpy(&semsi->semids[i], sema[i],
-					    dssize);
-				else
-					memset(&semsi->semids[i], 0, dssize);
-				semsi->semids[i].sem_base = NULL;
+				if (sema[i] != NULL) {
+					semsi->semids[i].sem_perm =
+					    sema[i]->sem_perm;
+					semsi->semids[i].sem_nsems =
+					    sema[i]->sem_nsems;
+					semsi->semids[i].sem_otime =
+					    sema[i]->sem_otime;
+					semsi->semids[i].sem_ctime =
+					    sema[i]->sem_ctime;
+				}
+
 				break;
 #endif
 #ifdef SYSVSHM
 			case KERN_SYSVIPC_SHM_INFO:
-				if (shmsegs[i] != NULL)
+				if (shmsegs[i] != NULL) {
 					memcpy(&shmsi->shmids[i], shmsegs[i],
 					    dssize);
-				else
-					memset(&shmsi->shmids[i], 0, dssize);
-				shmsi->shmids[i].shm_internal = NULL;
+					shmsi->shmids[i].shm_internal = NULL;
+				}
+
 				break;
 #endif
 			}
Index: sys/kern/sysv_msg.c
===================================================================
RCS file: /cvs/src/sys/kern/sysv_msg.c,v
diff -u -p -r1.41 sysv_msg.c
--- sys/kern/sysv_msg.c	11 Apr 2023 00:45:09 -0000	1.41
+++ sys/kern/sysv_msg.c	13 Jul 2026 18:27:04 -0000
@@ -56,11 +56,22 @@ void msg_free(struct msg *);
 void msg_enqueue(struct que *, struct msg *, struct proc *);
 void msg_dequeue(struct que *, struct msg *, struct proc *);
 struct msg *msg_lookup(struct que *, int);
-int msg_copyin(struct msg *, const char *, size_t, struct proc *);
-int msg_copyout(struct msg *, char *, size_t *, struct proc *);
+int msg_copyin(struct msg *, const char *, size_t);
+int msg_copyout(struct msg *, char *, size_t *, int);
+
+#define	MSGQ_RCVWAIT	0x40
+#define	MSGQ_RCVWAITING	0x80
 
 struct	pool sysvmsgpl;
-struct	msginfo msginfo;
+
+struct msginfo msginfo = {
+	.msgmax = MSGMAX,
+	.msgmni = MSGMNI,
+	.msgmnb = MSGMNB,
+	.msgtql = MSGTQL,
+	.msgssz = MSGSSZ,
+	.msgseg = MSGSEG,
+};
 
 TAILQ_HEAD(, que) msg_queues;
 
@@ -72,13 +83,6 @@ int maxmsgs;
 void
 msginit(void)
 {
-	msginfo.msgmax = MSGMAX;
-	msginfo.msgmni = MSGMNI;
-	msginfo.msgmnb = MSGMNB;
-	msginfo.msgtql = MSGTQL;
-	msginfo.msgssz = MSGSSZ;
-	msginfo.msgseg = MSGSEG;
-
 	pool_init(&sysvmsgpl, sizeof(struct msg), 0, IPL_NONE, PR_WAITOK,
 	    "sysvmsgpl", NULL);
 
@@ -280,7 +284,7 @@ sys_msgsnd(struct proc *p, void *v, regi
 	}
 
 	/* msg_copyin frees msg on error */
-	if ((error = msg_copyin(msg, (const char *)SCARG(uap, msgp), msgsz, p)))
+	if ((error = msg_copyin(msg, (const char *)SCARG(uap, msgp), msgsz)))
 		goto out;
 
 	msg_enqueue(que, msg, p);
@@ -326,27 +330,47 @@ sys_msgrcv(struct proc *p, void *v, regi
 
 	QREF(que);
 
+again:
 	/* msg_lookup handles matching; sleeping gets handled here */
 	while ((msg = msg_lookup(que, msgtyp)) == NULL) {
 
 		if (SCARG(uap, msgflg) & IPC_NOWAIT) {
 			error = ENOMSG;
-			goto out;
+			goto rele;
 		}
 
 		que->que_flags |= MSGQ_READERS;
 		if ((error = tsleep_nsec(que, PZERO|PCATCH, "msgwait", INFSLP)))
-			goto out;
+			goto rele;
 
 		/* make sure the queue still alive */
 		if (que->que_flags & MSGQ_DYING) {
 			error = EIDRM;
-			goto out;
+			goto rele;
+		}
+	}
+
+	/* acquired message could be delivering by concurrent thread */
+	if (que->que_flags & MSGQ_RCVWAIT) {
+		que->que_flags |= MSGQ_RCVWAITING;
+		error = tsleep_nsec(&que->que_flags, PZERO | PCATCH,
+		    "msgrcv", INFSLP);
+		if (error)
+			goto rele;
+
+		/* make sure the queue still alive */
+		if (que->que_flags & MSGQ_DYING) {
+			error = EIDRM;
+			goto rele;
 		}
+
+		goto again;
 	}
 
+	que->que_flags |= MSGQ_RCVWAIT;
+
 	/* if msg_copyout fails, keep the message around so it isn't lost */
-	if ((error = msg_copyout(msg, msgp, &msgsz, p)))
+	if ((error = msg_copyout(msg, msgp, &msgsz, SCARG(uap, msgflg))))
 		goto out;
 
 	msg_dequeue(que, msg, p);
@@ -365,6 +389,16 @@ sys_msgrcv(struct proc *p, void *v, regi
 
 	*retval = msgsz;
 out:
+	que->que_flags &= ~MSGQ_RCVWAIT;
+
+	if (que->que_flags & MSGQ_RCVWAITING) {
+		que->que_flags &= ~MSGQ_RCVWAITING;
+		if (que->que_flags & MSGQ_DYING)
+			wakeup(&que->que_flags);
+		else
+			wakeup_one(&que->que_flags);
+	}
+rele:
 	QRELE(que);
 
 	return (error);
@@ -565,7 +599,7 @@ msg_dequeue(struct que *que, struct msg 
  */
 
 int
-msg_copyin(struct msg *msg, const char *ubuf, size_t len, struct proc *p)
+msg_copyin(struct msg *msg, const char *ubuf, size_t len)
 {
 	struct mbuf **mm, *m;
 	size_t xfer;
@@ -574,7 +608,8 @@ msg_copyin(struct msg *msg, const char *
 	if (msg == NULL)
 		panic ("msg NULL");
 
-	if ((error = copyin(ubuf, &msg->msg_type, sizeof(long)))) {
+	/* msg->msg_type size is not included to `len' */
+	if ((error = copyin(ubuf, &msg->msg_type, sizeof(msg->msg_type)))) {
 		msg_free(msg);
 		return (error);
 	}
@@ -584,18 +619,18 @@ msg_copyin(struct msg *msg, const char *
 		return (EINVAL);
 	}
 
-	ubuf += sizeof(long);
+	ubuf += sizeof(msg->msg_type);
 
 	msg->msg_len = 0;
 	mm = &msg->msg_data;
 
 	while (msg->msg_len < len) {
 		m = m_get(M_WAIT, MT_DATA);
-		if (len >= MINCLSIZE) {
+		if ((xfer = len - msg->msg_len) >= MINCLSIZE) {
 			MCLGET(m, M_WAIT);
-			xfer = min(len, MCLBYTES);
+			xfer = min(xfer, MCLBYTES);
 		} else {
-			xfer = min(len, MLEN);
+			xfer = min(xfer, MLEN);
 		}
 		m->m_len = xfer;
 		msg->msg_len += xfer;
@@ -615,10 +650,10 @@ msg_copyin(struct msg *msg, const char *
 }
 
 int
-msg_copyout(struct msg *msg, char *ubuf, size_t *len, struct proc *p)
+msg_copyout(struct msg *msg, char *ubuf, size_t *len, int msgflg)
 {
 	struct mbuf *m;
-	size_t xfer;
+	size_t total, done, xfer;
 	int error;
 
 #ifdef DIAGNOSTIC
@@ -626,19 +661,24 @@ msg_copyout(struct msg *msg, char *ubuf,
 		panic("SysV message longer than MSGMAX");
 #endif
 
-	/* silently truncate messages too large for user buffer */
-	xfer = min(*len, msg->msg_len);
+	if ((total = min(*len, msg->msg_len)) < msg->msg_len) {
+		if ((msgflg & MSG_NOERROR) == 0)
+			return (E2BIG);
+	}
 
-	if ((error = copyout(&msg->msg_type, ubuf, sizeof(long))))
+	if ((error = copyout(&msg->msg_type, ubuf, sizeof(msg->msg_type))))
 		return (error);
 
-	ubuf += sizeof(long);
-	*len = xfer;
+	ubuf += sizeof(msg->msg_type);
+	*len = total;
 
-	for (m = msg->msg_data; m; m = m->m_next) {
-		if ((error = copyout(mtod(m, void *), ubuf, m->m_len)))
+	for (done = 0, m = msg->msg_data; m; m = m->m_next) {
+		if ((xfer = min(m->m_len, total - done)) == 0)
+			break;
+		if ((error = copyout(mtod(m, void *), ubuf, xfer)))
 			return (error);
-		ubuf += m->m_len;
+		ubuf += xfer;
+		done += xfer;
 	}
 
 	return (0);
@@ -649,7 +689,7 @@ sysctl_sysvmsg(int *name, u_int namelen,
 {
 	struct msg_sysctl_info *info;
 	struct que *que;
-	size_t infolen, infolen0;
+	size_t infolen;
 	int error;
 
 	switch (*name) {
@@ -666,10 +706,10 @@ sysctl_sysvmsg(int *name, u_int namelen,
 		 * message queues; for now, emulate this behavior
 		 * until a more thorough fix can be made.
 		 */
-		infolen0 = sizeof(msginfo) +
+		infolen = sizeof(msginfo) +
 		    msginfo.msgmni * sizeof(struct msqid_ds);
 		if (where == NULL) {
-			*sizep = infolen0;
+			*sizep = infolen;
 			return (0);
 		}
 
@@ -682,17 +722,10 @@ sysctl_sysvmsg(int *name, u_int namelen,
 		 */
 		if (*sizep == sizeof(struct msginfo))
 			return (copyout(&msginfo, where, sizeof(msginfo)));
-
-		info = malloc(infolen0, M_TEMP, M_WAIT|M_ZERO);
-
-		/* if the malloc slept, this may have changed */
-		infolen = sizeof(msginfo) +
-		    msginfo.msgmni * sizeof(struct msqid_ds);
-
-		if (*sizep < infolen) {
-			free(info, M_TEMP, infolen0);
+		if (*sizep < infolen)
 			return (ENOMEM);
-		}
+
+		info = malloc(infolen, M_TEMP, M_WAIT|M_ZERO);
 
 		memcpy(&info->msginfo, &msginfo, sizeof(struct msginfo));
 
@@ -707,7 +740,7 @@ sysctl_sysvmsg(int *name, u_int namelen,
 
 		error = copyout(info, where, infolen);
 
-		free(info, M_TEMP, infolen0);
+		free(info, M_TEMP, infolen);
 
 		return (error);
 
Index: sys/kern/sysv_sem.c
===================================================================
RCS file: /cvs/src/sys/kern/sysv_sem.c,v
diff -u -p -r1.66 sysv_sem.c
--- sys/kern/sysv_sem.c	2 Feb 2026 00:04:32 -0000	1.66
+++ sys/kern/sysv_sem.c	13 Jul 2026 18:27:04 -0000
@@ -47,11 +47,29 @@
 
 #define SEMOP_MAX (MALLOC_MAX / sizeof(struct sembuf))
 
+/*
+ * In-kernel semaphore implementation
+ */
+struct semid_ds_kern {
+	struct ipc_perm	sem_perm;	/* operation permission struct */
+	struct sem	*sem_base;	/* pointer to first semaphore in set */
+	unsigned short	sem_nsems;	/* number of sems in set */
+	time_t		sem_otime;	/* last operation time */
+	time_t		sem_ctime;	/* last change time */
+	    				/* Times measured in secs since */
+	    				/* 00:00:00 GMT, Jan. 1, 1970 */
+	struct refcnt	sem_refcnt;
+};
+
+/* rename sema for stable as it is declared as struct semid_ds in sem.h */
+extern struct semid_ds_kern **sema_kern;
+#define sema sema_kern
+
 int	semtot = 0;
 int	semutot = 0;
-struct	semid_ds **sema;	/* semaphore id list */
+struct	semid_ds_kern **sema;	/* semaphore id list */
 SLIST_HEAD(, sem_undo) semu_list; /* list of undo structures */
-struct	pool sema_pool;		/* pool for struct semid_ds */
+struct	pool sema_pool;		/* pool for struct semid_ds_kern */
 struct	pool semu_pool;		/* pool for struct sem_undo (SEMUSZ) */
 unsigned short *semseqs;	/* array of sem sequence numbers */
 
@@ -63,16 +81,33 @@ void
 seminit(void)
 {
 
-	pool_init(&sema_pool, sizeof(struct semid_ds), 0, 0, PR_WAITOK,
+	pool_init(&sema_pool, sizeof(struct semid_ds_kern), 0, 0, PR_WAITOK,
 	    "semapl", NULL);
 	pool_init(&semu_pool, SEMUSZ, 0, 0, PR_WAITOK, "semupl", NULL);
-	sema = mallocarray(seminfo.semmni, sizeof(struct semid_ds *),
+	sema = mallocarray(seminfo.semmni, sizeof(struct semid_ds_kern *),
 	    M_SEM, M_WAITOK|M_ZERO);
 	semseqs = mallocarray(seminfo.semmni, sizeof(unsigned short),
 	    M_SEM, M_WAITOK|M_ZERO);
 	SLIST_INIT(&semu_list);
 }
 
+static inline void
+sem_ref(struct semid_ds_kern *semaptr)
+{
+	refcnt_take(&semaptr->sem_refcnt);
+}
+
+void
+sem_rele(struct semid_ds_kern *semaptr)
+{
+	if (refcnt_rele(&semaptr->sem_refcnt)) {
+		semtot -= semaptr->sem_nsems;
+		free(semaptr->sem_base, M_SEM,
+		    semaptr->sem_nsems * sizeof(struct sem));
+		pool_put(&sema_pool, semaptr);
+	}
+}
+
 /*
  * Allocate a new sem_undo structure for a process
  * (returns ptr to structure or NULL if no more room)
@@ -237,7 +272,7 @@ sys___semctl(struct proc *p, void *v, re
 	int cmd = SCARG(uap, cmd);
 	union semun arg, *uarg = SCARG(uap, arg);
 	struct semid_ds sbuf;
-	struct semid_ds *semaptr;
+	struct semid_ds_kern *semaptr;
 	unsigned short *semval = NULL, nsems;
 	int i, ix, error;
 
@@ -271,11 +306,8 @@ again:
 			return (error);
 		semaptr->sem_perm.cuid = cred->cr_uid;
 		semaptr->sem_perm.uid = cred->cr_uid;
-		semtot -= semaptr->sem_nsems;
-		free(semaptr->sem_base, M_SEM,
-		    semaptr->sem_nsems * sizeof(struct sem));
-		pool_put(&sema_pool, semaptr);
 		sema[ix] = NULL;
+		sem_rele(semaptr);
 		semundo_clear(ix, -1);
 		wakeup(&sema[ix]);
 		break;
@@ -293,9 +325,14 @@ again:
 	case IPC_STAT:
 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
 			return (error);
-		memcpy(&sbuf, semaptr, sizeof sbuf);
-		sbuf.sem_base = NULL;
-		error = copyout(&sbuf, arg.buf, sizeof(struct semid_ds));
+
+		memset(&sbuf, 0, sizeof(sbuf));
+		sbuf.sem_perm = semaptr->sem_perm;
+		sbuf.sem_nsems = semaptr->sem_nsems;
+		sbuf.sem_otime = semaptr->sem_otime;
+		sbuf.sem_ctime = semaptr->sem_ctime;
+
+		error = copyout(&sbuf, arg.buf, sizeof(sbuf));
 		break;
 
 	case GETNCNT:
@@ -324,17 +361,20 @@ again:
 
 	case GETALL:
 		nsems = semaptr->sem_nsems;
+
+		sem_ref(semaptr);
 		semval = mallocarray(nsems, sizeof(arg.array[0]),
 		    M_TEMP, M_WAITOK);
-		if (semaptr != sema[ix] || 
-		    semaptr->sem_perm.seq != IPCID_TO_SEQ(semid) ||
-		    semaptr->sem_nsems != nsems) {
+		sem_rele(semaptr);
+
+		if (semaptr != sema[ix]) {
 			free(semval, M_TEMP, nsems * sizeof(arg.array[0]));
 			semval = NULL;
 			goto again;
 		}
 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
 			goto error;
+
 		for (i = 0; i < nsems; i++)
 			semval[i] = semaptr->sem_base[i].semval;
 		for (i = 0; i < nsems; i++) {
@@ -367,21 +407,26 @@ again:
 
 	case SETALL:
 		nsems = semaptr->sem_nsems;
+
+		sem_ref(semaptr);
 		semval = mallocarray(nsems, sizeof(arg.array[0]),
 		    M_TEMP, M_WAITOK);
 		for (i = 0; i < nsems; i++) {
 			error = copyin(&arg.array[i], &semval[i],
 			    sizeof(arg.array[0]));
 			if (error != 0)
-				goto error;
+				break;
 			if (semval[i] > seminfo.semvmx) {
 				error = ERANGE;
-				goto error;
+				break;
 			}
 		}
-		if (semaptr != sema[ix] ||
-		    semaptr->sem_perm.seq != IPCID_TO_SEQ(semid) ||
-		    semaptr->sem_nsems != nsems) {
+		sem_rele(semaptr);
+
+		if (error)
+			goto error;
+
+		if (semaptr != sema[ix]) {
 			free(semval, M_TEMP, nsems * sizeof(arg.array[0]));
 			semval = NULL;
 			goto again;
@@ -416,7 +461,7 @@ sys_semget(struct proc *p, void *v, regi
 	int key = SCARG(uap, key);
 	int nsems = SCARG(uap, nsems);
 	int semflg = SCARG(uap, semflg);
-	struct semid_ds *semaptr, *semaptr_new = NULL;
+	struct semid_ds_kern *semaptr, *semaptr_new = NULL;
 	struct ucred *cred = p->p_ucred;
 
 	DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
@@ -486,6 +531,7 @@ sys_semget(struct proc *p, void *v, regi
 			goto error;
 		}
 		DPRINTF(("semid %d is available\n", semid));
+		refcnt_init(&semaptr_new->sem_refcnt);
 		semaptr_new->sem_perm.key = key;
 		semaptr_new->sem_perm.cuid = cred->cr_uid;
 		semaptr_new->sem_perm.uid = cred->cr_uid;
@@ -527,8 +573,8 @@ sys_semop(struct proc *p, void *v, regis
 	struct sembuf sopbuf[NSOPS];
 	int semid = SCARG(uap, semid);
 	size_t nsops = SCARG(uap, nsops);
-	struct sembuf *sops;
-	struct semid_ds *semaptr;
+	struct sembuf *sops = NULL;
+	struct semid_ds_kern *semaptr;
 	struct sembuf *sopptr = NULL;
 	struct sem *semptr = NULL;
 	struct sem_undo *suptr = NULL;
@@ -539,38 +585,52 @@ sys_semop(struct proc *p, void *v, regis
 	DPRINTF(("call to semop(%d, %p, %lu)\n", semid, SCARG(uap, sops),
 	    (u_long)nsops));
 
+	if (nsops == 0)
+		goto skipcopy;
+
+	if (nsops > (size_t)seminfo.semopm) {
+		DPRINTF(("too many sops (max=%d, nsops=%lu)\n",
+		    seminfo.semopm, (u_long)nsops));
+		return (E2BIG);
+	}
+
+	if (nsops <= NSOPS)
+		sops = sopbuf;
+	else {
+		sops = mallocarray(nsops, sizeof(struct sembuf),
+		    M_SEM, M_WAITOK);
+	}
+
+	error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf));
+	if (error != 0) {
+		DPRINTF(("error = %d from copyin(%p, %p, %zu)\n", error,
+		    SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf)));
+		goto done2;
+	}
+
+skipcopy:
 	semid = IPCID_TO_IX(semid);	/* Convert back to zero origin */
 
-	if (semid < 0 || semid >= seminfo.semmni)
-		return (EINVAL);
+	if (semid < 0 || semid >= seminfo.semmni) {
+		error = EINVAL;
+		goto done2;
+	}
 
 	if ((semaptr = sema[semid]) == NULL ||
-	    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid)))
-		return (EINVAL);
+	    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) {
+		error = EINVAL;
+		goto done2;
+	}
 
 	if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
 		DPRINTF(("error = %d from ipaccess\n", error));
-		return (error);
+		goto done2;
 	}
 
 	if (nsops == 0) {
+		/* `sops' has not been allocated, no need to release */
 		*retval = 0;
 		return (0);
-	} else if (nsops > (size_t)seminfo.semopm) {
-		DPRINTF(("too many sops (max=%d, nsops=%lu)\n", seminfo.semopm,
-		    (u_long)nsops));
-		return (E2BIG);
-	}
-
-	if (nsops <= NSOPS)
-		sops = sopbuf;
-	else
-		sops = mallocarray(nsops, sizeof(struct sembuf), M_SEM, M_WAITOK);
-	error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf));
-	if (error != 0) {
-		DPRINTF(("error = %d from copyin(%p, %p, %zu)\n", error,
-		    SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf)));
-		goto done2;
 	}
 
 	/* 
@@ -665,22 +725,28 @@ sys_semop(struct proc *p, void *v, regis
 		else
 			semptr->semncnt++;
 
+		sem_ref(semaptr);
+
 		DPRINTF(("semop:  good night!\n"));
 		error = tsleep_nsec(&sema[semid], PLOCK | PCATCH,
 		    "semwait", INFSLP);
 		DPRINTF(("semop:  good morning (error=%d)!\n", error));
 
-		suptr = NULL;	/* sem_undo may have been reallocated */
+		sem_rele(semaptr);
 
 		/*
-		 * Make sure that the semaphore still exists
+		 * Make sure that the semaphore still exists. Even if
+		 * `semaptr' was released, it's address was not used to
+		 * alloc the new one. The semaphore is alive if address
+		 * was not changed.
 		 */
-		if (sema[semid] == NULL ||
-		    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) {
+		if (semaptr != sema[semid]) {
 			error = EIDRM;
 			goto done2;
 		}
 
+		suptr = NULL;	/* sem_undo may have been reallocated */
+
 		/*
 		 * The semaphore is still alive.  Readjust the count of
 		 * waiting processes.
@@ -821,7 +887,7 @@ semexit(struct process *pr)
 			int semid = suptr->un_ent[ix].un_id;
 			int semnum = suptr->un_ent[ix].un_num;
 			int adjval = suptr->un_ent[ix].un_adjval;
-			struct semid_ds *semaptr;
+			struct semid_ds_kern *semaptr;
 
 			if ((semaptr = sema[semid]) == NULL)
 				panic("semexit - semid not allocated");
@@ -858,17 +924,17 @@ semexit(struct process *pr)
 void
 sema_reallocate(int val)
 {
-	struct semid_ds **sema_new;
+	struct semid_ds_kern **sema_new;
 	unsigned short *newseqs;
-	sema_new = mallocarray(val, sizeof(struct semid_ds *),
+	sema_new = mallocarray(val, sizeof(struct semid_ds_kern *),
 	    M_SEM, M_WAITOK|M_ZERO);
 	memcpy(sema_new, sema,
-	    seminfo.semmni * sizeof(struct semid_ds *));
+	    seminfo.semmni * sizeof(struct semid_ds_kern *));
 	newseqs = mallocarray(val, sizeof(unsigned short), M_SEM,
 	    M_WAITOK|M_ZERO);
 	memcpy(newseqs, semseqs,
 	    seminfo.semmni * sizeof(unsigned short));
-	free(sema, M_SEM, seminfo.semmni * sizeof(struct semid_ds *));
+	free(sema, M_SEM, seminfo.semmni * sizeof(struct semid_ds_kern *));
 	free(semseqs, M_SEM, seminfo.semmni * sizeof(unsigned short));
 	sema = sema_new;
 	semseqs = newseqs;
Index: sys/kern/sysv_shm.c
===================================================================
RCS file: /cvs/src/sys/kern/sysv_shm.c,v
diff -u -p -r1.82 sysv_shm.c
--- sys/kern/sysv_shm.c	16 Apr 2026 05:07:07 -0000	1.82
+++ sys/kern/sysv_shm.c	13 Jul 2026 18:27:04 -0000
@@ -86,6 +86,10 @@ struct shmid_ds *shm_find_segment_by_shm
  * per proc 'struct shmmap_head' with an array of 'struct shmmap_state'
  */
 
+/* Limit to prevent chunk size overflow in sys_shmat() */
+#define SHMSEG_MAX \
+    ((0xffffffff - sizeof(int)) / sizeof(struct shmmap_state) / 8)
+
 #define	SHMSEG_REMOVED  	0x0200		/* can't overlap ACCESSPERMS */
 
 int shm_last_free, shm_nused, shm_committed;
@@ -224,17 +228,30 @@ sys_shmat(struct proc *p, void *v, regis
 
 	shmmap_h = (struct shmmap_head *)p->p_vmspace->vm_shm;
 	if (shmmap_h == NULL) {
+		struct shmmap_head *shmmap_h_probe;
+		int shmseg_local = atomic_load_int(&shminfo.shmseg);
+
 		size = sizeof(int) +
-		    shminfo.shmseg * sizeof(struct shmmap_state);
+		    shmseg_local * sizeof(struct shmmap_state);
 		shmmap_h = malloc(size, M_SHM, M_WAITOK | M_CANFAIL);
 		if (shmmap_h == NULL)
 			return (ENOMEM);
-		shmmap_h->shmseg = shminfo.shmseg;
+
+		shmmap_h_probe =
+		    (struct shmmap_head *)READ_ONCE(p->p_vmspace->vm_shm);
+		if (shmmap_h_probe != NULL) {
+			free(shmmap_h, M_SHM, size);
+			shmmap_h = shmmap_h_probe;
+			goto allocated;
+		}
+
+		shmmap_h->shmseg = shmseg_local;
 		for (i = 0, shmmap_s = shmmap_h->state; i < shmmap_h->shmseg;
 		    i++, shmmap_s++)
 			shmmap_s->shmid = -1;
 		p->p_vmspace->vm_shm = (caddr_t)shmmap_h;
 	}
+allocated:
 	shmseg = shm_find_segment_by_shmid(SCARG(uap, shmid));
 	if (shmseg == NULL)
 		return (EINVAL);
@@ -416,10 +433,10 @@ shmget_allocate_segment(struct proc *p,
 	 * the key we want in the meantime.  Yes, this is ugly.
 	 */
 	key = SCARG(uap, key);
-	shmseg = pool_get(&shm_pool, key == IPC_PRIVATE ? PR_WAITOK :
-	    PR_NOWAIT);
+	shmseg = pool_get(&shm_pool,
+	    (key == IPC_PRIVATE ? PR_WAITOK : PR_NOWAIT) | PR_ZERO);
 	if (shmseg == NULL) {
-		shmseg = pool_get(&shm_pool, PR_WAITOK);
+		shmseg = pool_get(&shm_pool, PR_WAITOK | PR_ZERO);
 		if (shm_find_segment_by_key(key) != -1) {
 			pool_put(&shm_pool, shmseg);
 			shm_nused--;
@@ -611,7 +628,7 @@ sysctl_sysvshm(int *name, u_int namelen,
 		return (0);
 	case KERN_SHMINFO_SHMSEG:
 		return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
-		    &shminfo.shmseg, 1, INT_MAX));
+		    &shminfo.shmseg, 1, SHMSEG_MAX));
 	case KERN_SHMINFO_SHMALL:
 		/* can't decrease shmall */
 		return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
