I'm using PTRACE_SEIZE
to trace the execution of a child process, but I'm running into an issue where a non-group-stop PTRACE_EVENT_STOP
(signal == SIGTRAP
) is being emitted when the tracee receives a SIGCONT
. I can't seem to find any documentation that might indicate why this is happening. From what I can gather in the ptrace(2)
manpage, PTRACE_EVENT_STOP
only occurs when one of the following conditions are true:
- The tracer called
ptrace(PTRACE_INTERRUPT, ...)
- The tracee entered group stop (signal is
SIGSTOP
,SIGTSTP
,SIGTTIN
, orSIGTTOU
) - The tracee called
fork
/vfork
/clone
and the tracer automatically attached to the new process/thread
In the sample program below, I'm getting a PTRACE_EVENT_STOP
with signal SIGTRAP
, but it doesn't match any of those 3 conditions. When I run the program, I get the following output:
[Child] Raising SIGSTOP
Event for pid 29236: Tracee entered group-stop PTRACE_EVENT_STOP (signal: 19)
Event for pid 29236: Tracee entered non-group-stop PTRACE_EVENT_STOP (signal: 5)
Event for pid 29236: Tracee entered/exited syscall
Event for pid 29236: Tracee entered/exited syscall
Event for pid 29236: Tracee received signal (signal: 18)
Event for pid 29236: Tracee entered/exited syscall
[Child] Resumed from SIGSTOP
Event for pid 29236: Tracee entered/exited syscall
Event for pid 29236: Tracee entered/exited syscall
Event for pid 29236: Process exited
Any ideas as to what this particular PTRACE_EVENT_STOP
means?
Sample program:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <unistd.h>
static void handle_events(pid_t pid_spec)
{
while (1) {
int status;
pid_t pid = waitpid(pid_spec, &status, __WALL);
printf("Event for pid %d: ", pid);
if (WIFEXITED(status)) {
printf("Process exited\n");
break;
} else if (WIFSIGNALED(status)) {
printf("Process killed by signal\n");
break;
} else if (WIFSTOPPED(status)) {
int ptrace_event = status >> 16;
int signal = WSTOPSIG(status);
switch (ptrace_event) {
case PTRACE_EVENT_STOP:
if (signal == SIGSTOP || signal == SIGTSTP
|| signal == SIGTTIN || signal == SIGTTOU) {
printf("Tracee entered group-stop PTRACE_EVENT_STOP (signal: %d)\n", signal);
ptrace(PTRACE_LISTEN, pid, NULL, NULL);
} else {
printf("Tracee entered non-group-stop PTRACE_EVENT_STOP (signal: %d)\n", signal);
ptrace(PTRACE_SYSCALL, pid, NULL, 0);
}
break;
default:
if (signal == (SIGTRAP | 0x80)) {
printf("Tracee entered/exited syscall\n");
ptrace(PTRACE_SYSCALL, pid, NULL, 0);
} else {
printf("Tracee received signal (signal: %d)\n", signal);
ptrace(PTRACE_SYSCALL, pid, NULL, signal);
}
break;
}
}
}
}
int main()
{
pid_t pid = fork();
if (pid == 0) {
printf("[Child] Raising SIGSTOP\n");
// Allow parent to PTRACE_SEIZE
if (raise(SIGSTOP) != 0) {
_exit(255);
}
printf("[Child] Resumed from SIGSTOP\n");
_exit(0);
}
// Wait for stop
int status;
waitpid(pid, &status, WSTOPPED);
ptrace(PTRACE_SEIZE, pid, NULL, PTRACE_O_TRACESYSGOOD);
// Allow child to continue
kill(pid, SIGCONT);
handle_events(pid);
return EXIT_SUCCESS;
}
strace
seems to call PTRACE_SYSCALL
whenever it sees a non-group-stop PTRACE_EVENT_STOP
: