| From d694ad62bf539dbb20a0899ac2a954555f9e4a83 Mon Sep 17 00:00:00 2001 |
| From: Manfred Spraul <manfred@colorfullife.com> |
| Date: Mon, 25 Jul 2011 17:11:47 -0700 |
| Subject: ipc/sem.c: fix race with concurrent semtimedop() timeouts |
| and IPC_RMID |
| |
| From: Manfred Spraul <manfred@colorfullife.com> |
| |
| commit d694ad62bf539dbb20a0899ac2a954555f9e4a83 upstream. |
| |
| If a semaphore array is removed and in parallel a sleeping task is woken |
| up (signal or timeout, does not matter), then the woken up task does not |
| wait until wake_up_sem_queue_do() is completed. This will cause crashes, |
| because wake_up_sem_queue_do() will read from a stale pointer. |
| |
| The fix is simple: Regardless of anything, always call get_queue_result(). |
| This function waits until wake_up_sem_queue_do() has finished it's task. |
| |
| Addresses https://bugzilla.kernel.org/show_bug.cgi?id=27142 |
| |
| Reported-by: Yuriy Yevtukhov <yuriy@ucoz.com> |
| Reported-by: Harald Laabs <kernel@dasr.de> |
| Signed-off-by: Manfred Spraul <manfred@colorfullife.com> |
| Acked-by: Eric Dumazet <eric.dumazet@gmail.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| ipc/sem.c | 13 +++++++++++-- |
| 1 file changed, 11 insertions(+), 2 deletions(-) |
| |
| --- a/ipc/sem.c |
| +++ b/ipc/sem.c |
| @@ -1456,15 +1456,24 @@ SYSCALL_DEFINE4(semtimedop, int, semid, |
| } |
| |
| sma = sem_lock(ns, semid); |
| + |
| + /* |
| + * Wait until it's guaranteed that no wakeup_sem_queue_do() is ongoing. |
| + */ |
| + error = get_queue_result(&queue); |
| + |
| + /* |
| + * Array removed? If yes, leave without sem_unlock(). |
| + */ |
| if (IS_ERR(sma)) { |
| error = -EIDRM; |
| goto out_free; |
| } |
| |
| - error = get_queue_result(&queue); |
| |
| /* |
| - * If queue.status != -EINTR we are woken up by another process |
| + * If queue.status != -EINTR we are woken up by another process. |
| + * Leave without unlink_queue(), but with sem_unlock(). |
| */ |
| |
| if (error != -EINTR) { |