The Pedigree Project 0.1
tracee.c
1#define _GNU_SOURCE
2#include <fcntl.h>
3#include <stdlib.h>
4#include <string.h>
5#include <unistd.h>
6
7#include "contract.h"
8#include <sys/prctl.h>
9
10_Thread_local volatile uint64_t tc_image_tls = TC_IMAGE_TLS;
11static volatile sig_atomic_t handler_report = -1;
12
13static void caught(int signal, siginfo_t* info, void* context) {
14 (void)context;
15 int saved = errno;
16 struct tc_message message = {.kind = 'H',
17 .signal = signal,
18 .code = info->si_code,
19 .pid = info->si_pid,
20 .uid = info->si_uid};
21 ssize_t ignored = write(handler_report, &message, sizeof(message));
22 (void)ignored;
23 errno = saved;
24}
25
26struct alive {
27 atomic_uint ready, finish;
28};
29
30static void* remain_alive(void* argument) {
31 struct alive* state = argument;
32 atomic_store(&state->ready, 1);
33 tc_atomic_wait(&state->finish, 1);
34 return NULL;
35}
36
37static long multithreaded_enrollment(int* error) {
38 struct alive state = {0};
39 pthread_t thread;
40 int result = pthread_create(&thread, NULL, remain_alive, &state);
41 if (result) {
42 *error = result;
43 return -2;
44 }
45 long enrolled = -2;
46 if (!tc_atomic_wait(&state.ready, 1)) {
47 errno = 0;
48 enrolled = ptrace(PTRACE_TRACEME, (pid_t)0, (void*)0, (void*)0);
49 *error = errno;
50 }
51 atomic_store(&state.finish, 1);
52 if (pthread_join(thread, NULL))
53 enrolled = -2;
54 return enrolled;
55}
56
57void tc_tracee(int command, int report, int traced) {
58 alarm(40);
59 sigset_t empty;
60 sigemptyset(&empty);
61 struct sigaction action = {.sa_sigaction = caught, .sa_flags = SA_SIGINFO};
62 sigemptyset(&action.sa_mask);
63 handler_report = report;
64 if (sigprocmask(SIG_SETMASK, &empty, NULL) || sigaction(SIGUSR1, &action, NULL) ||
65 sigaction(SIGUSR2, &action, NULL))
66 _exit(122);
67 errno = 0;
68 struct tc_message ready = {.kind = 'R'};
69 if (traced) {
70 ready.result = ptrace(PTRACE_TRACEME, (pid_t)0, (void*)0, (void*)0);
71 ready.error = errno;
72 }
73 if (tc_write(report, &ready, sizeof(ready)) || ready.result)
74 _exit(123);
75 for (;;) {
76 struct tc_command request;
77 if (tc_read(command, &request, sizeof(request)))
78 _exit(124);
79 struct tc_message reply = {.kind = request.operation, .signal = request.signal};
80 errno = 0;
81 switch (request.operation) {
82 case 'S':
83 reply.kind = 'A';
84 reply.result = syscall(SYS_tgkill, getpid(), syscall(SYS_gettid), request.signal);
85 break;
86 case 'I':
87 case 'F':
88 action.sa_handler = request.operation == 'I' ? SIG_IGN : SIG_DFL;
89 action.sa_flags = 0;
90 reply.result = sigaction(request.signal, &action, NULL);
91 break;
92 case 'B':
93 case 'U': {
94 sigset_t mask;
95 sigemptyset(&mask);
96 sigaddset(&mask, request.signal);
97 reply.result = sigprocmask(request.operation == 'B' ? SIG_BLOCK : SIG_UNBLOCK, &mask, NULL);
98 break;
99 }
100 case 'D':
101 reply.result = prctl(PR_SET_DUMPABLE, 0UL, 0UL, 0UL, 0UL);
102 break;
103 case 'T':
104 reply.result =
105 ptrace(PTRACE_TRACEME, (pid_t)424242, (void*)UINTPTR_MAX, (void*)UINTPTR_MAX);
106 break;
107 case 'M':
108 reply.result = multithreaded_enrollment(&reply.error);
109 break;
110 case 'Q': {
111 sigset_t mask;
112 sigemptyset(&mask);
113 sigaddset(&mask, request.signal);
114 struct timespec immediate = {0, 0};
115 for (unsigned i = 0; i < 16; ++i) {
116 siginfo_t info;
117 int signal = sigtimedwait(&mask, &info, &immediate);
118 if (signal != request.signal || info.si_code != SI_QUEUE ||
119 info.si_value.sival_int != (int)i + 100) {
120 reply.result = -1;
121 break;
122 }
123 ++reply.result;
124 }
125 break;
126 }
127 case 'X':
128 case 'J': {
129 tc_image_tls ^= UINT64_C(0xffffffffffffffff);
130 char descriptor[24];
131 snprintf(descriptor, sizeof(descriptor), "%d", report);
132 if (fcntl(report, F_SETFD, 0))
133 _exit(125);
134 if (request.operation == 'X')
135 execl(TC_EXECUTABLE, TC_EXECUTABLE, "--exec-child", descriptor, (char*)NULL);
136 else
137 execl(TC_ENTRY_EXECUTABLE, TC_ENTRY_EXECUTABLE, descriptor, (char*)NULL);
138 reply.result = -1;
139 break;
140 }
141 case 'P':
142 case 'E':
143 break;
144 default:
145 _exit(126);
146 }
147 if (request.operation != 'M')
148 reply.error = errno;
149 if (tc_write(report, &reply, sizeof(reply)))
150 _exit(127);
151 if (request.operation == 'E')
152 _exit(TC_EXIT_CODE);
153 }
154}
155
156int tc_exec_child(int report) {
157 alarm(10);
158 struct tc_message message = {.kind = 'X', .pid = getpid(), .value = tc_image_tls};
159 if (tc_image_tls != TC_IMAGE_TLS || tc_write(report, &message, sizeof(message)))
160 return 1;
161 return 0;
162}
Definition tracee.c:26