diff options
author | Oleg Nesterov <oleg@redhat.com> | 2011-07-21 11:06:53 -0400 |
---|---|---|
committer | Oleg Nesterov <oleg@redhat.com> | 2011-07-21 11:06:53 -0400 |
commit | 8a35241803eeb0e9fd3fe27835d6b2775c73b641 (patch) | |
tree | 09f15db936084e239279844bcb6db6608e2bb06f /kernel/signal.c | |
parent | f701e5b73a1a79ea62ffd45d9e2bed4c7d5c1fd2 (diff) |
ptrace: fix ptrace_signal() && STOP_DEQUEUED interaction
Simple test-case,
int main(void)
{
int pid, status;
pid = fork();
if (!pid) {
pause();
assert(0);
return 0x23;
}
assert(ptrace(PTRACE_ATTACH, pid, 0,0) == 0);
assert(wait(&status) == pid);
assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
kill(pid, SIGCONT); // <--- also clears STOP_DEQUEUD
assert(ptrace(PTRACE_CONT, pid, 0,0) == 0);
assert(wait(&status) == pid);
assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGCONT);
assert(ptrace(PTRACE_CONT, pid, 0, SIGSTOP) == 0);
assert(wait(&status) == pid);
assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
kill(pid, SIGKILL);
return 0;
}
Without the patch it hangs. After the patch SIGSTOP "injected" by the
tracer is not ignored and stops the tracee.
Note also that if this test-case uses, say, SIGWINCH instead of SIGCONT,
everything works without the patch. This can't be right, and this is
confusing.
The problem is that SIGSTOP (or any other sig_kernel_stop() signal) has
no effect without JOBCTL_STOP_DEQUEUED. This means it is simply ignored
after PTRACE_CONT unless JOBCTL_STOP_DEQUEUED was set "by accident", say
it wasn't cleared after initial SIGSTOP sent by PTRACE_ATTACH.
At first glance we could change ptrace_signal() to add STOP_DEQUEUED
after return from ptrace_stop(), but this is not right in case when the
tracer does not change the reported SIGSTOP and SIGCONT comes in between.
This is even more wrong with PT_SEIZED, SIGCONT adds JOBCTL_TRAP_NOTIFY
which will be "lost" during the TRAP_STOP | TRAP_NOTIFY report.
So lets add STOP_DEQUEUED _before_ we report the signal. It has no effect
unless sig_kernel_stop() == T after the tracer resumes us, and in the
latter case the pending STOP_DEQUEUED means no SIGCONT in between, we
should stop.
Note also that if SIGCONT was sent, PT_SEIZED tracee will correctly
report PTRACE_EVENT_STOP/SIGTRAP and thus the tracer can notice the fact
SIGSTOP was cancelled.
Also, move the current->ptrace check from ptrace_signal() to its caller,
get_signal_to_deliver(), this looks more natural.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'kernel/signal.c')
-rw-r--r-- | kernel/signal.c | 17 |
1 files changed, 11 insertions, 6 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index 0a1bf2c8bdcd..c34f8f899b76 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -2084,12 +2084,17 @@ static void do_jobctl_trap(void) | |||
2084 | static int ptrace_signal(int signr, siginfo_t *info, | 2084 | static int ptrace_signal(int signr, siginfo_t *info, |
2085 | struct pt_regs *regs, void *cookie) | 2085 | struct pt_regs *regs, void *cookie) |
2086 | { | 2086 | { |
2087 | if (!current->ptrace) | ||
2088 | return signr; | ||
2089 | |||
2090 | ptrace_signal_deliver(regs, cookie); | 2087 | ptrace_signal_deliver(regs, cookie); |
2091 | 2088 | /* | |
2092 | /* Let the debugger run. */ | 2089 | * We do not check sig_kernel_stop(signr) but set this marker |
2090 | * unconditionally because we do not know whether debugger will | ||
2091 | * change signr. This flag has no meaning unless we are going | ||
2092 | * to stop after return from ptrace_stop(). In this case it will | ||
2093 | * be checked in do_signal_stop(), we should only stop if it was | ||
2094 | * not cleared by SIGCONT while we were sleeping. See also the | ||
2095 | * comment in dequeue_signal(). | ||
2096 | */ | ||
2097 | current->jobctl |= JOBCTL_STOP_DEQUEUED; | ||
2093 | ptrace_stop(signr, CLD_TRAPPED, 0, info); | 2098 | ptrace_stop(signr, CLD_TRAPPED, 0, info); |
2094 | 2099 | ||
2095 | /* We're back. Did the debugger cancel the sig? */ | 2100 | /* We're back. Did the debugger cancel the sig? */ |
@@ -2193,7 +2198,7 @@ relock: | |||
2193 | if (!signr) | 2198 | if (!signr) |
2194 | break; /* will return 0 */ | 2199 | break; /* will return 0 */ |
2195 | 2200 | ||
2196 | if (signr != SIGKILL) { | 2201 | if (unlikely(current->ptrace) && signr != SIGKILL) { |
2197 | signr = ptrace_signal(signr, info, | 2202 | signr = ptrace_signal(signr, info, |
2198 | regs, cookie); | 2203 | regs, cookie); |
2199 | if (!signr) | 2204 | if (!signr) |