[PATCH] fix do_wait() vs exec() race
authorOleg Nesterov <oleg@tv-sign.ru>
Wed, 23 Nov 2005 21:37:43 +0000 (13:37 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Thu, 24 Nov 2005 00:08:39 +0000 (16:08 -0800)
When non-leader thread does exec, de_thread adds old leader to the init's
->children list in EXIT_ZOMBIE state and drops tasklist_lock.

This means that release_task(leader) in de_thread() is racy vs do_wait()
from init task.

I think de_thread() should set old leader's state to EXIT_DEAD instead.

Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>
Cc: george anzinger <george@mvista.com>
Cc: Roland Dreier <rolandd@cisco.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/exec.c

index c466fec5de2005cee77acfc7a2f120afefcd33f8..1f8a9fd2c9ed1de1cb8d8413bcfdc197024ab7fe 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -668,7 +668,7 @@ static inline int de_thread(struct task_struct *tsk)
        if (!thread_group_leader(current)) {
                struct task_struct *parent;
                struct dentry *proc_dentry1, *proc_dentry2;
-               unsigned long exit_state, ptrace;
+               unsigned long ptrace;
 
                /*
                 * Wait for the thread group leader to be a zombie.
@@ -726,15 +726,15 @@ static inline int de_thread(struct task_struct *tsk)
                list_del(&current->tasks);
                list_add_tail(&current->tasks, &init_task.tasks);
                current->exit_signal = SIGCHLD;
-               exit_state = leader->exit_state;
+
+               BUG_ON(leader->exit_state != EXIT_ZOMBIE);
+               leader->exit_state = EXIT_DEAD;
 
                write_unlock_irq(&tasklist_lock);
                spin_unlock(&leader->proc_lock);
                spin_unlock(&current->proc_lock);
                proc_pid_flush(proc_dentry1);
                proc_pid_flush(proc_dentry2);
-
-               BUG_ON(exit_state != EXIT_ZOMBIE);
         }
 
        /*