Created
August 3, 2016 23:30
-
-
Save rocallahan/1344f7d01183c233d08a2c6b93413068 to your computer and use it in GitHub Desktop.
Standalone testcase for PTRACE_EVENT_EXIT bug (reduced)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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); | |
} | |
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