Commit a9625ef1 authored by Roland McGrath's avatar Roland McGrath Committed by Linus Torvalds

[PATCH] fix __ptrace_unlink TASK_TRACED recovery for real parent

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.

Fixed thus.
Signed-off-by: default avatarRoland McGrath <roland@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 819509c9
......@@ -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)
......
......@@ -557,7 +557,7 @@ static inline void reparent_thread(task_t *p, task_t *father, int traced)
* a normal stop since it's no longer being
* traced.
*/
p->state = TASK_STOPPED;
ptrace_untrace(p);
}
}
......
......@@ -44,6 +44,23 @@ static inline int pending_resume_signal(struct sigpending *pending)
return sigtestsetmask(&pending->signal, M(SIGCONT) | M(SIGKILL));
}
/*
* 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;
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) {
/*
* 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 (child->state == TASK_TRACED)
ptrace_untrace(child);
}
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment