X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/cc9f6e38162d3c1bf6ca97536c2477f476c8e01b..3a60a9f5b85abb8c2cf24e1926c5c7b3f608a5e2:/bsd/kern/sysv_sem.c?ds=sidebyside diff --git a/bsd/kern/sysv_sem.c b/bsd/kern/sysv_sem.c index 8f7b26537..24bc5cfad 100644 --- a/bsd/kern/sysv_sem.c +++ b/bsd/kern/sysv_sem.c @@ -248,12 +248,6 @@ grow_semu_array(int newSize) { register int i; register struct sem_undo *newSemu; - static boolean_t grow_semu_array_in_progress = FALSE; - - while (grow_semu_array_in_progress) { - msleep(&grow_semu_array_in_progress, &sysv_sem_subsys_mutex, - PPAUSE, "grow_semu_array", NULL); - } if (newSize <= seminfo.semmnu) return 1; @@ -271,13 +265,8 @@ grow_semu_array(int newSize) #ifdef SEM_DEBUG printf("growing semu[] from %d to %d\n", seminfo.semmnu, newSize); #endif - grow_semu_array_in_progress = TRUE; - SYSV_SEM_SUBSYS_UNLOCK(); - MALLOC(newSemu, struct sem_undo*, sizeof(struct sem_undo) * newSize, - M_SYSVSEM, M_WAITOK); - SYSV_SEM_SUBSYS_LOCK(); - grow_semu_array_in_progress = FALSE; - wakeup((caddr_t) &grow_semu_array_in_progress); + MALLOC(newSemu, struct sem_undo *, sizeof (struct sem_undo) * newSize, + M_SYSVSEM, M_WAITOK | M_ZERO); if (NULL == newSemu) { #ifdef SEM_DEBUG @@ -286,15 +275,16 @@ grow_semu_array(int newSize) return 0; } - /* Initialize our structure. */ + /* copy the old data to the new array */ for (i = 0; i < seminfo.semmnu; i++) { newSemu[i] = semu[i]; } - for (i = seminfo.semmnu; i < newSize; i++) - { - newSemu[i].un_proc = NULL; - } + /* + * The new elements (from newSemu[i] to newSemu[newSize-1]) have their + * "un_proc" set to 0 (i.e. NULL) by the M_ZERO flag to MALLOC() above, + * so they're already marked as "not in use". + */ /* Clean up the old array */ if (semu) @@ -336,8 +326,9 @@ 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, - M_SYSVSEM, M_WAITOK); + MALLOC(newSema, struct user_semid_ds *, + sizeof (struct user_semid_ds) * newSize, + M_SYSVSEM, M_WAITOK | M_ZERO); if (NULL == newSema) { #ifdef SEM_DEBUG @@ -346,7 +337,7 @@ grow_sema_array(int newSize) return 0; } - /* Initialize our new ids, and copy over the old ones */ + /* copy over the old ids */ for (i = 0; i < seminfo.semmni; i++) { newSema[i] = sema[i]; @@ -361,12 +352,11 @@ grow_sema_array(int newSize) if (sema[i].sem_perm.mode & SEM_ALLOC) wakeup((caddr_t)&sema[i]); } - - for (i = seminfo.semmni; i < newSize; i++) - { - newSema[i].sem_base = NULL; - newSema[i].sem_perm.mode = 0; - } + /* + * 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 + * flag to MALLOC() above, so they're already marked as "not in use". + */ /* Clean up the old array */ if (sema) @@ -410,8 +400,8 @@ grow_sem_pool(int new_pool_size) #ifdef SEM_DEBUG printf("growing sem_pool array from %d to %d\n", seminfo.semmns, new_pool_size); #endif - MALLOC(new_sem_pool, struct sem *, sizeof(struct sem) * new_pool_size, - M_SYSVSEM, M_WAITOK); + MALLOC(new_sem_pool, struct sem *, sizeof (struct sem) * new_pool_size, + M_SYSVSEM, M_WAITOK | M_ZERO); if (NULL == new_sem_pool) { #ifdef SEM_DEBUG printf("allocation failed. no changes made.\n"); @@ -535,8 +525,9 @@ semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, register struct undo *sueptr, **suepptr, *new_sueptr; int i; - /* Look for and remember the sem_undo if the caller doesn't provide - it */ + /* + * Look for and remember the sem_undo if the caller doesn't provide it + */ suptr = *supptr; if (suptr == NULL) { @@ -562,7 +553,6 @@ semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, * 0). */ new_sueptr = NULL; -lookup: for (i = 0, suepptr = &suptr->un_ent, sueptr = suptr->un_ent; i < suptr->un_cnt; i++, suepptr = &sueptr->une_next, sueptr = sueptr->une_next) { @@ -578,61 +568,36 @@ lookup: FREE(sueptr, M_SYSVSEM); sueptr = NULL; } - if (new_sueptr != NULL) { - /* - * We lost the race: free the "undo" entry we allocated - * and use the one that won. - */ - FREE(new_sueptr, M_SYSVSEM); - new_sueptr = NULL; - } - return(0); + return 0; } /* Didn't find the right entry - create it */ if (adjval == 0) { - if (new_sueptr != NULL) { - FREE(new_sueptr, M_SYSVSEM); - new_sueptr = NULL; - } - return(0); + /* no adjustment: no need for a new entry */ + return 0; } - if (new_sueptr != NULL) { - /* - * Use the new "undo" entry we allocated in the previous pass - */ - new_sueptr->une_next = suptr->un_ent; - suptr->un_ent = new_sueptr; - suptr->un_cnt++; - new_sueptr->une_adjval = adjval; - new_sueptr->une_id = semid; - new_sueptr->une_num = semnum; - return 0; + if (suptr->un_cnt == limitseminfo.semume) { + /* reached the limit number of semaphore undo entries */ + return EINVAL; } - if (suptr->un_cnt != limitseminfo.semume) { - SYSV_SEM_SUBSYS_UNLOCK(); - /* - * Unlocking opens the door to race conditions. Someone else - * could be trying to allocate the same thing at this point, - * so we'll have to check if we lost the race. - */ - MALLOC(new_sueptr, struct undo *, sizeof (struct undo), - M_SYSVSEM, M_WAITOK); - SYSV_SEM_SUBSYS_LOCK(); - if (new_sueptr == NULL) { - return ENOMEM; - } - /* - * There might be other threads doing the same thing for this - * process, so check again if an "undo" entry exists for that - * semaphore. - */ - goto lookup; - } else - return(EINVAL); - return(0); + /* allocate a new semaphore undo entry */ + MALLOC(new_sueptr, struct undo *, sizeof (struct undo), + M_SYSVSEM, M_WAITOK); + if (new_sueptr == NULL) { + return ENOMEM; + } + + /* fill in the new semaphore undo entry */ + new_sueptr->une_next = suptr->un_ent; + suptr->un_ent = new_sueptr; + suptr->un_cnt++; + new_sueptr->une_adjval = adjval; + new_sueptr->une_id = semid; + new_sueptr->une_num = semnum; + + return 0; } /* Assumes we already hold the subsystem lock. @@ -742,8 +707,6 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval) if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M))) goto semctlout; - SYSV_SEM_SUBSYS_UNLOCK(); - if (IS_64BIT_PROCESS(p)) { eval = copyin(user_arg.buf, &sbuf, sizeof(struct user_semid_ds)); } else { @@ -752,10 +715,9 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval) semid_ds_32to64((struct semid_ds *)&sbuf, &sbuf); } - if (eval != 0) - return(eval); - - SYSV_SEM_SUBSYS_LOCK(); + if (eval != 0) { + goto semctlout; + } semaptr->sem_perm.uid = sbuf.sem_perm.uid; semaptr->sem_perm.gid = sbuf.sem_perm.gid; @@ -768,7 +730,6 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval) if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) goto semctlout; bcopy(semaptr, &uds, sizeof(struct user_semid_ds)); - SYSV_SEM_SUBSYS_UNLOCK(); if (IS_64BIT_PROCESS(p)) { eval = copyout(&uds, user_arg.buf, sizeof(struct user_semid_ds)); } else { @@ -776,7 +737,6 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval) semid_ds_64to32(&uds, &semid_ds32); eval = copyout(&semid_ds32, user_arg.buf, sizeof(struct semid_ds)); } - SYSV_SEM_SUBSYS_LOCK(); break; case GETNCNT: @@ -904,7 +864,7 @@ semget(__unused struct proc *p, struct semget_args *uap, register_t *retval) #endif - SYSV_SEM_SUBSYS_LOCK(); + SYSV_SEM_SUBSYS_LOCK(); if (key != IPC_PRIVATE) { @@ -1190,42 +1150,44 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval) #ifdef SEM_DEBUG printf("semop: good morning (eval=%d)!\n", eval); #endif - /* we need the lock here due to mods on semptr */ if (eval != 0) { - if (sopptr->sem_op == 0) - semptr->semzcnt--; - else - semptr->semncnt--; - eval = EINTR; - goto semopout; } + /* + * IMPORTANT: while we were asleep, the semaphore array might + * have been reallocated somewhere else (see grow_sema_array()). + * When we wake up, we have to re-lookup the semaphore + * structures and re-validate them. + */ + suptr = NULL; /* sem_undo may have been reallocated */ semaptr = &sema[semid]; /* sema may have been reallocated */ - -#ifdef SEM_DEBUG - printf("semop: good morning!\n"); -#endif - /* * Make sure that the semaphore still exists */ if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || - semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) { - /* The man page says to return EIDRM. */ - /* Unfortunately, BSD doesn't define that code! */ - if (sopptr->sem_op == 0) - semptr->semzcnt--; - else - semptr->semncnt--; + semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid) || + sopptr->sem_num >= semaptr->sem_nsems) { + if (eval == EINTR) { + /* + * EINTR takes precedence over the fact that + * the semaphore disappeared while we were + * sleeping... + */ + } else { + /* + * The man page says to return EIDRM. + * Unfortunately, BSD doesn't define that code! + */ #ifdef EIDRM - eval = EIDRM; + eval = EIDRM; #else - eval = EINVAL; + eval = EINVAL; #endif - goto semopout; + } + goto semopout; } /* @@ -1239,6 +1201,10 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval) semptr->semzcnt--; else semptr->semncnt--; + + if (eval != 0) { /* EINTR */ + goto semopout; + } } done: @@ -1553,9 +1519,7 @@ IPCS_sem_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1, error = EINVAL; break; } - SYSV_SEM_SUBSYS_UNLOCK(); error = copyout(&seminfo, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen); - SYSV_SEM_SUBSYS_LOCK(); break; case IPCS_SEM_ITER: /* Iterate over existing segments */ @@ -1588,14 +1552,12 @@ IPCS_sem_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1, semid_ds_64to32(semid_dsp, &semid_ds32); semid_dsp = &semid_ds32; } - SYSV_SEM_SUBSYS_UNLOCK(); error = copyout(semid_dsp, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen); if (!error) { /* update cursor */ ipcs.u64.ipcs_cursor = cursor + 1; error = SYSCTL_OUT(req, &ipcs, ipcs_sz); } - SYSV_SEM_SUBSYS_LOCK(); break; default: