Skip to content

Instantly share code, notes, and snippets.

@rocallahan
Created August 2, 2016 06:09
Show Gist options
  • Save rocallahan/b09b1de28a32918cb27d4ad68421678d to your computer and use it in GitHub Desktop.
Save rocallahan/b09b1de28a32918cb27d4ad68421678d to your computer and use it in GitHub Desktop.
Standalone testcase for PTRACE_EVENT_EXIT bug
#define _GNU_SOURCE
#include <sched.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/fcntl.h>
#include <sys/user.h>
#include <sys/ptrace.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
#include <sys/prctl.h>
#include <linux/bpf_common.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
static void* do_thread(__attribute__((unused)) void* p) {
struct timespec ts_long = { 1000, 0 };
sigset_t sigs;
sigemptyset(&sigs);
sigaddset(&sigs, SIGINT);
pthread_sigmask(SIG_BLOCK, &sigs, NULL);
kill(getpid(), SIGINT);
nanosleep(&ts_long, NULL);
return NULL;
}
static void handler(__attribute__((unused)) int sig) {
}
static void dump_stack(pid_t tid) {
int fd;
char buf[0x10000];
ssize_t len;
sprintf(buf, "/proc/%d/stack", tid);
fd = open(buf, O_RDONLY);
len = read(fd, buf, sizeof(buf));
write(1, buf, len);
}
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;
struct timespec ts_short = { 0, 1000000 };
struct timespec ts_long = { 1000, 0 };
signal(SIGINT, handler);
kill(getpid(), SIGSTOP);
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));
pthread_create(&thread, NULL, do_thread, NULL);
assert(nanosleep(&ts_long, NULL) < 0 && errno == EINTR);
exit(0);
}
int main(void) {
pid_t child;
int status;
cpu_set_t cpus;
pid_t child_thread;
pid_t schedule_pid;
int got_ptrace_exit_for_child_thread = 0;
CPU_ZERO(&cpus);
CPU_SET(0, &cpus);
assert(0 == sched_setaffinity(0, sizeof(cpus), &cpus));
child = fork();
if (!child) {
do_tracee();
/* unreached */
return 0;
}
assert(0 == ptrace(PTRACE_SEIZE, child, NULL, (void*)(PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACECLONE | 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));
schedule_pid = child;
while (1) {
long sig;
struct user_regs_struct regs;
pid_t pid = schedule_pid;
printf("Waiting for %d ...\n", pid);
assert(schedule_pid == waitpid(schedule_pid, &status, 0));
if (WIFEXITED(status)) {
printf("%d exiting with status %d\n", pid, WEXITSTATUS(status));
if (pid == child_thread) {
printf("FAILED: did not see PTRACE_EVENT_EXIT before thread %d exited\n", pid);
return 1;
}
}
ptrace(PTRACE_GETREGS, pid, NULL, &regs);
printf("Pid %d got status 0x%x, rax=0x%llx, orig_rax=0x%llx\n", pid, status, (long long)regs.rax, (long long)regs.orig_rax);
if ((status >> 8) == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) {
printf("%d got PTRACE_EVENT_EXIT\n", pid);
if (schedule_pid == child_thread) {
printf("SUCCESS: saw PTRACE_EVENT_EXIT before thread %d exited\n", pid);
return 0;
}
schedule_pid = schedule_pid == child ? child_thread : child;
}
dump_stack(pid);
sig = WIFSTOPPED(status) && WSTOPSIG(status) == SIGINT ? SIGINT : 0;
if ((status >> 8) == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) {
printf("Issuing PTRACE_SYSCALL for %d\n", pid);
assert(0 == ptrace(PTRACE_SYSCALL, pid, NULL, (void*)sig) || errno == ESRCH);
if (regs.orig_rax == SYS_nanosleep) {
schedule_pid = schedule_pid == child ? child_thread : child;
}
} else {
if ((status >> 8) == (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) {
long msg;
assert(0 == ptrace(PTRACE_GETEVENTMSG, pid, NULL, &msg));
child_thread = msg;
}
printf("Issuing PTRACE_CONT for %d\n", pid);
assert(0 == ptrace(PTRACE_CONT, pid, NULL, (void*)sig) || errno == ESRCH);
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment