diff options
author | Tejun Heo <tj@kernel.org> | 2011-03-23 05:37:01 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2011-03-23 05:37:01 -0400 |
commit | 244056f9dbbc6dc4126a301c745fa3dd67d8af3c (patch) | |
tree | dced7105f53b5d5ed34e170d77f5c6ab0172542a /kernel/signal.c | |
parent | ceb6bd67f9b9db765e1c29405f26e8460391badd (diff) |
job control: Don't send duplicate job control stop notification while ptraced
Just as group_exit_code shouldn't be generated when a PTRACE_CONT'd
task re-enters job control stop, notifiction for the event should be
suppressed too. The logic is the same as the group_exit_code
generation suppression in do_signal_stop(), if SIGNAL_STOP_STOPPED is
already set, the task is re-entering job control stop without
intervening SIGCONT and the notifications should be suppressed.
Test case follows.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static pid_t tracee, tracer;
static const char *pid_who(pid_t pid)
{
return pid == tracee ? "tracee" : (pid == tracer ? "tracer" : "mommy ");
}
static void sigchld_sigaction(int signo, siginfo_t *si, void *ucxt)
{
printf("%s: SIG status=%02d code=%02d (%s)\n",
pid_who(getpid()), si->si_status, si->si_code,
pid_who(si->si_pid));
}
int main(void)
{
const struct sigaction chld_sa = { .sa_sigaction = sigchld_sigaction,
.sa_flags = SA_SIGINFO|SA_RESTART };
siginfo_t si;
sigaction(SIGCHLD, &chld_sa, NULL);
tracee = fork();
if (!tracee) {
tracee = getpid();
while (1)
pause();
}
kill(tracee, SIGSTOP);
waitid(P_PID, tracee, &si, WSTOPPED);
tracer = fork();
if (!tracer) {
tracer = getpid();
ptrace(PTRACE_ATTACH, tracee, NULL, NULL);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
waitid(P_PID, tracee, &si, WSTOPPED);
printf("tracer: detaching\n");
ptrace(PTRACE_DETACH, tracee, NULL, NULL);
return 0;
}
while (1)
pause();
return 0;
}
Before the patch, the parent gets the second notification for the
tracee after the tracer detaches. si_status is zero because
group_exit_code is not set by the group stop completion which
triggered this notification.
mommy : SIG status=19 code=05 (tracee)
tracer: SIG status=00 code=05 (tracee)
tracer: SIG status=19 code=04 (tracee)
tracer: SIG status=00 code=05 (tracee)
tracer: detaching
mommy : SIG status=00 code=05 (tracee)
mommy : SIG status=00 code=01 (tracer)
^C
After the patch, the duplicate notification is gone.
mommy : SIG status=19 code=05 (tracee)
tracer: SIG status=00 code=05 (tracee)
tracer: SIG status=19 code=04 (tracee)
tracer: SIG status=00 code=05 (tracee)
tracer: detaching
mommy : SIG status=00 code=01 (tracer)
^C
Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Oleg Nesterov <oleg@redhat.com>
Diffstat (limited to 'kernel/signal.c')
-rw-r--r-- | kernel/signal.c | 10 |
1 files changed, 9 insertions, 1 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index f65403da4101..f799a054f292 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -268,6 +268,10 @@ void task_clear_group_stop_pending(struct task_struct *task) | |||
268 | * | 268 | * |
269 | * CONTEXT: | 269 | * CONTEXT: |
270 | * Must be called with @task->sighand->siglock held. | 270 | * Must be called with @task->sighand->siglock held. |
271 | * | ||
272 | * RETURNS: | ||
273 | * %true if group stop completion should be notified to the parent, %false | ||
274 | * otherwise. | ||
271 | */ | 275 | */ |
272 | static bool task_participate_group_stop(struct task_struct *task) | 276 | static bool task_participate_group_stop(struct task_struct *task) |
273 | { | 277 | { |
@@ -284,7 +288,11 @@ static bool task_participate_group_stop(struct task_struct *task) | |||
284 | if (!WARN_ON_ONCE(sig->group_stop_count == 0)) | 288 | if (!WARN_ON_ONCE(sig->group_stop_count == 0)) |
285 | sig->group_stop_count--; | 289 | sig->group_stop_count--; |
286 | 290 | ||
287 | if (!sig->group_stop_count) { | 291 | /* |
292 | * Tell the caller to notify completion iff we are entering into a | ||
293 | * fresh group stop. Read comment in do_signal_stop() for details. | ||
294 | */ | ||
295 | if (!sig->group_stop_count && !(sig->flags & SIGNAL_STOP_STOPPED)) { | ||
288 | sig->flags = SIGNAL_STOP_STOPPED; | 296 | sig->flags = SIGNAL_STOP_STOPPED; |
289 | return true; | 297 | return true; |
290 | } | 298 | } |