On Fri, Sep 16, 2022 at 11:38:49PM +0200, Moritz Buhl wrote:
> Syzkaller found a panic with sysv semaphores:
> https://syzkaller.appspot.com/bug?id=f7e8e2822779918d7a23d9ff9d7c0a3779c00a46

The last diff would introduced two memory leaks, seminfo.semopm is
the only seminfo value that can shrink via sysctl, so we have to
recheck it.  semtot also can change during a sleep.
These problems are now addressed below.

I am still uncertain if anything relevant could change during a
sleep in copyout for GETALL.

The diff is growing in size, would it make sense to look at semctl
separately from semop and semget?

Except for the fuzzer, I am not aware of any code that makes extensive
use of the related syscalls.  But they are cute.

mbuhl

Index: kern/sysv_sem.c
===================================================================
RCS file: /cvs/src/sys/kern/sysv_sem.c,v
retrieving revision 1.62
diff -u -p -r1.62 sysv_sem.c
--- kern/sysv_sem.c     16 Sep 2022 15:57:23 -0000      1.62
+++ kern/sysv_sem.c     17 Sep 2022 15:12:32 -0000
@@ -236,7 +236,7 @@ sys___semctl(struct proc *p, void *v, re
        union semun arg, *uarg = SCARG(uap, arg);
        struct semid_ds sbuf;
        struct semid_ds *semaptr;
-       unsigned short *semval = NULL;
+       unsigned short *semval = NULL, nsems;
        int i, ix, error;
 
        switch (cmd) {
@@ -248,6 +248,9 @@ sys___semctl(struct proc *p, void *v, re
                if ((error = copyin(uarg, &arg, sizeof(union semun))))
                        return (error);
        }
+       if (cmd == IPC_SET)
+               if ((error = copyin(arg.buf, &sbuf, sizeof(sbuf))))
+                       return (error);
 
        DPRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, uarg));
 
@@ -255,6 +258,7 @@ sys___semctl(struct proc *p, void *v, re
        if (ix < 0 || ix >= seminfo.semmni)
                return (EINVAL);
 
+again:
        if ((semaptr = sema[ix]) == NULL ||
            semaptr->sem_perm.seq != IPCID_TO_SEQ(semid))
                return (EINVAL);
@@ -277,8 +281,6 @@ sys___semctl(struct proc *p, void *v, re
        case IPC_SET:
                if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
                        return (error);
-               if ((error = copyin(arg.buf, &sbuf, sizeof(sbuf))) != 0)
-                       return (error);
                semaptr->sem_perm.uid = sbuf.sem_perm.uid;
                semaptr->sem_perm.gid = sbuf.sem_perm.gid;
                semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
@@ -319,11 +321,22 @@ sys___semctl(struct proc *p, void *v, re
                break;
 
        case GETALL:
+               nsems = semaptr->sem_nsems;
+               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) {
+                       free(semval, M_TEMP, nsems * sizeof(arg.array[0]));
+                       goto again;
+               }
                if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
-                       return (error);
-               for (i = 0; i < semaptr->sem_nsems; i++) {
-                       error = copyout(&semaptr->sem_base[i].semval,
-                           &arg.array[i], sizeof(arg.array[0]));
+                       goto error;
+               for (i = 0; i < nsems; i++)
+                       semval[i] = semaptr->sem_base[i].semval;
+               for (i = 0; i < nsems; i++) {
+                       error = copyout(&semval[i], &arg.array[i],
+                           sizeof(arg.array[0]));
                        if (error != 0)
                                break;
                }
@@ -350,11 +363,18 @@ sys___semctl(struct proc *p, void *v, re
                break;
 
        case SETALL:
-               if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
-                       return (error);
+               nsems = semaptr->sem_nsems;
                semval = mallocarray(semaptr->sem_nsems, sizeof(arg.array[0]),
                    M_TEMP, M_WAITOK);
-               for (i = 0; i < semaptr->sem_nsems; i++) {
+               if (semaptr != sema[ix] ||
+                   semaptr->sem_perm.seq != IPCID_TO_SEQ(semid) ||
+                   semaptr->sem_nsems != nsems) {
+                       free(semval, M_TEMP, nsems * sizeof(arg.array[0]));
+                       goto again;
+               }
+               if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
+                       goto error;
+               for (i = 0; i < nsems; i++) {
                        error = copyin(&arg.array[i], &semval[i],
                            sizeof(arg.array[0]));
                        if (error != 0)
@@ -364,7 +384,15 @@ sys___semctl(struct proc *p, void *v, re
                                goto error;
                        }
                }
-               for (i = 0; i < semaptr->sem_nsems; i++)
+               if (semaptr != sema[ix] ||
+                   semaptr->sem_perm.seq != IPCID_TO_SEQ(semid) ||
+                   semaptr->sem_nsems != nsems) {
+                       free(semval, M_TEMP, nsems * sizeof(arg.array[0]));
+                       goto again;
+               }
+               if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
+                       goto error;
+               for (i = 0; i < nsems; i++)
                        semaptr->sem_base[i].semval = semval[i];
                semundo_clear(ix, -1);
                wakeup(&sema[ix]);
@@ -375,9 +403,7 @@ sys___semctl(struct proc *p, void *v, re
        }
 
 error:
-       if (semval)
-               free(semval, M_TEMP,
-                   semaptr->sem_nsems * sizeof(arg.array[0]));
+       free(semval, M_TEMP, nsems * sizeof(arg.array[0]));
 
        return (error);
 }
@@ -418,6 +444,10 @@ sys_semget(struct proc *p, void *v, regi
                semaptr_new = pool_get(&sema_pool, PR_WAITOK | PR_ZERO);
                semaptr_new->sem_base = mallocarray(nsems, sizeof(struct sem),
                    M_SEM, M_WAITOK|M_ZERO);
+               if (nsems > seminfo.semmns - semtot) {
+                       error = ENOSPC;
+                       goto error;
+               }
        }
 
        if (key != IPC_PRIVATE) {
@@ -518,15 +548,6 @@ sys_semop(struct proc *p, void *v, regis
        if (semid < 0 || semid >= seminfo.semmni)
                return (EINVAL);
 
-       if ((semaptr = sema[semid]) == NULL ||
-           semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid)))
-               return (EINVAL);
-
-       if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
-               DPRINTF(("error = %d from ipaccess\n", error));
-               return (error);
-       }
-
        if (nsops == 0) {
                *retval = 0;
                return (0);
@@ -544,6 +565,22 @@ sys_semop(struct proc *p, void *v, regis
        if (error != 0) {
                DPRINTF(("error = %d from copyin(%p, %p, %u)\n", error,
                    SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf)));
+               goto done2;
+       }
+
+       if (nsops > (size_t)seminfo.semopm) {
+               error = E2BIG;
+               goto done2;
+       }
+
+       if ((semaptr = sema[semid]) == NULL ||
+           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));
                goto done2;
        }
 

Reply via email to