aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2004-10-25 18:02:35 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-10-25 18:02:35 -0700
commit403f291258456ffc3beb40bc92c0006569e20bc0 (patch)
tree8ce911c4e46cc7fd2bc292762d3f961374fbc9db /kernel
parentc257386fbe7febfd2827967e3368e13212672979 (diff)
downloadhistory-403f291258456ffc3beb40bc92c0006569e20bc0.tar.gz
[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: Roland McGrath <roland@redhat.com> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/ptrace.c14
1 files changed, 14 insertions, 0 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index d78dafa86fc5a0..60801c692810e6 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -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);
}
}