Currently ptrace_detach does UTRACE_DETACH then ptrace_unlink(). This is not right, UTRACE_DETACH wakes up the tracee. It can stop/die after that, but since it is still task_ptrace() this can confuse its ->real_parent.
We can't solve this if we add __wake_up_parent(->real_parent). So change the order. And move most of the code into the new helper, ptrace_do_detach(). It will be use by exit_ptrace(). --- kernel/ptrace.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) --- PU/kernel/ptrace.c~12_REWORK_DETACH 2009-08-22 20:06:42.000000000 +0200 +++ PU/kernel/ptrace.c 2009-08-22 20:08:23.000000000 +0200 @@ -740,28 +740,38 @@ static bool __ptrace_detach(struct task_ return false; } -int ptrace_detach(struct task_struct *child, unsigned int data) +static void ptrace_do_detach(struct task_struct *tracee, unsigned int data) { - bool dead = false; - - if (!valid_signal(data)) - return -EIO; - - ptrace_detach_task(child, data); + bool traced, dead; write_lock_irq(&tasklist_lock); /* - * This child can be already killed. Make sure de_thread() or + * This tracee can be already killed. Make sure de_thread() or * our sub-thread doing do_wait() didn't do release_task() yet. */ - if (child->ptrace) { - child->exit_code = data; - dead = __ptrace_detach(current, child); + traced = tracee->ptrace; + if (likely(traced)) { + tracee->exit_code = data; + dead = __ptrace_detach(current, tracee); } write_unlock_irq(&tasklist_lock); - if (unlikely(dead)) - release_task(child); + if (unlikely(!traced)) + return; + if (unlikely(dead)) { + release_task(tracee); + return; + } + + ptrace_detach_task(tracee, data); +} + +int ptrace_detach(struct task_struct *child, unsigned int data) +{ + if (!valid_signal(data)) + return -EIO; + + ptrace_do_detach(child, data); return 0; }