Skip to content

Commit

Permalink
sigqueue_free: fix the race with collect_signal()
Browse files Browse the repository at this point in the history
Spotted by taoyue <[email protected]> and Jeremy Katz <[email protected]>.

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 <[email protected]>
Cc: taoyue <[email protected]>
Cc: Jeremy Katz <[email protected]>
Cc: Sukadev Bhattiprolu <[email protected]>
Cc: Alexey Dobriyan <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Roland McGrath <[email protected]>
Cc: <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Oleg Nesterov authored and Linus Torvalds committed Aug 31, 2007
1 parent 99db67b commit 60187d2
Showing 1 changed file with 9 additions and 10 deletions.
19 changes: 9 additions & 10 deletions kernel/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1300,20 +1300,19 @@ struct sigqueue *sigqueue_alloc(void)
void sigqueue_free(struct sigqueue *q)
{
unsigned long flags;
spinlock_t *lock = &current->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 = &current->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);
}
Expand Down

0 comments on commit 60187d2

Please sign in to comment.