]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/sysv_sem.c
xnu-2782.40.9.tar.gz
[apple/xnu.git] / bsd / kern / sysv_sem.c
index 6c90d6485563a85cb622a96e4166e96f6d949ec3..418a4c6e00922fb0ccb43cf7c016d68ebb086949 100644 (file)
@@ -62,7 +62,7 @@
 #include <security/mac_framework.h>
 #endif
 
-#include <bsm/audit_kernel.h>
+#include <security/audit/audit.h>
 
 #if SYSV_SEM
 
@@ -173,10 +173,20 @@ sysv_semtime(void)
  * NOTE: Source and target may *NOT* overlap! (target is smaller)
  */
 static void
-semid_ds_64to32(struct user_semid_ds *in, struct semid_ds *out)
+semid_ds_kernelto32(struct user_semid_ds *in, struct user32_semid_ds *out)
 {
        out->sem_perm = in->sem_perm;
-       out->sem_base = (__int32_t)in->sem_base;
+       out->sem_base = CAST_DOWN_EXPLICIT(__int32_t,in->sem_base);
+       out->sem_nsems = in->sem_nsems;
+       out->sem_otime = in->sem_otime;         /* XXX loses precision */
+       out->sem_ctime = in->sem_ctime;         /* XXX loses precision */
+}
+
+static void
+semid_ds_kernelto64(struct user_semid_ds *in, struct user64_semid_ds *out)
+{
+       out->sem_perm = in->sem_perm;
+       out->sem_base = CAST_DOWN_EXPLICIT(__int32_t,in->sem_base);
        out->sem_nsems = in->sem_nsems;
        out->sem_otime = in->sem_otime;         /* XXX loses precision */
        out->sem_ctime = in->sem_ctime;         /* XXX loses precision */
@@ -193,25 +203,50 @@ semid_ds_64to32(struct user_semid_ds *in, struct semid_ds *out)
  * XXX is the same.
  */
 static void
-semid_ds_32to64(struct semid_ds *in, struct user_semid_ds *out)
+semid_ds_32tokernel(struct user32_semid_ds *in, struct user_semid_ds *out)
 {
        out->sem_ctime = in->sem_ctime;
        out->sem_otime = in->sem_otime;
        out->sem_nsems = in->sem_nsems;
-       out->sem_base = (void *)in->sem_base;
+       out->sem_base = (void *)(uintptr_t)in->sem_base;
+       out->sem_perm = in->sem_perm;
+}
+
+static void
+semid_ds_64tokernel(struct user64_semid_ds *in, struct user_semid_ds *out)
+{
+       out->sem_ctime = in->sem_ctime;
+       out->sem_otime = in->sem_otime;
+       out->sem_nsems = in->sem_nsems;
+       out->sem_base = (void *)(uintptr_t)in->sem_base;
        out->sem_perm = in->sem_perm;
 }
 
 
 /*
- * Entry point for all SEM calls
+ * semsys
+ *
+ * Entry point for all SEM calls: semctl, semget, semop
+ *
+ * Parameters: p       Process requesting the call
+ *             uap     User argument descriptor (see below)
+ *             retval  Return value of the selected sem call
+ *
+ * Indirect parameters:        uap->which      sem call to invoke (index in array of sem calls)
+ *                     uap->a2         User argument descriptor
+ *                 
+ * Returns:    0       Success
+ *             !0      Not success
+ *
+ * Implicit returns: retval    Return value of the selected sem call
+ *
+ * DEPRECATED:  This interface should not be used to call the other SEM
+ *             functions (semctl, semget, semop). The correct usage is
+ *             to call the other SEM functions directly.
  *
- * In Darwin this is no longer the entry point.  It will be removed after
- *  the code has been tested better.
  */
-/* XXX actually varargs. */
 int
-semsys(struct proc *p, struct semsys_args *uap, register_t *retval)
+semsys(struct proc *p, struct semsys_args *uap, int32_t *retval)
 {
 
        /* The individual calls handling the locking now */
@@ -409,7 +444,8 @@ grow_sem_pool(int new_pool_size)
        /* Update our id structures to point to the new semaphores */
        for(i = 0; i < seminfo.semmni; i++) {
                if (sema[i].u.sem_perm.mode & SEM_ALLOC)  /* ID in use */
-                       sema[i].u.sem_base += (new_sem_pool - sem_pool);
+                       sema[i].u.sem_base = new_sem_pool + 
+                               (sema[i].u.sem_base - sem_pool);
        }
 
        sem_free = sem_pool;
@@ -639,7 +675,7 @@ semundo_clear(int semid, int semnum)
  * because the alignment is the same in user and kernel space.
  */
 int
-semctl(struct proc *p, struct semctl_args *uap, register_t *retval)
+semctl(struct proc *p, struct semctl_args *uap, int32_t *retval)
 {
        int semid = uap->semid;
        int semnum = uap->semnum;
@@ -649,7 +685,6 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval)
        int i, rval, eval;
        struct user_semid_ds sbuf;
        struct semid_kernel *semakptr;
-       struct user_semid_ds uds;
        
 
        AUDIT_ARG(svipc_cmd, cmd);
@@ -714,11 +749,13 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval)
                                goto semctlout;
 
                if (IS_64BIT_PROCESS(p)) {
-                       eval = copyin(user_arg.buf, &sbuf, sizeof(struct user_semid_ds));
+                       struct user64_semid_ds ds64;
+                       eval = copyin(user_arg.buf, &ds64, sizeof(ds64));
+                       semid_ds_64tokernel(&ds64, &sbuf);
                } else {
-                       eval = copyin(user_arg.buf, &sbuf, sizeof(struct semid_ds));
-                       /* convert in place; ugly, but safe */
-                       semid_ds_32to64((struct semid_ds *)&sbuf, &sbuf);
+                       struct user32_semid_ds ds32;
+                       eval = copyin(user_arg.buf, &ds32, sizeof(ds32));
+                       semid_ds_32tokernel(&ds32, &sbuf);
                }
                
                if (eval != 0) {
@@ -735,13 +772,17 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval)
        case IPC_STAT:
                if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_R)))
                                goto semctlout;
-               bcopy((caddr_t)&semakptr->u, &uds, sizeof(struct user_semid_ds));
+
                if (IS_64BIT_PROCESS(p)) {
-                       eval = copyout(&uds, user_arg.buf, sizeof(struct user_semid_ds));
+                       struct user64_semid_ds semid_ds64;
+                       bzero(&semid_ds64, sizeof(semid_ds64));
+                       semid_ds_kernelto64(&semakptr->u, &semid_ds64);
+                       eval = copyout(&semid_ds64, user_arg.buf, sizeof(semid_ds64));
                } else {
-                       struct semid_ds semid_ds32;
-                       semid_ds_64to32(&uds, &semid_ds32);
-                       eval = copyout(&semid_ds32, user_arg.buf, sizeof(struct semid_ds));
+                       struct user32_semid_ds semid_ds32;
+                       bzero(&semid_ds32, sizeof(semid_ds32));
+                       semid_ds_kernelto32(&semakptr->u, &semid_ds32);
+                       eval = copyout(&semid_ds32, user_arg.buf, sizeof(semid_ds32));
                }
                break;
 
@@ -815,12 +856,27 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval)
                        eval = EINVAL;
                        goto semctlout;
                }
+               
                /*
                 * Cast down a pointer instead of using 'val' member directly
                 * to avoid introducing endieness and a pad field into the
                 * header file.  Ugly, but it works.
                 */
-               semakptr->u.sem_base[semnum].semval = CAST_DOWN(int,user_arg.buf);
+               u_int newsemval = CAST_DOWN_EXPLICIT(u_int, user_arg.buf);
+               
+               /*
+                * The check is being performed as unsigned values to match 
+                * eventual destination
+                */ 
+               if (newsemval > (u_int)seminfo.semvmx)
+               {
+#ifdef SEM_DEBUG
+                       printf("Out of range sem value for set\n");
+#endif
+                       eval = ERANGE;
+                       goto semctlout;
+               }
+               semakptr->u.sem_base[semnum].semval = newsemval;
                semakptr->u.sem_base[semnum].sempid = p->p_pid;
                /* XXX scottl Should there be a MAC call here? */
                semundo_clear(semid, semnum);
@@ -858,7 +914,7 @@ semctlout:
 }
 
 int
-semget(__unused struct proc *p, struct semget_args *uap, register_t *retval)
+semget(__unused struct proc *p, struct semget_args *uap, int32_t *retval)
 {
        int semid, eval;
        int key = uap->key;
@@ -961,8 +1017,8 @@ semget(__unused struct proc *p, struct semget_args *uap, register_t *retval)
                sema[semid].u.sem_perm._key = key;
                sema[semid].u.sem_perm.cuid = kauth_cred_getuid(cred);
                sema[semid].u.sem_perm.uid = kauth_cred_getuid(cred);
-               sema[semid].u.sem_perm.cgid = cred->cr_gid;
-               sema[semid].u.sem_perm.gid = cred->cr_gid;
+               sema[semid].u.sem_perm.cgid = kauth_cred_getgid(cred);
+               sema[semid].u.sem_perm.gid = kauth_cred_getgid(cred);
                sema[semid].u.sem_perm.mode = (semflg & 0777) | SEM_ALLOC;
                sema[semid].u.sem_perm._seq =
                    (sema[semid].u.sem_perm._seq + 1) & 0x7fff;
@@ -1002,11 +1058,11 @@ semgetout:
 }
 
 int
-semop(struct proc *p, struct semop_args *uap, register_t *retval)
+semop(struct proc *p, struct semop_args *uap, int32_t *retval)
 {
        int semid = uap->semid;
        int nsops = uap->nsops;
-       struct sembuf sops[MAX_SOPS];
+       struct sembuf sops[seminfo.semopm];
        register struct semid_kernel *semakptr;
        register struct sembuf *sopptr = NULL;  /* protected by 'semptr' */
        register struct sem *semptr = NULL;     /* protected by 'if' */
@@ -1046,13 +1102,23 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval)
                goto semopout;
        }
 
-       if (nsops < 0 || nsops > MAX_SOPS) {
+       if (nsops < 0 || nsops > seminfo.semopm) {
 #ifdef SEM_DEBUG
-               printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops);
+               printf("too many sops (max=%d, nsops=%d)\n",
+                   seminfo.semopm, nsops);
 #endif
                eval = E2BIG;
                goto semopout;
        }
+       
+       /*  OK for LP64, since sizeof(struct sembuf) is currently invariant */
+       if ((eval = copyin(uap->sops, &sops, nsops * sizeof(struct sembuf))) != 0) {
+#ifdef SEM_DEBUG
+               printf("eval = %d from copyin(%08x, %08x, %ld)\n", eval,
+                   uap->sops, &sops, nsops * sizeof(struct sembuf));
+#endif
+               goto semopout;
+       }
 
 #if CONFIG_MACF
        /*
@@ -1072,15 +1138,6 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval)
                goto semopout;
 #endif
 
-       /*  OK for LP64, since sizeof(struct sembuf) is currently invariant */
-       if ((eval = copyin(uap->sops, &sops, nsops * sizeof(struct sembuf))) != 0) {
-#ifdef SEM_DEBUG
-               printf("eval = %d from copyin(%08x, %08x, %ld)\n", eval,
-                   uap->sops, &sops, nsops * sizeof(struct sembuf));
-#endif
-               goto semopout;
-       }
-
        /*
         * Loop trying to satisfy the vector of requests.
         * If we reach a point where we must wait, any requests already
@@ -1416,10 +1473,7 @@ semexit(struct proc *p)
                /* Maybe we should build a list of semakptr's to wake
                 * up, finish all access to data structures, release the
                 * subsystem lock, and wake all the processes.  Something
-                * to think about.  It wouldn't buy us anything unless
-                * wakeup had the potential to block, or the syscall
-                * funnel state was changed to allow multiple threads
-                * in the BSD code at once.
+                * to think about.
                 */
 #ifdef SEM_WAKEUP
                        sem_wakeup((caddr_t)semakptr);
@@ -1501,19 +1555,19 @@ out:
 
 /* SYSCTL_NODE(_kern, KERN_SYSV, sysv, CTLFLAG_RW, 0, "SYSV"); */
 extern struct sysctl_oid_list sysctl__kern_sysv_children;
