The Pedigree Project 0.1
ownership.c
1#define _GNU_SOURCE
2#include <string.h>
3#include <unistd.h>
4
5#include "contract.h"
6
7struct outsider {
8 pid_t child;
9 int peek_result, wait_result, status;
10 siginfo_t peek;
11 long result[3];
12 int error[3];
13 struct user_regs_struct registers;
14 siginfo_t info;
15};
16
17static void* inspect_sibling(void* argument) {
18 struct outsider* state = argument;
19 memset(&state->registers, 0xa5, sizeof(state->registers));
20 memset(&state->info, 0xa5, sizeof(state->info));
21 state->peek_result = waitid(P_PID, state->child, &state->peek, WSTOPPED | WNOWAIT);
22 state->wait_result = tc_wait(state->child, &state->status, 10000);
23 errno = 0;
24 state->result[0] = ptrace(PTRACE_GETREGS, state->child, (void*)0, (void*)&state->registers);
25 state->error[0] = errno;
26 errno = 0;
27 state->result[1] = ptrace(PTRACE_GETSIGINFO, state->child, (void*)0, (void*)&state->info);
28 state->error[1] = errno;
29 errno = 0;
30 state->result[2] = ptrace(PTRACE_CONT, state->child, (void*)0, (void*)0);
31 state->error[2] = errno;
32 return NULL;
33}
34
35static int sibling_denied(void) {
36 int failed = 0, started = 0;
37 struct tc_child child = TC_CHILD_INIT;
38 struct outsider state = {0};
39 struct user_regs_struct first, second;
40 siginfo_t peek;
41 pthread_t sibling;
42 CHECK(tc_spawn(&child, 1) == 0 && tc_send(&child, 'S', SIGUSR1) == 0);
43 CHECK(waitid(P_PID, child.pid, &peek, WSTOPPED | WNOWAIT) == 0);
44 CHECK(peek.si_pid == child.pid && peek.si_code == CLD_TRAPPED && peek.si_status == SIGUSR1);
45 CHECK(ptrace(PTRACE_GETREGS, child.pid, (void*)0, (void*)&first) == 0);
46 state.child = child.pid;
47 CHECK(pthread_create(&sibling, NULL, inspect_sibling, &state) == 0);
48 started = 1;
49 CHECK(pthread_join(sibling, NULL) == 0);
50 started = 0;
51 CHECK(state.peek_result == 0 && state.peek.si_pid == child.pid &&
52 state.peek.si_code == CLD_TRAPPED && state.peek.si_status == SIGUSR1);
53 CHECK(state.wait_result == 0 && WIFSTOPPED(state.status) && WSTOPSIG(state.status) == SIGUSR1);
54 for (size_t i = 0; i < 3; ++i)
55 CHECK(state.result[i] == -1 && state.error[i] == ESRCH);
56 const unsigned char* bytes = (const unsigned char*)&state.registers;
57 for (size_t i = 0; i < sizeof(state.registers); ++i)
58 CHECK(bytes[i] == 0xa5);
59 bytes = (const unsigned char*)&state.info;
60 for (size_t i = 0; i < sizeof(state.info); ++i)
61 CHECK(bytes[i] == 0xa5);
62 CHECK(ptrace(PTRACE_GETREGS, child.pid, (void*)0, (void*)&second) == 0);
63 CHECK(memcmp(&first, &second, sizeof(first)) == 0);
64 CHECK(waitid(P_PID, child.pid, &peek, WSTOPPED | WNOHANG) == 0 && !peek.si_pid);
65 CHECK(tc_resume(child.pid, PTRACE_CONT, 0) == 0 && tc_receive(child.report, 'A', NULL) == 0);
66 CHECK(tc_finish(&child) == 0);
67out:
68 if (started && pthread_join(sibling, NULL))
69 failed = 1;
70 tc_close(&child);
71 return failed;
72}
73
74struct creator {
75 struct tc_child child;
76 atomic_uint ready, finish;
77 int result;
78};
79
80static void* create_then_exit(void* argument) {
81 struct creator* state = argument;
82 state->result = tc_spawn(&state->child, 1) || tc_stop(&state->child, SIGUSR1);
83 atomic_store(&state->ready, 1);
84 if (!state->result && tc_atomic_wait(&state->finish, 1))
85 state->result = -1;
86 // The relation must retire with this task even though its Process stays alive.
87 return NULL;
88}
89
90static int tracer_task_exit(void) {
91 int failed = 0, started = 0;
92 struct creator state = {.child = TC_CHILD_INIT};
93 struct tc_message message;
94 struct user_regs_struct registers;
95 pthread_t creator;
96 CHECK(pthread_create(&creator, NULL, create_then_exit, &state) == 0);
97 started = 1;
98 CHECK(tc_atomic_wait(&state.ready, 1) == 0 && state.result == 0);
99 errno = 0;
100 CHECK(ptrace(PTRACE_GETREGS, state.child.pid, (void*)0, (void*)&registers) == -1 &&
101 errno == ESRCH);
102 atomic_store(&state.finish, 1);
103 CHECK(pthread_join(creator, NULL) == 0);
104 started = 0;
105 CHECK(state.result == 0);
106 CHECK(tc_receive(state.child.report, 'H', &message) == 0);
107 CHECK(message.signal == SIGUSR1 && message.code == SI_TKILL && message.pid == state.child.pid &&
108 message.uid == getuid());
109 CHECK(tc_receive(state.child.report, 'A', NULL) == 0);
110 errno = 0;
111 CHECK(ptrace(PTRACE_GETREGS, state.child.pid, (void*)0, (void*)&registers) == -1 &&
112 errno == ESRCH);
113 CHECK(tc_finish(&state.child) == 0);
114out:
115 atomic_store(&state.finish, 1);
116 if (started && pthread_join(creator, NULL))
117 failed = 1;
118 tc_close(&state.child);
119 return failed;
120}
121
122int tc_ownership(void) {
123 return sibling_denied() || tracer_task_exit();
124}