From: Roland McGrath The __ptrace_unlink code that checks for TASK_TRACED fixed the problem of a thread being left in TASK_TRACED when no longer being ptraced. However, an oversight in the original fix made it fail to handle the case where the child is ptraced by its real parent. This patch fixes that. Signed-off-by: Roland McGrath Signed-off-by: Andrew Morton --- 25-akpm/include/linux/ptrace.h | 1 25-akpm/kernel/exit.c | 2 - 25-akpm/kernel/ptrace.c | 47 +++++++++++++++++++++-------------------- 3 files changed, 27 insertions(+), 23 deletions(-) diff -puN include/linux/ptrace.h~fix-__ptrace_unlink-task_traced-recovery-for-real-parent include/linux/ptrace.h --- 25/include/linux/ptrace.h~fix-__ptrace_unlink-task_traced-recovery-for-real-parent 2005-01-05 15:33:42.269524248 -0800 +++ 25-akpm/include/linux/ptrace.h 2005-01-05 15:33:42.275523336 -0800 @@ -87,6 +87,7 @@ extern void ptrace_notify(int exit_code) extern void __ptrace_link(struct task_struct *child, struct task_struct *new_parent); extern void __ptrace_unlink(struct task_struct *child); +extern void ptrace_untrace(struct task_struct *child); static inline void ptrace_link(struct task_struct *child, struct task_struct *new_parent) diff -puN kernel/exit.c~fix-__ptrace_unlink-task_traced-recovery-for-real-parent kernel/exit.c --- 25/kernel/exit.c~fix-__ptrace_unlink-task_traced-recovery-for-real-parent 2005-01-05 15:33:42.270524096 -0800 +++ 25-akpm/kernel/exit.c 2005-01-05 15:33:42.277523032 -0800 @@ -555,7 +555,7 @@ static inline void reparent_thread(task_ * a normal stop since it's no longer being * traced. */ - p->state = TASK_STOPPED; + ptrace_untrace(p); } } diff -puN kernel/ptrace.c~fix-__ptrace_unlink-task_traced-recovery-for-real-parent kernel/ptrace.c --- 25/kernel/ptrace.c~fix-__ptrace_unlink-task_traced-recovery-for-real-parent 2005-01-05 15:33:42.272523792 -0800 +++ 25-akpm/kernel/ptrace.c 2005-01-05 15:33:42.276523184 -0800 @@ -45,6 +45,23 @@ static inline int pending_resume_signal( } /* + * Turn a tracing stop into a normal stop now, since with no tracer there + * would be no way to wake it up with SIGCONT or SIGKILL. If there was a + * signal sent that would resume the child, but didn't because it was in + * TASK_TRACED, resume it now. + */ +void ptrace_untrace(task_t *child) +{ + spin_lock(&child->sighand->siglock); + child->state = TASK_STOPPED; + if (pending_resume_signal(&child->pending) || + pending_resume_signal(&child->signal->shared_pending)) { + signal_wake_up(child, 1); + } + spin_unlock(&child->sighand->siglock); +} + +/* * unptrace a task: move it back to its original parent and * remove it from the ptrace list. * @@ -55,29 +72,15 @@ void __ptrace_unlink(task_t *child) if (!child->ptrace) BUG(); child->ptrace = 0; - if (list_empty(&child->ptrace_list)) - return; - list_del_init(&child->ptrace_list); - REMOVE_LINKS(child); - child->parent = child->real_parent; - SET_LINKS(child); - - if (child->state == TASK_TRACED) { - /* - * Turn a tracing stop into a normal stop now, - * since with no tracer there would be no way - * to wake it up with SIGCONT or SIGKILL. - * If there was a signal sent that would resume the child, - * but didn't because it was in TASK_TRACED, resume it now. - */ - spin_lock(&child->sighand->siglock); - child->state = TASK_STOPPED; - if (pending_resume_signal(&child->pending) || - pending_resume_signal(&child->signal->shared_pending)) { - signal_wake_up(child, 1); - } - spin_unlock(&child->sighand->siglock); + if (!list_empty(&child->ptrace_list)) { + list_del_init(&child->ptrace_list); + REMOVE_LINKS(child); + child->parent = child->real_parent; + SET_LINKS(child); } + + if (child->state == TASK_TRACED) + ptrace_untrace(child); } /* _