/* gcc -lpthread -g -o test test.c */ | |
#include <assert.h> | |
#include <errno.h> | |
#include <linux/filter.h> | |
#include <linux/seccomp.h> | |
#include <pthread.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/prctl.h> | |
#include <sys/ptrace.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <unistd.h> | |
static struct timespec ts_short = {0, 10 * 1000000}; | |
static struct timespec ts_long = {0, 100 * 1000000}; | |
static void *do_thread(__attribute__((unused)) void *p) { | |
nanosleep(&ts_short, NULL); | |
exit(0); | |
return NULL; | |
} | |
/* Trivial filter that just generates a ptrace trap for every syscall */ | |
static struct sock_filter filter = | |
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE | SECCOMP_RET_DATA); | |
static struct sock_fprog prog = {1, &filter}; | |
static void do_tracee(void) { | |
pthread_t thread; | |
/* Wait for ptracer to attach and continue us */ | |
kill(getpid(), SIGSTOP); | |
/* Spawn thread to wait for a short time and then exit_group */ | |
pthread_create(&thread, NULL, do_thread, NULL); | |
/* Install seccomp filter */ | |
assert(0 == prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); | |
assert(0 == prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, (long)&prog, 0, 0)); | |
/* Generate a seccomp event */ | |
pause(); | |
} | |
int main(void) { | |
pid_t child; | |
int status; | |
int got_ptrace_exit_for_child = 0; | |
child = fork(); | |
if (!child) { | |
do_tracee(); | |
/* unreached */ | |
return 0; | |
} | |
/* Start tracing child. Trace thread-group-leader only. */ | |
assert(0 == ptrace(PTRACE_SEIZE, child, NULL, | |
(void *)(PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXIT | | |
PTRACE_O_TRACESECCOMP))); | |
assert(child == waitpid(child, &status, 0)); | |
assert(status == ((SIGSTOP << 8) | 0x7f)); | |
assert(0 == ptrace(PTRACE_CONT, child, NULL, NULL)); | |
while (1) { | |
printf("Waiting for %d ...\n", child); | |
assert(child == waitpid(child, &status, 0)); | |
if (WIFEXITED(status)) { | |
printf("%d exiting with status %d\n", child, WEXITSTATUS(status)); | |
printf("FAILED: did not see PTRACE_EVENT_EXIT before task %d exited\n", | |
child); | |
return 1; | |
} | |
if ((status >> 8) == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) { | |
printf("%d got PTRACE_EVENT_EXIT\n", child); | |
printf("SUCCESS: saw PTRACE_EVENT_EXIT before task %d exited\n", child); | |
return 0; | |
} | |
if ((status >> 8) == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) { | |
/* Ensure traced task is stopped in the seccomp trap while the untraced | |
task does an exit_group. */ | |
nanosleep(&ts_long, NULL); | |
continue; | |
} | |
printf("Issuing PTRACE_CONT for %d\n", child); | |
if (ptrace(PTRACE_CONT, child, NULL, NULL) < 0) { | |
printf("FAILED: %s trying to PTRACE_CONT task %d (no PTRACE_EVENT_EXIT " | |
"seen)\n", | |
strerror(errno), child); | |
return 1; | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment