X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/21362eb3e66fd2c787aee132bce100a44d71a99c..cf7d32b81c573a0536dc4da4157f9c26f8d0bed3:/bsd/kern/sysv_sem.c diff --git a/bsd/kern/sysv_sem.c b/bsd/kern/sysv_sem.c index 5691a71a9..6c90d6485 100644 --- a/bsd/kern/sysv_sem.c +++ b/bsd/kern/sysv_sem.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2007 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -35,6 +35,13 @@ /* * John Bellardo modified the implementation for Darwin. 12/2000 */ +/* + * NOTICE: This file was modified by McAfee Research in 2004 to introduce + * support for mandatory and extensible security protections. This notice + * is included in support of clause 2.2 (b) of the Apple Public License, + * Version 2.0. + * Copyright (c) 2005-2006 SPARTA, Inc. + */ #include #include @@ -51,13 +58,26 @@ #include #include #include +#if CONFIG_MACF +#include +#endif #include +#if SYSV_SEM + /* Uncomment this line to see the debugging output */ /* #define SEM_DEBUG */ +/* Uncomment this line to see MAC debugging output. */ +/* #define MAC_DEBUG */ +#if CONFIG_MACF_DEBUG +#define MPRINTF(a) printf(a) +#else +#define MPRINTF(a) +#endif + #define M_SYSVSEM M_TEMP @@ -95,8 +115,8 @@ struct seminfo seminfo = { }; -static struct sem_undo *semu_alloc(struct proc *p); -static int semundo_adjust(struct proc *p, struct sem_undo **supptr, +static int semu_alloc(struct proc *p); +static int semundo_adjust(struct proc *p, int *supidx, int semid, int semnum, int adjval); static void semundo_clear(int semid, int semnum); @@ -107,9 +127,9 @@ static sy_call_t *semcalls[] = { }; static int semtot = 0; /* # of used semaphores */ -struct user_semid_ds *sema = NULL; /* semaphore id pool */ +struct semid_kernel *sema = NULL; /* semaphore id pool */ struct sem *sem_pool = NULL; /* semaphore pool */ -static struct sem_undo *semu_list = NULL; /* active undo structures */ +static int semu_list_idx = -1; /* active undo structures */ struct sem_undo *semu = NULL; /* semaphore undo pool */ @@ -128,12 +148,10 @@ sysv_sem_lock_init( void ) { sysv_sem_subsys_lck_grp_attr = lck_grp_attr_alloc_init(); - lck_grp_attr_setstat(sysv_sem_subsys_lck_grp_attr); - sysv_sem_subsys_lck_grp = lck_grp_alloc_init("sysv_shm_subsys_lock", sysv_sem_subsys_lck_grp_attr); + sysv_sem_subsys_lck_grp = lck_grp_alloc_init("sysv_sem_subsys_lock", sysv_sem_subsys_lck_grp_attr); sysv_sem_subsys_lck_attr = lck_attr_alloc_init(); - lck_attr_setdebug(sysv_sem_subsys_lck_attr); lck_mtx_init(&sysv_sem_subsys_mutex, sysv_sem_subsys_lck_grp, sysv_sem_subsys_lck_attr); } @@ -273,7 +291,7 @@ grow_semu_array(int newSize) static int grow_sema_array(int newSize) { - register struct user_semid_ds *newSema; + register struct semid_kernel *newSema; register int i; if (newSize <= seminfo.semmni) @@ -292,8 +310,8 @@ grow_sema_array(int newSize) #ifdef SEM_DEBUG printf("growing sema[] from %d to %d\n", seminfo.semmni, newSize); #endif - MALLOC(newSema, struct user_semid_ds *, - sizeof (struct user_semid_ds) * newSize, + MALLOC(newSema, struct semid_kernel *, + sizeof (struct semid_kernel) * newSize, M_SYSVSEM, M_WAITOK | M_ZERO); if (NULL == newSema) { @@ -313,11 +331,19 @@ grow_sema_array(int newSize) * this with the existing code, so we wake up the * process and let it do a lot of work to determine the * semaphore set is really not available yet, and then - * sleep on the correct, reallocated user_semid_ds pointer. + * sleep on the correct, reallocated semid_kernel pointer. */ - if (sema[i].sem_perm.mode & SEM_ALLOC) + if (sema[i].u.sem_perm.mode & SEM_ALLOC) wakeup((caddr_t)&sema[i]); } + +#if CONFIG_MACF + for (i = seminfo.semmni; i < newSize; i++) + { + mac_sysvsem_label_init(&newSema[i]); + } +#endif + /* * The new elements (from newSema[i] to newSema[newSize-1]) have their * "sem_base" and "sem_perm.mode" set to 0 (i.e. NULL) by the M_ZERO @@ -382,8 +408,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].sem_perm.mode & SEM_ALLOC) /* ID in use */ - sema[i].sem_base += (new_sem_pool - sem_pool); + if (sema[i].u.sem_perm.mode & SEM_ALLOC) /* ID in use */ + sema[i].u.sem_base += (new_sem_pool - sem_pool); } sem_free = sem_pool; @@ -407,12 +433,12 @@ grow_sem_pool(int new_pool_size) * Assumes we already hold the subsystem lock. */ -static struct sem_undo * +static int semu_alloc(struct proc *p) { register int i; register struct sem_undo *suptr; - register struct sem_undo **supptr; + int *supidx; int attempt; /* @@ -430,12 +456,12 @@ semu_alloc(struct proc *p) for (i = 0; i < seminfo.semmnu; i++) { suptr = SEMU(i); if (suptr->un_proc == NULL) { - suptr->un_next = semu_list; - semu_list = suptr; + suptr->un_next_idx = semu_list_idx; + semu_list_idx = i; suptr->un_cnt = 0; suptr->un_ent = NULL; suptr->un_proc = p; - return(suptr); + return i; } } @@ -448,14 +474,15 @@ semu_alloc(struct proc *p) /* All the structures are in use - try to free some */ int did_something = 0; - supptr = &semu_list; - while ((suptr = *supptr) != NULL) { + supidx = &semu_list_idx; + while (*supidx != -1) { + suptr = SEMU(*supidx); if (suptr->un_cnt == 0) { suptr->un_proc = NULL; - *supptr = suptr->un_next; + *supidx = suptr->un_next_idx; did_something = 1; } else - supptr = &(suptr->un_next); + supidx = &(suptr->un_next_idx); } /* If we didn't free anything. Try expanding @@ -465,7 +492,7 @@ semu_alloc(struct proc *p) */ if (!did_something) if (!grow_semu_array(seminfo.semmnu + 1)) - return(NULL); + return -1; } else { /* * The second pass failed even though we freed @@ -475,7 +502,7 @@ semu_alloc(struct proc *p) panic("semu_alloc - second attempt failed"); } } - return (NULL); + return -1; } /* @@ -484,10 +511,11 @@ semu_alloc(struct proc *p) * Assumes we already hold the subsystem lock. */ static int -semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, +semundo_adjust(struct proc *p, int *supidx, int semid, int semnum, int adjval) { register struct sem_undo *suptr; + int suidx; register struct undo *sueptr, **suepptr, *new_sueptr; int i; @@ -495,22 +523,23 @@ semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, * Look for and remember the sem_undo if the caller doesn't provide it */ - suptr = *supptr; - if (suptr == NULL) { - for (suptr = semu_list; suptr != NULL; - suptr = suptr->un_next) { + suidx = *supidx; + if (suidx == -1) { + for (suidx = semu_list_idx; suidx != -1; + suidx = suptr->un_next_idx) { + suptr = SEMU(suidx); if (suptr->un_proc == p) { - *supptr = suptr; + *supidx = suidx; break; } } - if (suptr == NULL) { + if (suidx == -1) { if (adjval == 0) return(0); - suptr = semu_alloc(p); - if (suptr == NULL) + suidx = semu_alloc(p); + if (suidx == -1) return(ENOSPC); - *supptr = suptr; + *supidx = suidx; } } @@ -518,6 +547,7 @@ semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, * Look for the requested entry and adjust it (delete if adjval becomes * 0). */ + suptr = SEMU(suidx); new_sueptr = NULL; for (i = 0, suepptr = &suptr->un_ent, sueptr = suptr->un_ent; i < suptr->un_cnt; @@ -572,12 +602,14 @@ static void semundo_clear(int semid, int semnum) { struct sem_undo *suptr; + int suidx; - for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) { + for (suidx = semu_list_idx; suidx != -1; suidx = suptr->un_next_idx) { struct undo *sueptr; struct undo **suepptr; int i = 0; + suptr = SEMU(suidx); sueptr = suptr->un_ent; suepptr = &suptr->un_ent; while (i < suptr->un_cnt) { @@ -616,7 +648,7 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval) kauth_cred_t cred = kauth_cred_get(); int i, rval, eval; struct user_semid_ds sbuf; - struct user_semid_ds *semaptr; + struct semid_kernel *semakptr; struct user_semid_ds uds; @@ -639,38 +671,46 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval) goto semctlout; } - semaptr = &sema[semid]; - if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || - semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) { + semakptr = &sema[semid]; + if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 || + semakptr->u.sem_perm._seq != IPCID_TO_SEQ(uap->semid)) { eval = EINVAL; goto semctlout; } +#if CONFIG_MACF + eval = mac_sysvsem_check_semctl(cred, semakptr, cmd); + if (eval) + goto semctlout; +#endif eval = 0; rval = 0; switch (cmd) { case IPC_RMID: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M))) + if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_M))) goto semctlout; - semaptr->sem_perm.cuid = kauth_cred_getuid(cred); - semaptr->sem_perm.uid = kauth_cred_getuid(cred); - semtot -= semaptr->sem_nsems; - for (i = semaptr->sem_base - sem_pool; i < semtot; i++) - sem_pool[i] = sem_pool[i + semaptr->sem_nsems]; + semakptr->u.sem_perm.cuid = kauth_cred_getuid(cred); + semakptr->u.sem_perm.uid = kauth_cred_getuid(cred); + semtot -= semakptr->u.sem_nsems; + for (i = semakptr->u.sem_base - sem_pool; i < semtot; i++) + sem_pool[i] = sem_pool[i + semakptr->u.sem_nsems]; for (i = 0; i < seminfo.semmni; i++) { - if ((sema[i].sem_perm.mode & SEM_ALLOC) && - sema[i].sem_base > semaptr->sem_base) - sema[i].sem_base -= semaptr->sem_nsems; + if ((sema[i].u.sem_perm.mode & SEM_ALLOC) && + sema[i].u.sem_base > semakptr->u.sem_base) + sema[i].u.sem_base -= semakptr->u.sem_nsems; } - semaptr->sem_perm.mode = 0; + semakptr->u.sem_perm.mode = 0; +#if CONFIG_MACF + mac_sysvsem_label_recycle(semakptr); +#endif semundo_clear(semid, -1); - wakeup((caddr_t)semaptr); + wakeup((caddr_t)semakptr); break; case IPC_SET: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M))) + if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_M))) goto semctlout; if (IS_64BIT_PROCESS(p)) { @@ -685,17 +725,17 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval) goto semctlout; } - 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) | - (sbuf.sem_perm.mode & 0777); - semaptr->sem_ctime = sysv_semtime(); + semakptr->u.sem_perm.uid = sbuf.sem_perm.uid; + semakptr->u.sem_perm.gid = sbuf.sem_perm.gid; + semakptr->u.sem_perm.mode = (semakptr->u.sem_perm.mode & + ~0777) | (sbuf.sem_perm.mode & 0777); + semakptr->u.sem_ctime = sysv_semtime(); break; case IPC_STAT: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) + if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_R))) goto semctlout; - bcopy(semaptr, &uds, sizeof(struct user_semid_ds)); + 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)); } else { @@ -706,42 +746,42 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval) break; case GETNCNT: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) + if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_R))) goto semctlout; - if (semnum < 0 || semnum >= semaptr->sem_nsems) { + if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { eval = EINVAL; goto semctlout; } - rval = semaptr->sem_base[semnum].semncnt; + rval = semakptr->u.sem_base[semnum].semncnt; break; case GETPID: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) + if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_R))) goto semctlout; - if (semnum < 0 || semnum >= semaptr->sem_nsems) { + if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { eval = EINVAL; goto semctlout; } - rval = semaptr->sem_base[semnum].sempid; + rval = semakptr->u.sem_base[semnum].sempid; break; case GETVAL: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) + if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_R))) goto semctlout; - if (semnum < 0 || semnum >= semaptr->sem_nsems) { + if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { eval = EINVAL; goto semctlout; } - rval = semaptr->sem_base[semnum].semval; + rval = semakptr->u.sem_base[semnum].semval; break; case GETALL: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) + if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_R))) goto semctlout; /* XXXXXXXXXXXXXXXX TBD XXXXXXXXXXXXXXXX */ - for (i = 0; i < semaptr->sem_nsems; i++) { + for (i = 0; i < semakptr->u.sem_nsems; i++) { /* XXX could be done in one go... */ - eval = copyout((caddr_t)&semaptr->sem_base[i].semval, + eval = copyout((caddr_t)&semakptr->u.sem_base[i].semval, user_arg.array + (i * sizeof(unsigned short)), sizeof(unsigned short)); if (eval != 0) @@ -750,24 +790,24 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval) break; case GETZCNT: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) + if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_R))) goto semctlout; - if (semnum < 0 || semnum >= semaptr->sem_nsems) { + if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { eval = EINVAL; goto semctlout; } - rval = semaptr->sem_base[semnum].semzcnt; + rval = semakptr->u.sem_base[semnum].semzcnt; break; case SETVAL: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) + if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_W))) { #ifdef SEM_DEBUG printf("Invalid credentials for write\n"); #endif goto semctlout; } - if (semnum < 0 || semnum >= semaptr->sem_nsems) + if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { #ifdef SEM_DEBUG printf("Invalid number out of range for set\n"); @@ -780,25 +820,29 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval) * to avoid introducing endieness and a pad field into the * header file. Ugly, but it works. */ - semaptr->sem_base[semnum].semval = CAST_DOWN(int,user_arg.buf); + semakptr->u.sem_base[semnum].semval = CAST_DOWN(int,user_arg.buf); + semakptr->u.sem_base[semnum].sempid = p->p_pid; + /* XXX scottl Should there be a MAC call here? */ semundo_clear(semid, semnum); - wakeup((caddr_t)semaptr); + wakeup((caddr_t)semakptr); break; case SETALL: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) + if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_W))) goto semctlout; /*** XXXXXXXXXXXX TBD ********/ - for (i = 0; i < semaptr->sem_nsems; i++) { + for (i = 0; i < semakptr->u.sem_nsems; i++) { /* XXX could be done in one go... */ eval = copyin(user_arg.array + (i * sizeof(unsigned short)), - (caddr_t)&semaptr->sem_base[i].semval, + (caddr_t)&semakptr->u.sem_base[i].semval, sizeof(unsigned short)); if (eval != 0) break; + semakptr->u.sem_base[i].sempid = p->p_pid; } + /* XXX scottl Should there be a MAC call here? */ semundo_clear(semid, -1); - wakeup((caddr_t)semaptr); + wakeup((caddr_t)semakptr); break; default: @@ -835,18 +879,18 @@ semget(__unused struct proc *p, struct semget_args *uap, register_t *retval) if (key != IPC_PRIVATE) { for (semid = 0; semid < seminfo.semmni; semid++) { - if ((sema[semid].sem_perm.mode & SEM_ALLOC) && - sema[semid].sem_perm.key == key) + if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) && + sema[semid].u.sem_perm._key == key) break; } if (semid < seminfo.semmni) { #ifdef SEM_DEBUG printf("found public key\n"); #endif - if ((eval = ipcperm(cred, &sema[semid].sem_perm, + if ((eval = ipcperm(cred, &sema[semid].u.sem_perm, semflg & 0700))) goto semgetout; - if (nsems < 0 || sema[semid].sem_nsems < nsems) { + if (nsems < 0 || sema[semid].u.sem_nsems < nsems) { #ifdef SEM_DEBUG printf("too small\n"); #endif @@ -860,6 +904,11 @@ semget(__unused struct proc *p, struct semget_args *uap, register_t *retval) eval = EEXIST; goto semgetout; } +#if CONFIG_MACF + eval = mac_sysvsem_check_semget(cred, &sema[semid]); + if (eval) + goto semgetout; +#endif goto found; } } @@ -890,7 +939,7 @@ semget(__unused struct proc *p, struct semget_args *uap, register_t *retval) } } for (semid = 0; semid < seminfo.semmni; semid++) { - if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0) + if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) == 0) break; } if (semid == seminfo.semmni) { @@ -909,23 +958,26 @@ semget(__unused struct proc *p, struct semget_args *uap, register_t *retval) #ifdef SEM_DEBUG printf("semid %d is available\n", semid); #endif - sema[semid].sem_perm.key = key; - sema[semid].sem_perm.cuid = kauth_cred_getuid(cred); - sema[semid].sem_perm.uid = kauth_cred_getuid(cred); - sema[semid].sem_perm.cgid = cred->cr_gid; - sema[semid].sem_perm.gid = cred->cr_gid; - sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC; - sema[semid].sem_perm.seq = - (sema[semid].sem_perm.seq + 1) & 0x7fff; - sema[semid].sem_nsems = nsems; - sema[semid].sem_otime = 0; - sema[semid].sem_ctime = sysv_semtime(); - sema[semid].sem_base = &sem_pool[semtot]; + 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.mode = (semflg & 0777) | SEM_ALLOC; + sema[semid].u.sem_perm._seq = + (sema[semid].u.sem_perm._seq + 1) & 0x7fff; + sema[semid].u.sem_nsems = nsems; + sema[semid].u.sem_otime = 0; + sema[semid].u.sem_ctime = sysv_semtime(); + sema[semid].u.sem_base = &sem_pool[semtot]; semtot += nsems; - bzero(sema[semid].sem_base, - sizeof(sema[semid].sem_base[0])*nsems); + bzero(sema[semid].u.sem_base, + sizeof(sema[semid].u.sem_base[0])*nsems); +#if CONFIG_MACF + mac_sysvsem_label_associate(cred, &sema[semid]); +#endif #ifdef SEM_DEBUG - printf("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base, + printf("sembase = 0x%x, next = 0x%x\n", sema[semid].u.sem_base, &sem_pool[semtot]); #endif } else { @@ -937,7 +989,7 @@ semget(__unused struct proc *p, struct semget_args *uap, register_t *retval) } found: - *retval = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm); + *retval = IXSEQ_TO_IPCID(semid, sema[semid].u.sem_perm); AUDIT_ARG(svipc_id, *retval); #ifdef SEM_DEBUG printf("semget is done, returning %d\n", *retval); @@ -955,10 +1007,10 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval) int semid = uap->semid; int nsops = uap->nsops; struct sembuf sops[MAX_SOPS]; - register struct user_semid_ds *semaptr; + register struct semid_kernel *semakptr; register struct sembuf *sopptr = NULL; /* protected by 'semptr' */ register struct sem *semptr = NULL; /* protected by 'if' */ - struct sem_undo *suptr = NULL; + int supidx = -1; int i, j, eval; int do_wakeup, do_undos; @@ -977,17 +1029,17 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval) goto semopout; } - semaptr = &sema[semid]; - if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) { + semakptr = &sema[semid]; + if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0) { eval = EINVAL; goto semopout; } - if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) { + if (semakptr->u.sem_perm._seq != IPCID_TO_SEQ(uap->semid)) { eval = EINVAL; goto semopout; } - if ((eval = ipcperm(kauth_cred_get(), &semaptr->sem_perm, IPC_W))) { + if ((eval = ipcperm(kauth_cred_get(), &semakptr->u.sem_perm, IPC_W))) { #ifdef SEM_DEBUG printf("eval = %d from ipaccess\n", eval); #endif @@ -1002,6 +1054,24 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval) goto semopout; } +#if CONFIG_MACF + /* + * Initial pass thru sops to see what permissions are needed. + */ + j = 0; /* permission needed */ + for (i = 0; i < nsops; i++) + j |= (sops[i].sem_op == 0) ? SEM_R : SEM_A; + + /* + * The MAC hook checks whether the thread has read (and possibly + * write) permissions to the semaphore array based on the + * sopptr->sem_op value. + */ + eval = mac_sysvsem_check_semop(kauth_cred_get(), semakptr, j); + if (eval) + 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 @@ -1028,16 +1098,16 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval) for (i = 0; i < nsops; i++) { sopptr = &sops[i]; - if (sopptr->sem_num >= semaptr->sem_nsems) { + if (sopptr->sem_num >= semakptr->u.sem_nsems) { eval = EFBIG; goto semopout; } - semptr = &semaptr->sem_base[sopptr->sem_num]; + semptr = &semakptr->u.sem_base[sopptr->sem_num]; #ifdef SEM_DEBUG - printf("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n", - semaptr, semaptr->sem_base, semptr, + printf("semop: semakptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n", + semakptr, semakptr->u.sem_base, semptr, sopptr->sem_num, semptr->semval, sopptr->sem_op, (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"); #endif @@ -1085,7 +1155,7 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval) printf("semop: rollback 0 through %d\n", i-1); #endif for (j = 0; j < i; j++) - semaptr->sem_base[sops[j].sem_num].semval -= + semakptr->u.sem_base[sops[j].sem_num].semval -= sops[j].sem_op; /* @@ -1110,7 +1180,7 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval) * waiting for. We will get the lock back after we * wake up. */ - eval = msleep((caddr_t)semaptr, &sysv_sem_subsys_mutex , (PZERO - 4) | PCATCH, + eval = msleep((caddr_t)semakptr, &sysv_sem_subsys_mutex , (PZERO - 4) | PCATCH, "semwait", 0); #ifdef SEM_DEBUG @@ -1127,15 +1197,20 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval) * structures and re-validate them. */ - suptr = NULL; /* sem_undo may have been reallocated */ - semaptr = &sema[semid]; /* sema may have been reallocated */ + semptr = NULL; /* * Make sure that the semaphore still exists + * + * XXX POSIX: Third test this 'if' and 'EINTR' precedence may + * fail testing; if so, we will need to revert this code. */ - if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || - semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid) || - sopptr->sem_num >= semaptr->sem_nsems) { + semakptr = &sema[semid]; /* sema may have been reallocated */ + if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 || + semakptr->u.sem_perm._seq != IPCID_TO_SEQ(uap->semid) || + sopptr->sem_num >= semakptr->u.sem_nsems) { + /* The man page says to return EIDRM. */ + /* Unfortunately, BSD doesn't define that code! */ if (eval == EINTR) { /* * EINTR takes precedence over the fact that @@ -1143,14 +1218,10 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval) * sleeping... */ } else { - /* - * The man page says to return EIDRM. - * Unfortunately, BSD doesn't define that code! - */ #ifdef EIDRM eval = EIDRM; #else - eval = EINVAL; + eval = EINVAL; /* Ancient past */ #endif } goto semopout; @@ -1162,7 +1233,7 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval) * because the sem[] may have been reallocated while * we were sleeping, updating our sem_base pointer. */ - semptr = &semaptr->sem_base[sopptr->sem_num]; + semptr = &semakptr->u.sem_base[sopptr->sem_num]; if (sopptr->sem_op == 0) semptr->semzcnt--; else @@ -1190,7 +1261,7 @@ done: adjval = sops[i].sem_op; if (adjval == 0) continue; - eval = semundo_adjust(p, &suptr, semid, + eval = semundo_adjust(p, &supidx, semid, sops[i].sem_num, -adjval); if (eval == 0) continue; @@ -1210,13 +1281,13 @@ done: adjval = sops[j].sem_op; if (adjval == 0) continue; - if (semundo_adjust(p, &suptr, semid, + if (semundo_adjust(p, &supidx, semid, sops[j].sem_num, adjval) != 0) panic("semop - can't undo undos"); } for (j = 0; j < nsops; j++) - semaptr->sem_base[sops[j].sem_num].semval -= + semakptr->u.sem_base[sops[j].sem_num].semval -= sops[j].sem_op; #ifdef SEM_DEBUG @@ -1229,21 +1300,22 @@ done: /* We're definitely done - set the sempid's */ for (i = 0; i < nsops; i++) { sopptr = &sops[i]; - semptr = &semaptr->sem_base[sopptr->sem_num]; + semptr = &semakptr->u.sem_base[sopptr->sem_num]; semptr->sempid = p->p_pid; } + semakptr->u.sem_otime = sysv_semtime(); if (do_wakeup) { #ifdef SEM_DEBUG printf("semop: doing wakeup\n"); #ifdef SEM_WAKEUP - sem_wakeup((caddr_t)semaptr); + sem_wakeup((caddr_t)semakptr); #else - wakeup((caddr_t)semaptr); + wakeup((caddr_t)semakptr); #endif printf("semop: back from wakeup\n"); #else - wakeup((caddr_t)semaptr); + wakeup((caddr_t)semakptr); #endif } #ifdef SEM_DEBUG @@ -1263,8 +1335,9 @@ semopout: void semexit(struct proc *p) { - register struct sem_undo *suptr; - register struct sem_undo **supptr; + register struct sem_undo *suptr = NULL; + int suidx; + int *supidx; int did_something; /* If we have not allocated our semaphores yet there can't be @@ -1285,13 +1358,14 @@ semexit(struct proc *p) * associated with this process. */ - for (supptr = &semu_list; (suptr = *supptr) != NULL; - supptr = &suptr->un_next) { + for (supidx = &semu_list_idx; (suidx = *supidx) != -1; + supidx = &suptr->un_next_idx) { + suptr = SEMU(suidx); if (suptr->un_proc == p) break; } - if (suptr == NULL) + if (suidx == -1) goto unlock; #ifdef SEM_DEBUG @@ -1308,17 +1382,17 @@ semexit(struct proc *p) int semid; int semnum; int adjval; - struct user_semid_ds *semaptr; + struct semid_kernel *semakptr; sueptr = suptr->un_ent; semid = sueptr->une_id; semnum = sueptr->une_num; adjval = sueptr->une_adjval; - semaptr = &sema[semid]; - if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) + semakptr = &sema[semid]; + if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0) panic("semexit - semid not allocated"); - if (semnum >= semaptr->sem_nsems) + if (semnum >= semakptr->u.sem_nsems) panic("semexit - semnum out of range"); #ifdef SEM_DEBUG @@ -1327,19 +1401,19 @@ semexit(struct proc *p) semid, semnum, adjval, - semaptr->sem_base[semnum].semval); + semakptr->u.sem_base[semnum].semval); #endif if (adjval < 0) { - if (semaptr->sem_base[semnum].semval < -adjval) - semaptr->sem_base[semnum].semval = 0; + if (semakptr->u.sem_base[semnum].semval < -adjval) + semakptr->u.sem_base[semnum].semval = 0; else - semaptr->sem_base[semnum].semval += + semakptr->u.sem_base[semnum].semval += adjval; } else - semaptr->sem_base[semnum].semval += adjval; + semakptr->u.sem_base[semnum].semval += adjval; - /* Maybe we should build a list of semaptr's to wake + /* 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 @@ -1348,9 +1422,9 @@ semexit(struct proc *p) * in the BSD code at once. */ #ifdef SEM_WAKEUP - sem_wakeup((caddr_t)semaptr); + sem_wakeup((caddr_t)semakptr); #else - wakeup((caddr_t)semaptr); + wakeup((caddr_t)semakptr); #endif #ifdef SEM_DEBUG printf("semexit: back from wakeup\n"); @@ -1369,7 +1443,7 @@ semexit(struct proc *p) printf("removing vector\n"); #endif suptr->un_proc = NULL; - *supptr = suptr->un_next; + *supidx = suptr->un_next_idx; unlock: /* @@ -1413,7 +1487,7 @@ sysctl_seminfo(__unused struct sysctl_oid *oidp, void *arg1, if ((sem_pool == NULL) && (sema == NULL) && (semu == NULL) && - (semu_list == NULL)) { + (semu_list_idx == -1)) { if ((error = SYSCTL_IN(req, arg1, sizeof(int)))) { goto out; } @@ -1427,19 +1501,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, KSYSV_SEMMNI, semmni, CTLTYPE_INT | CTLFLAG_RW, +SYSCTL_PROC(_kern_sysv, OID_AUTO, semmni, CTLTYPE_INT | CTLFLAG_RW, &limitseminfo.semmni, 0, &sysctl_seminfo ,"I","semmni"); -SYSCTL_PROC(_kern_sysv, KSYSV_SEMMNS, semmns, CTLTYPE_INT | CTLFLAG_RW, +SYSCTL_PROC(_kern_sysv, OID_AUTO, semmns, CTLTYPE_INT | CTLFLAG_RW, &limitseminfo.semmns, 0, &sysctl_seminfo ,"I","semmns"); -SYSCTL_PROC(_kern_sysv, KSYSV_SEMMNU, semmnu, CTLTYPE_INT | CTLFLAG_RW, +SYSCTL_PROC(_kern_sysv, OID_AUTO, semmnu, CTLTYPE_INT | CTLFLAG_RW, &limitseminfo.semmnu, 0, &sysctl_seminfo ,"I","semmnu"); -SYSCTL_PROC(_kern_sysv, KSYSV_SEMMSL, semmsl, CTLTYPE_INT | CTLFLAG_RW, +SYSCTL_PROC(_kern_sysv, OID_AUTO, semmsl, CTLTYPE_INT | CTLFLAG_RW, &limitseminfo.semmsl, 0, &sysctl_seminfo ,"I","semmsl"); -SYSCTL_PROC(_kern_sysv, KSYSV_SEMUNE, semume, CTLTYPE_INT | CTLFLAG_RW, +SYSCTL_PROC(_kern_sysv, OID_AUTO, semume, CTLTYPE_INT | CTLFLAG_RW, &limitseminfo.semume, 0, &sysctl_seminfo ,"I","semume"); @@ -1459,15 +1533,18 @@ IPCS_sem_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1, size_t semid_ds_sz = sizeof(struct user_semid_ds); struct proc *p = current_proc(); + if (!IS_64BIT_PROCESS(p)) { + ipcs_sz = sizeof(struct IPCS_command); + semid_ds_sz = sizeof(struct semid_ds); + } + /* Copy in the command structure */ if ((error = SYSCTL_IN(req, &ipcs, ipcs_sz)) != 0) { return(error); } - if (!IS_64BIT_PROCESS(p)) { - ipcs_sz = sizeof(struct IPCS_command); - semid_ds_sz = sizeof(struct semid_ds); - } + if (!IS_64BIT_PROCESS(p)) /* convert in place */ + ipcs.u64.ipcs_data = CAST_USER_ADDR_T(ipcs.u32.ipcs_data); /* Let us version this interface... */ if (ipcs.u64.ipcs_magic != IPCS_MAGIC) { @@ -1499,7 +1576,7 @@ IPCS_sem_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1, break; } for( ; cursor < seminfo.semmni; cursor++) { - if (sema[cursor].sem_perm.mode & SEM_ALLOC) + if (sema[cursor].u.sem_perm.mode & SEM_ALLOC) break; continue; } @@ -1508,7 +1585,7 @@ IPCS_sem_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1, break; } - semid_dsp = &sema[cursor]; /* default: 64 bit */ + semid_dsp = &sema[cursor].u; /* default: 64 bit */ /* * If necessary, convert the 64 bit kernel segment @@ -1539,3 +1616,5 @@ SYSCTL_PROC(_kern_sysv_ipcs, OID_AUTO, sem, CTLFLAG_RW|CTLFLAG_ANYBODY, 0, 0, IPCS_sem_sysctl, "S,IPCS_sem_command", "ipcs sem command interface"); + +#endif /* SYSV_SEM */