Commit 403f2912 authored by Roland McGrath's avatar Roland McGrath Committed by Linus Torvalds

[PATCH] Wake up signalled tasks when exiting ptrace

In general it is not safe to do any non-ptrace wakeup of a thread in
TASK_TRACED, because the waking thread could race with a ptrace call
that could be doing things like mucking directly with its kernel stack. 

AFAIK noone has established that whatever clobberation ptrace can do to
a running thread is safe even if it will never return to user mode, so
we can't allow this even for SIGKILL.

What we _can_ safely do is make a thread switching out of TASK_TRACED
resume rather than sitting in TASK_STOPPED if it has a pending SIGKILL
or SIGCONT.  The following patch does this.

This should be sufficient for the shutdown case.  When killing all
processes, if the tracer gets killed first, the tracee goes into
TASK_STOPPED and will be woken and killed by the SIGKILL (same as
before).  If the tracee gets killed first, it gets a pending SIGKILL and
doesn't wake up immediately--but, now, when the tracer gets killed, the
tracee will then wake up to die. 

This will also fix the (same) situations that can arise now where you
have used gdb (or whatever ptrace caller), killed -9 the gdb and the
process being debugged, but still have to kill -CONT the process before
it goes away (now it should just go away either the first time or when
you kill gdb). 
Signed-off-by: default avatarRoland McGrath <roland@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent c257386f
......@@ -38,6 +38,12 @@ void __ptrace_link(task_t *child, task_t *new_parent)
SET_LINKS(child);
}
static inline int pending_resume_signal(struct sigpending *pending)
{
#define M(sig) (1UL << ((sig)-1))
return sigtestsetmask(&pending->signal, M(SIGCONT) | M(SIGKILL));
}
/*
* unptrace a task: move it back to its original parent and
* remove it from the ptrace list.
......@@ -61,8 +67,16 @@ void __ptrace_unlink(task_t *child)
* 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);
}
}
......
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