-SYSCTL_PROC(_kern_sysv, OID_AUTO, semmni, CTLTYPE_INT | CTLFLAG_RW,
+SYSCTL_PROC(_kern_sysv, OID_AUTO, semmni, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
     &limitseminfo.semmni, 0, &sysctl_seminfo ,"I","semmni");
 
-SYSCTL_PROC(_kern_sysv, OID_AUTO, semmns, CTLTYPE_INT | CTLFLAG_RW,
+SYSCTL_PROC(_kern_sysv, OID_AUTO, semmns, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
     &limitseminfo.semmns, 0, &sysctl_seminfo ,"I","semmns");
 
-SYSCTL_PROC(_kern_sysv, OID_AUTO, semmnu, CTLTYPE_INT | CTLFLAG_RW,
+SYSCTL_PROC(_kern_sysv, OID_AUTO, semmnu, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
     &limitseminfo.semmnu, 0, &sysctl_seminfo ,"I","semmnu");
 
-SYSCTL_PROC(_kern_sysv, OID_AUTO, semmsl, CTLTYPE_INT | CTLFLAG_RW,
+SYSCTL_PROC(_kern_sysv, OID_AUTO, semmsl, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
     &limitseminfo.semmsl, 0, &sysctl_seminfo ,"I","semmsl");
     
-SYSCTL_PROC(_kern_sysv, OID_AUTO, semume, CTLTYPE_INT | CTLFLAG_RW,
+SYSCTL_PROC(_kern_sysv, OID_AUTO, semume, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
     &limitseminfo.semume, 0, &sysctl_seminfo ,"I","semume");
 
 
@@ -1524,18 +1578,22 @@ IPCS_sem_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1,
        int error;
        int cursor;
        union {
-               struct IPCS_command u32;
+               struct user32_IPCS_command u32;
                struct user_IPCS_command u64;
        } ipcs;
-       struct semid_ds semid_ds32;     /* post conversion, 32 bit version */
+       struct user32_semid_ds semid_ds32;      /* post conversion, 32 bit version */
+       struct user64_semid_ds semid_ds64;      /* post conversion, 64 bit version */
        void *semid_dsp;
-       size_t ipcs_sz = sizeof(struct user_IPCS_command);
-       size_t semid_ds_sz = sizeof(struct user_semid_ds);
+       size_t ipcs_sz;
+       size_t semid_ds_sz;
        struct proc *p = current_proc();
 
-       if (!IS_64BIT_PROCESS(p)) {
-               ipcs_sz = sizeof(struct IPCS_command);
-               semid_ds_sz = sizeof(struct semid_ds);
+       if (IS_64BIT_PROCESS(p)) {
+               ipcs_sz = sizeof(struct user_IPCS_command);
+               semid_ds_sz = sizeof(struct user64_semid_ds);
+       } else {
+               ipcs_sz = sizeof(struct user32_IPCS_command);
+               semid_ds_sz = sizeof(struct user32_semid_ds);
        }
 
        /* Copy in the command structure */
@@ -1592,13 +1650,23 @@ IPCS_sem_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1,
                 * descriptor to a 32 bit user one.
                 */
                if (!IS_64BIT_PROCESS(p)) {
-                       semid_ds_64to32(semid_dsp, &semid_ds32);
+                       bzero(&semid_ds32, sizeof(semid_ds32));
+                       semid_ds_kernelto32(semid_dsp, &semid_ds32);
                        semid_dsp = &semid_ds32;
+               } else {
+                       bzero(&semid_ds64, sizeof(semid_ds64));
+                       semid_ds_kernelto64(semid_dsp, &semid_ds64);
+                       semid_dsp = &semid_ds64;
                }
+
                error = copyout(semid_dsp, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen);
                if (!error) {
                        /* update cursor */
                        ipcs.u64.ipcs_cursor = cursor + 1;
+
+                       if (!IS_64BIT_PROCESS(p))       /* convert in place */
+                               ipcs.u32.ipcs_data = CAST_DOWN_EXPLICIT(user32_addr_t,ipcs.u64.ipcs_data);
+
                        error = SYSCTL_OUT(req, &ipcs, ipcs_sz);
                }
                break;
@@ -1612,7 +1680,7 @@ IPCS_sem_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1,
 }
 
 SYSCTL_DECL(_kern_sysv_ipcs);
-SYSCTL_PROC(_kern_sysv_ipcs, OID_AUTO, sem, CTLFLAG_RW|CTLFLAG_ANYBODY,
+SYSCTL_PROC(_kern_sysv_ipcs, OID_AUTO, sem, CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED,
        0, 0, IPCS_sem_sysctl,
        "S,IPCS_sem_command",
        "ipcs sem command interface");