forked from Minki/linux
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>
This commit is contained in:
parent
99db67bc04
commit
60187d2708
@ -1300,20 +1300,19 @@ struct sigqueue *sigqueue_alloc(void)
|
||||
void sigqueue_free(struct sigqueue *q)
|
||||
{
|
||||
unsigned long flags;
|
||||
spinlock_t *lock = ¤t->sighand->siglock;
|
||||
|
||||
BUG_ON(!(q->flags & SIGQUEUE_PREALLOC));
|
||||
/*
|
||||
* If the signal is still pending remove it from the
|
||||
* pending queue.
|
||||
* pending queue. We must hold ->siglock while testing
|
||||
* q->list to serialize with collect_signal().
|
||||
*/
|
||||
if (unlikely(!list_empty(&q->list))) {
|
||||
spinlock_t *lock = ¤t->sighand->siglock;
|
||||
read_lock(&tasklist_lock);
|
||||
spin_lock_irqsave(lock, flags);
|
||||
if (!list_empty(&q->list))
|
||||
list_del_init(&q->list);
|
||||
spin_unlock_irqrestore(lock, flags);
|
||||
read_unlock(&tasklist_lock);
|
||||
}
|
||||
spin_lock_irqsave(lock, flags);
|
||||
if (!list_empty(&q->list))
|
||||
list_del_init(&q->list);
|
||||
spin_unlock_irqrestore(lock, flags);
|
||||
|
||||
q->flags &= ~SIGQUEUE_PREALLOC;
|
||||
__sigqueue_free(q);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user