aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@tv-sign.ru>2007-08-31 02:56:35 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-08-31 04:42:23 -0400
commit60187d2708caa870f0825d753df1612ea688eb9e (patch)
tree35bbae1a2cdc1887ea1d85aa9c03ec0415dc5912
parent99db67bc04af0f2e8cb710ac92aaeb9af135a7c6 (diff)
sigqueue_free: fix the race with collect_signal()
Spotted by taoyue <yue.tao@windriver.com> and Jeremy Katz <jeremy.katz@windriver.com>. collect_signal: sigqueue_free: list_del_init(&first->list); if (!list_empty(&q->list)) { // not taken } q->flags &= ~SIGQUEUE_PREALLOC; __sigqueue_free(first); __sigqueue_free(q); Now, __sigqueue_free() is called twice on the same "struct sigqueue" with the obviously bad implications. In particular, this double free breaks the array_cache->avail logic, so the same sigqueue could be "allocated" twice, and the bug can manifest itself via the "impossible" BUG_ON(!SIGQUEUE_PREALLOC) in sigqueue_free/send_sigqueue. Hopefully this can explain these mysterious bug-reports, see http://marc.info/?t=118766926500003 http://marc.info/?t=118466273000005 Alexey Dobriyan reports this patch makes the difference for the testcase, but nobody has an access to the application which opened the problems originally. Also, this patch removes tasklist lock/unlock, ->siglock is enough. Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru> Cc: taoyue <yue.tao@windriver.com> Cc: Jeremy Katz <jeremy.katz@windriver.com> Cc: Sukadev Bhattiprolu <sukadev@us.ibm.com> Cc: Alexey Dobriyan <adobriyan@sw.ru> Cc: Ingo Molnar <mingo@elte.hu> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Roland McGrath <roland@redhat.com> Cc: <stable@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--kernel/signal.c19
1 files changed, 9 insertions, 10 deletions
diff --git a/kernel/signal.c b/kernel/signal.c
index ad63109e413c..3169bed0b4d0 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1300,20 +1300,19 @@ struct sigqueue *sigqueue_alloc(void)
1300void sigqueue_free(struct sigqueue *q) 1300void sigqueue_free(struct sigqueue *q)
1301{ 1301{
1302 unsigned long flags; 1302 unsigned long flags;
1303 spinlock_t *lock = &current->sighand->siglock;
1304
1303 BUG_ON(!(q->flags & SIGQUEUE_PREALLOC)); 1305 BUG_ON(!(q->flags & SIGQUEUE_PREALLOC));
1304 /* 1306 /*
1305 * If the signal is still pending remove it from the 1307 * If the signal is still pending remove it from the
1306 * pending queue. 1308 * pending queue. We must hold ->siglock while testing
1309 * q->list to serialize with collect_signal().
1307 */ 1310 */
1308 if (unlikely(!list_empty(&q->list))) { 1311 spin_lock_irqsave(lock, flags);
1309 spinlock_t *lock = &current->sighand->siglock; 1312 if (!list_empty(&q->list))
1310 read_lock(&tasklist_lock); 1313 list_del_init(&q->list);
1311 spin_lock_irqsave(lock, flags); 1314 spin_unlock_irqrestore(lock, flags);
1312 if (!list_empty(&q->list)) 1315
1313 list_del_init(&q->list);
1314 spin_unlock_irqrestore(lock, flags);
1315 read_unlock(&tasklist_lock);
1316 }
1317 q->flags &= ~SIGQUEUE_PREALLOC; 1316 q->flags &= ~SIGQUEUE_PREALLOC;
1318 __sigqueue_free(q); 1317 __sigqueue_free(q);
1319} 1318}