Commit:     60187d2708caa870f0825d753df1612ea688eb9e
Parent:     99db67bc04af0f2e8cb710ac92aaeb9af135a7c6
Author:     Oleg Nesterov <[EMAIL PROTECTED]>
AuthorDate: Thu Aug 30 23:56:35 2007 -0700
Committer:  Linus Torvalds <[EMAIL PROTECTED]>
CommitDate: Fri Aug 31 01:42:23 2007 -0700

    sigqueue_free: fix the race with collect_signal()
    Spotted by taoyue <[EMAIL PROTECTED]> and Jeremy Katz <[EMAIL PROTECTED]>.
    collect_signal:                             sigqueue_free:
                                                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 
    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 
    the "impossible" BUG_ON(!SIGQUEUE_PREALLOC) in sigqueue_free/send_sigqueue.
    Hopefully this can explain these mysterious bug-reports, see
    Alexey Dobriyan reports this patch makes the difference for the testcase, 
    nobody has an access to the application which opened the problems 
    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]>
    Signed-off-by: Andrew Morton <[EMAIL PROTECTED]>
    Signed-off-by: Linus Torvalds <[EMAIL PROTECTED]>
 kernel/signal.c |   19 +++++++++----------
 1 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/kernel/signal.c b/kernel/signal.c
index ad63109..3169bed 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -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;
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at

Reply via email to