Skip to content

Instantly share code, notes, and snippets.

@michalbednarski
Created December 23, 2019 18:38
Show Gist options
  • Save michalbednarski/438b058f77f43dba78aa8d2ddd08f629 to your computer and use it in GitHub Desktop.
Save michalbednarski/438b058f77f43dba78aa8d2ddd08f629 to your computer and use it in GitHub Desktop.
#define _DEFAULT_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <linux/elf.h> /* NT_PRSTATUS */
#define DONT_CALL_PTRACE_SETOPTIONS ((int)-1)
#define TESTEDOPS_JUST_CONT 1
#define TESTEDOPS_JUST_SYSCALL 2
#define TESTEDOPS_MOD_REGS 3
#define TESTEDOPS_MOD_REGS_AND_MEM 4
static const char *testpath_b1 = "b";
static const char *testpath_b2 = "B";
struct test_options {
int ptrace_options;
int tested_ops;
};
void run_test_tracee(struct test_options *options) {
if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
perror("ptrace(PTRACE_TRACEME)");
return;
}
kill(getpid(), SIGTRAP);
int fd1 = openat(AT_FDCWD, "a", O_WRONLY | O_CREAT, 0600);
int errno1 = errno;
int fd2 = openat(AT_FDCWD, "A", O_WRONLY | O_CREAT, 0600);
int errno2 = errno;
printf("Tracee opened fd1=%d fd2=%d errno1=%d errno2=%d\n", fd1, fd2, errno1, errno2);
if (fd1 >= 0) close(fd1);
if (fd2 >= 0) close(fd2);
}
void verbose_wait4(pid_t pid, const char *note) {
int wstatus = 0;
int result = wait4(pid, &wstatus, 0, NULL);
if (result < 0) {
perror("wait4");
}
printf("wstatus=0x%x for %s\n", wstatus, note);
}
void transfer_regs(int op, pid_t pid, struct user_regs_struct *regs) {
struct iovec iov;
iov.iov_base = regs;
iov.iov_len = sizeof(*regs);
int status = ptrace(op, pid, NT_PRSTATUS, &iov);
if (status < 0) {
if (op == PTRACE_GETREGSET) {
perror("PTRACE_GETREGSET");
}
else if (op == PTRACE_SETREGSET) {
perror("PTRACE_SETREGSET");
}
else {
perror("PTRACE_???");
}
}
}
void run_test_tracer(struct test_options *options) {
pid_t forked = fork();
if (forked == 0) {
run_test_tracee(options);
_exit(0);
} else if (forked < 0) {
perror("fork 2");
return;
}
verbose_wait4(forked, "initial SIGTRAP");
if (options->ptrace_options != DONT_CALL_PTRACE_SETOPTIONS) {
int opt_status = ptrace(PTRACE_SETOPTIONS, forked, NULL, options->ptrace_options);
if (opt_status != 0) {
perror("PTRACE_SETOPTIONS");
}
}
if (options->tested_ops != TESTEDOPS_JUST_CONT) {
ptrace(PTRACE_SYSCALL, forked, 0, 0); verbose_wait4(forked, "sysenter 1");
// Now at sysenter open 1
struct user_regs_struct regs = {};
transfer_regs(PTRACE_GETREGSET, forked, &regs);
printf("x0=0x%lx x1=0x%lx x7=0x%lx x8=0x%lx\n", regs.regs[0], regs.regs[1], regs.regs[7], regs.regs[8]);
if (options->tested_ops == TESTEDOPS_MOD_REGS) {
regs.regs[1] = (uint64_t) testpath_b1;
transfer_regs(PTRACE_SETREGSET, forked, &regs);
}
if (options->tested_ops == TESTEDOPS_MOD_REGS_AND_MEM) {
ptrace(PTRACE_POKEDATA, forked, regs.sp - 8, 67);
regs.regs[1] = regs.sp - 8;
transfer_regs(PTRACE_SETREGSET, forked, &regs);
}
ptrace(PTRACE_SYSCALL, forked, 0, 0); verbose_wait4(forked, "sysexit 1");
// Now at sysexit open 1
transfer_regs(PTRACE_GETREGSET, forked, &regs);
printf("x0=0x%lx x1=0x%lx x7=0x%lx x8=0x%lx\n", regs.regs[0], regs.regs[1], regs.regs[7], regs.regs[8]);
ptrace(PTRACE_SYSCALL, forked, 0, 0); verbose_wait4(forked, "sysenter 2");
// Now at sysenter open 2
transfer_regs(PTRACE_GETREGSET, forked, &regs);
printf("x0=0x%lx x1=0x%lx x7=0x%lx x8=0x%lx\n", regs.regs[0], regs.regs[1], regs.regs[7], regs.regs[8]);
if (options->tested_ops == TESTEDOPS_MOD_REGS) {
regs.regs[1] = (uint64_t) testpath_b2;
transfer_regs(PTRACE_SETREGSET, forked, &regs);
}
if (options->tested_ops == TESTEDOPS_MOD_REGS_AND_MEM) {
ptrace(PTRACE_POKEDATA, forked, regs.sp - 8, 99);
regs.regs[1] = regs.sp - 8;
transfer_regs(PTRACE_SETREGSET, forked, &regs);
}
ptrace(PTRACE_SYSCALL, forked, 0, 0); verbose_wait4(forked, "sysexit 2");
// Now at sysexit open 2
transfer_regs(PTRACE_GETREGSET, forked, &regs);
printf("x0=0x%lx x1=0x%lx x7=0x%lx x8=0x%lx\n", regs.regs[0], regs.regs[1], regs.regs[7], regs.regs[8]);
}
ptrace(PTRACE_CONT, forked, 0, 0);
verbose_wait4(forked, "exit after CONT");
}
void run_test(struct test_options *options) {
char testdir[20] = "testcase_XXXXXX";
if (mkdtemp(testdir) == NULL) {
perror("mkdtemp");
return;
}
printf("Starting test ptrace_options=0x%x tested_ops=%d testdir=\"%s\"\n", options->ptrace_options, options->tested_ops, testdir);
pid_t forked = fork();
if (forked == 0) {
if (chdir(testdir) < 0) {
perror("chdir(temp)");
return;
}
run_test_tracer(options);
_exit(0);
} else if (forked < 0) {
perror("fork 1");
return;
}
int tracer_wstatus = -1;
wait4(forked, &tracer_wstatus, 0, NULL);
char cmd[100];
printf("ls -l result:\n");
snprintf(cmd, 100, "ls -l %s", testdir);
system(cmd);
printf("End test, tracer_wstatus=0x%x\n\n", tracer_wstatus);
snprintf(cmd, 100, "rm -rf %s", testdir);
system(cmd);
}
void combinations1(struct test_options *options) {
options->tested_ops = TESTEDOPS_JUST_CONT;
run_test(options);
options->tested_ops = TESTEDOPS_JUST_SYSCALL;
run_test(options);
options->tested_ops = TESTEDOPS_MOD_REGS;
run_test(options);
options->tested_ops = TESTEDOPS_MOD_REGS_AND_MEM;
run_test(options);
}
void combinations2(struct test_options *options) {
options->ptrace_options = DONT_CALL_PTRACE_SETOPTIONS;
combinations1(options);
options->ptrace_options = -2;
combinations1(options);
options->ptrace_options = 0;
combinations1(options);
options->ptrace_options = PTRACE_O_TRACESYSGOOD;
combinations1(options);
options->ptrace_options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXIT;
combinations1(options);
options->ptrace_options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXIT | PTRACE_O_TRACESECCOMP;
combinations1(options);
}
int main(int argc, char **argv) {
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
const char *home = getenv("HOME");
if (home == NULL) {
printf("HOME is not set\n");
return 1;
}
if (chdir(home) < 0) {
perror("chdir(HOME)");
return 1;
}
struct test_options options = {};
combinations2(&options);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment