8static int kill_stopped(
void) {
9 int failed = 0, status;
10 struct tc_child child = TC_CHILD_INIT;
11 struct user_regs_struct registers;
13 CHECK(tc_spawn(&child, 1) == 0 && tc_stop(&child, SIGUSR1) == 0);
14 pid_t pid = child.pid;
15 CHECK(kill(pid, SIGKILL) == 0);
16 CHECK(tc_wait(pid, &status, 10000) == 0);
17 if (WIFEXITED(status) || WIFSIGNALED(status))
19 CHECK(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL);
21 CHECK(wait4(pid, NULL, WNOHANG, NULL) == -1 && errno == ECHILD);
23 CHECK(ptrace(PTRACE_GETREGS, pid, (
void*)0, (
void*)®isters) == -1 && errno == ESRCH);
25 CHECK(ptrace(PTRACE_GETSIGINFO, pid, (
void*)0, (
void*)&info) == -1 && errno == ESRCH);
31static int tracee_exec(
int raw_entry) {
32 int failed = 0, status;
33 struct tc_child child = TC_CHILD_INIT;
34 struct user_regs_struct old, entry, repeated;
38 CHECK(tc_spawn(&child, 1) == 0 && tc_stop(&child, SIGUSR1) == 0);
39 CHECK(ptrace(PTRACE_GETREGS, child.pid, (
void*)0, (
void*)&old) == 0);
40 CHECK(tc_resume(child.pid, PTRACE_CONT, 0) == 0 && tc_receive(child.report,
'A', NULL) == 0);
41 CHECK(tc_send(&child, raw_entry ?
'J' :
'X', 0) == 0);
42 CHECK(tc_wait_stop(&child, SIGTRAP) == 0);
43 CHECK(ptrace(PTRACE_GETREGS, child.pid, (
void*)0, (
void*)&entry) == 0);
44 CHECK(ptrace(PTRACE_GETREGS, child.pid, (
void*)0, (
void*)&repeated) == 0);
45 CHECK(memcmp(&entry, &repeated,
sizeof(entry)) == 0);
46 CHECK(entry.rip && entry.rsp && entry.rip != old.rip);
47 CHECK(ptrace(PTRACE_GETSIGINFO, child.pid, (
void*)0, (
void*)&info) == 0);
48 CHECK(info.si_signo == SIGTRAP && !info.si_errno);
49 CHECK(tc_resume(child.pid, PTRACE_CONT, 0) == 0);
51 CHECK(tc_read(child.report, &actual,
sizeof(actual)) == 0);
52 CHECK(actual.magic == TC_ENTRY_MAGIC_VALUE && actual.fs_result == 0);
53 unsigned long stopped[27], resumed[27];
54 memcpy(stopped, &entry,
sizeof(stopped));
55 memcpy(resumed, &actual.registers,
sizeof(resumed));
56 for (
size_t i = 0; i < 27; ++i) {
58 if (i == TC_ORIG_RAX / 8 || i == TC_REG_GS_BASE / 8)
60 if (stopped[i] != resumed[i])
61 fprintf(stderr,
"exec entry register[%zu] stop=%#lx resumed=%#lx\n", i, stopped[i],
63 CHECK(stopped[i] == resumed[i]);
65 CHECK(entry.orig_rax == SYS_execve);
67 CHECK(tc_receive(child.report,
'X', &
message) == 0);
70 CHECK(tc_wait(child.pid, &status, 10000) == 0);
71 if (WIFEXITED(status) || WIFSIGNALED(status))
73 CHECK(WIFEXITED(status) && WEXITSTATUS(status) == 0);
79int tc_exec_tracer(pid_t pid,
int command,
int report) {
81 struct tc_child child = {pid, command, report};
82 struct user_regs_struct registers;
85 CHECK(ptrace(PTRACE_GETREGS, pid, (
void*)0, (
void*)®isters) == 0);
86 CHECK(registers.rip && registers.rsp);
87 CHECK(ptrace(PTRACE_GETSIGINFO, pid, (
void*)0, (
void*)&info) == 0);
88 CHECK(tc_signal_info(&info, SIGUSR1, SI_TKILL, pid, getuid()) == 0);
89 CHECK(tc_resume(pid, PTRACE_CONT, 0) == 0 && tc_receive(report,
'A', NULL) == 0);
90 CHECK(tc_finish(&child) == 0);
96static int tracer_exec(
void) {
98 struct tc_child child = TC_CHILD_INIT;
99 char pid[24], command[24], report[24];
100 CHECK(tc_spawn(&child, 1) == 0 && tc_stop(&child, SIGUSR1) == 0);
101 CHECK(fcntl(child.command, F_SETFD, 0) == 0 && fcntl(child.report, F_SETFD, 0) == 0);
102 snprintf(pid,
sizeof(pid),
"%d", child.pid);
103 snprintf(command,
sizeof(command),
"%d", child.command);
104 snprintf(report,
sizeof(report),
"%d", child.report);
105 execl(TC_EXECUTABLE, TC_EXECUTABLE,
"--exec-tracer", pid, command, report, (
char*)NULL);
112int tc_lifecycle(
void) {
113 return kill_stopped() || tracee_exec(1) || tracee_exec(0) || tracer_exec();