diff options
| author | Roland McGrath <roland@redhat.com> | 2005-01-14 23:39:11 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-01-14 23:39:11 -0800 |
| commit | 0f9dea3fc12cfac611ca90d0ed0f6cbc063d2134 (patch) | |
| tree | bf58e16470c37339629589e6856cea260bd74e54 | |
| parent | 040e3e09c0905972d58c290240e1f626a8692565 (diff) | |
[PATCH] fix exec deadlock when ptrace used inside the thread group
If one thread uses ptrace on another thread in the same thread group, there
can be a deadlock when calling exec. The ptrace_stop change ensures that
no tracing stop can be entered for a queued signal, or exit tracing, if the
tracer is part of the same dying group. The exit_notify change prevents a
ptrace zombie from sticking around if its tracer is in the midst of a group
exit (which an exec fakes), so these zombies don't hold up de_thread's
synchronization. The de_thread change ensures the new thread group leader
doesn't wind up ptracing itself, which would produce its own deadlocks.
Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
| -rw-r--r-- | fs/exec.c | 8 | ||||
| -rw-r--r-- | kernel/exit.c | 4 | ||||
| -rw-r--r-- | kernel/signal.c | 4 |
3 files changed, 14 insertions, 2 deletions
diff --git a/fs/exec.c b/fs/exec.c index 24f873e55080..8459791d6bc1 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -685,6 +685,14 @@ static inline int de_thread(struct task_struct *tsk) */ ptrace = leader->ptrace; parent = leader->parent; + if (unlikely(ptrace) && unlikely(parent == current)) { + /* + * Joker was ptracing his own group leader, + * and now he wants to be his own parent! + * We can't have that. + */ + ptrace = 0; + } ptrace_unlink(current); ptrace_unlink(leader); diff --git a/kernel/exit.c b/kernel/exit.c index e0df301a4553..3171228f25c3 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -747,7 +747,9 @@ static void exit_notify(struct task_struct *tsk) } state = EXIT_ZOMBIE; - if (tsk->exit_signal == -1 && tsk->ptrace == 0) + if (tsk->exit_signal == -1 && + (likely(tsk->ptrace == 0) || + unlikely(tsk->parent->signal->flags & SIGNAL_GROUP_EXIT))) state = EXIT_DEAD; tsk->exit_state = state; diff --git a/kernel/signal.c b/kernel/signal.c index 6d0a3bd948ab..d98e9624ea30 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1588,7 +1588,9 @@ static void ptrace_stop(int exit_code, int nostop_code, siginfo_t *info) read_lock(&tasklist_lock); if (likely(current->ptrace & PT_PTRACED) && likely(current->parent != current->real_parent || - !(current->ptrace & PT_ATTACHED))) { + !(current->ptrace & PT_ATTACHED)) && + (likely(current->parent->signal != current->signal) || + !unlikely(current->signal->flags & SIGNAL_GROUP_EXIT))) { do_notify_parent_cldstop(current, current->parent, CLD_TRAPPED); read_unlock(&tasklist_lock); |
