Skip to content

Instantly share code, notes, and snippets.

@rocallahan
Created August 3, 2016 23:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rocallahan/1344f7d01183c233d08a2c6b93413068 to your computer and use it in GitHub Desktop.
Save rocallahan/1344f7d01183c233d08a2c6b93413068 to your computer and use it in GitHub Desktop.
Standalone testcase for PTRACE_EVENT_EXIT bug (reduced)
/* 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