diff options
author | Oleg Nesterov <oleg@redhat.com> | 2009-04-02 16:58:21 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-02 19:05:00 -0700 |
commit | 95a3540da9c81a5987be810e1d9a83640a366bd5 (patch) | |
tree | d5143f232f885f6aa8829288dc0ceb2cd9e6e34e | |
parent | bb24c679a51b1a9b726b901330649e3861814ac0 (diff) |
ptrace_detach: the wrong wakeup breaks the ERESTARTxxx logic
Another ancient bug. Consider this trivial test-case,
int main(void)
{
int pid = fork();
if (pid) {
ptrace(PTRACE_ATTACH, pid, NULL, NULL);
wait(NULL);
ptrace(PTRACE_DETACH, pid, NULL, NULL);
} else {
pause();
printf("WE HAVE A KERNEL BUG!!!\n");
}
return 0;
}
the child must not "escape" for sys_pause(), but it can and this was seen
in practice.
This is because ptrace_detach does:
if (!child->exit_state)
wake_up_process(child);
this wakeup can happen after this child has already restarted sys_pause(),
because it gets another wakeup from ptrace_untrace().
With or without this patch, perhaps sys_pause() needs a fix. But this
wakeup also breaks the SIGNAL_STOP_STOPPED logic in ptrace_untrace().
Remove this wakeup. The caller saw this task in TASK_TRACED state, and
unless it was SIGKILL'ed in between __ptrace_unlink()->ptrace_untrace()
should handle this case correctly. If it was SIGKILL'ed, we don't need to
wakup the dying tracee too.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Cc: Jerome Marchand <jmarchan@redhat.com>
Acked-by: Roland McGrath <roland@redhat.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | kernel/ptrace.c | 4 |
1 files changed, 0 insertions, 4 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index f5a9fa5aafa1..296e8105863a 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -301,11 +301,7 @@ int ptrace_detach(struct task_struct *child, unsigned int data) */ if (child->ptrace) { child->exit_code = data; - dead = __ptrace_detach(current, child); - - if (!child->exit_state) - wake_up_process(child); } write_unlock_irq(&tasklist_lock); |