diff --git a/ipc/sem.c b/ipc/sem.c index 5215a81420df..67f2110c8a80 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1606,16 +1606,27 @@ void exit_sem(struct task_struct *tsk) rcu_read_lock(); un = list_entry_rcu(ulp->list_proc.next, struct sem_undo, list_proc); - if (&un->list_proc == &ulp->list_proc) - semid = -1; - else - semid = un->semid; + if (&un->list_proc == &ulp->list_proc) { + /* + * We must wait for freeary() before freeing this ulp, + * in case we raced with last sem_undo. There is a small + * possibility where we exit while freeary() didn't + * finish unlocking sem_undo_list. + */ + spin_unlock_wait(&ulp->lock); + rcu_read_unlock(); + break; + } + spin_lock(&ulp->lock); + semid = un->semid; + spin_unlock(&ulp->lock); rcu_read_unlock(); + /* exit_sem raced with IPC_RMID, nothing to do */ if (semid == -1) - break; + continue; - sma = sem_lock_check(tsk->nsproxy->ipc_ns, un->semid); + sma = sem_lock_check(tsk->nsproxy->ipc_ns, semid); /* exit_sem raced with IPC_RMID, nothing to do */ if (IS_ERR(sma))