The Pedigree Project 0.1
ptrace-contract-test/errors.c
1#define _GNU_SOURCE
2#include <elf.h>
3#include <string.h>
4#include <unistd.h>
5
6#include "contract.h"
7#include <sys/mman.h>
8#include <sys/uio.h>
9
10static int admission(void) {
11 int failed = 0;
12 struct tc_child child = TC_CHILD_INIT;
13 struct tc_message message;
14 struct user_regs_struct registers;
15 CHECK(tc_spawn(&child, 0) == 0);
16 errno = 0;
17 CHECK(ptrace(PTRACE_GETREGS, child.pid, (void*)0, (void*)0) == -1 && errno == ESRCH);
18 CHECK(tc_send(&child, 'T', 0) == 0 && tc_receive(child.report, 'T', &message) == 0);
19 CHECK(message.result == 0);
20 CHECK(tc_send(&child, 'T', 0) == 0 && tc_receive(child.report, 'T', &message) == 0);
21 CHECK(message.result == -1 && message.error == EPERM);
22 errno = 0;
23 CHECK(ptrace(PTRACE_GETREGS, child.pid, (void*)0, (void*)0) == -1 && errno == ESRCH);
24 CHECK(tc_stop(&child, SIGUSR1) == 0);
25 CHECK(ptrace(PTRACE_GETREGS, child.pid, (void*)UINTPTR_MAX, (void*)&registers) == 0);
26 CHECK(tc_resume(child.pid, PTRACE_DETACH, 0) == 0 && tc_receive(child.report, 'A', NULL) == 0);
27 CHECK(tc_finish(&child) == 0);
28 tc_close(&child);
29
30 CHECK(tc_spawn(&child, 0) == 0);
31 CHECK(tc_send(&child, 'M', 0) == 0 && tc_receive(child.report, 'M', &message) == 0);
32 CHECK(message.result == -1 && message.error == EOPNOTSUPP);
33 CHECK(tc_finish(&child) == 0);
34 tc_close(&child);
35
36 CHECK(tc_spawn(&child, 0) == 0);
37 CHECK(tc_send(&child, 'D', 0) == 0 && tc_receive(child.report, 'D', &message) == 0);
38 CHECK(message.result == 0);
39 CHECK(tc_send(&child, 'T', 0) == 0 && tc_receive(child.report, 'T', &message) == 0);
40 CHECK(message.result == -1 && message.error == EPERM);
41 CHECK(tc_finish(&child) == 0);
42 tc_close(&child);
43
44 CHECK(tc_spawn(&child, 1) == 0);
45 CHECK(tc_send(&child, 'D', 0) == 0 && tc_receive(child.report, 'D', &message) == 0);
46 CHECK(message.result == 0 && tc_stop(&child, SIGUSR1) == 0);
47 errno = 0;
48 CHECK(ptrace(PTRACE_GETREGS, child.pid, (void*)0, (void*)&registers) == -1 && errno == EPERM);
49 errno = 0;
50 CHECK(tc_resume(child.pid, PTRACE_CONT, 0) == -1 && errno == EPERM);
51out:
52 tc_close(&child);
53 return failed;
54}
55
56static int copyout(void) {
57 int failed = 0;
58 struct tc_child child = TC_CHILD_INIT;
59 void* inaccessible = MAP_FAILED;
60 struct iovec* readonly = MAP_FAILED;
61 struct user_regs_struct original, after;
62 unsigned char output[TC_REG_SIZE + 32];
63 struct iovec vector;
64 CHECK(tc_spawn(&child, 1) == 0 && tc_stop(&child, SIGUSR1) == 0);
65 CHECK(ptrace(PTRACE_GETREGS, child.pid, (void*)0, (void*)&original) == 0);
66 inaccessible = mmap(NULL, tc_page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
67 readonly = mmap(NULL, tc_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
68 CHECK(inaccessible != MAP_FAILED && readonly != MAP_FAILED);
69 void* bad[] = {NULL, inaccessible};
70 for (size_t i = 0; i < sizeof(bad) / sizeof(bad[0]); ++i) {
71 errno = 0;
72 CHECK(ptrace(PTRACE_GETREGS, child.pid, (void*)0, bad[i]) == -1 && errno == EFAULT);
73 errno = 0;
74 CHECK(ptrace(PTRACE_GETSIGINFO, child.pid, (void*)0, bad[i]) == -1 && errno == EFAULT);
75 errno = 0;
76 CHECK(ptrace(PTRACE_GETREGSET, child.pid, (void*)(uintptr_t)NT_PRSTATUS, bad[i]) == -1 &&
77 errno == EFAULT);
78 }
79 memset(output, 0xa5, sizeof(output));
80 vector = (struct iovec){.iov_base = output, .iov_len = 7};
81 errno = 0;
82 CHECK(ptrace(PTRACE_GETREGSET, child.pid, (void*)(uintptr_t)NT_PRSTATUS, (void*)&vector) == -1 &&
83 errno == EINVAL);
84 CHECK(vector.iov_base == output && vector.iov_len == 7);
85 vector.iov_len = sizeof(output);
86 errno = 0;
87 CHECK(ptrace(PTRACE_GETREGSET, child.pid, (void*)0, (void*)&vector) == -1 && errno == EINVAL);
88 CHECK(vector.iov_base == output && vector.iov_len == sizeof(output));
89 for (size_t i = 0; i < sizeof(output); ++i)
90 CHECK(output[i] == 0xa5);
91 errno = 0;
92 CHECK(ptrace(PTRACE_GETREGSET, child.pid, (void*)0, inaccessible) == -1 && errno == EFAULT);
93
94 vector = (struct iovec){.iov_base = inaccessible, .iov_len = sizeof(output)};
95 errno = 0;
96 CHECK(ptrace(PTRACE_GETREGSET, child.pid, (void*)(uintptr_t)NT_PRSTATUS, (void*)&vector) == -1 &&
97 errno == EFAULT);
98 CHECK(vector.iov_base == inaccessible && vector.iov_len == sizeof(output));
99 vector.iov_len = 0;
100 CHECK(ptrace(PTRACE_GETREGSET, child.pid, (void*)(uintptr_t)NT_PRSTATUS, (void*)&vector) == 0);
101 CHECK(vector.iov_base == inaccessible && !vector.iov_len);
102
103 *readonly = (struct iovec){.iov_base = output, .iov_len = sizeof(output)};
104 CHECK(mprotect(readonly, tc_page, PROT_READ) == 0);
105 errno = 0;
106 CHECK(ptrace(PTRACE_GETREGSET, child.pid, (void*)(uintptr_t)NT_PRSTATUS, (void*)readonly) == -1 &&
107 errno == EFAULT);
108 CHECK(memcmp(output, &original, TC_REG_SIZE) == 0);
109 for (size_t i = TC_REG_SIZE; i < sizeof(output); ++i)
110 CHECK(output[i] == 0xa5);
111 CHECK(readonly->iov_base == output && readonly->iov_len == sizeof(output));
112 CHECK(mprotect(readonly, tc_page, PROT_READ | PROT_WRITE) == 0);
113 *readonly = (struct iovec){.iov_base = NULL, .iov_len = 0};
114 CHECK(mprotect(readonly, tc_page, PROT_READ) == 0);
115 errno = 0;
116 CHECK(ptrace(PTRACE_GETREGSET, child.pid, (void*)(uintptr_t)NT_PRSTATUS, (void*)readonly) == -1 &&
117 errno == EFAULT);
118
119 const uintptr_t invalid_signals[] = {65, UINTPTR_MAX, UINT64_C(0x10000000a)};
120 for (size_t i = 0; i < sizeof(invalid_signals) / sizeof(invalid_signals[0]); ++i) {
121 errno = 0;
122 CHECK(ptrace(PTRACE_CONT, child.pid, (void*)0, (void*)invalid_signals[i]) == -1 &&
123 errno == EIO);
124 errno = 0;
125 CHECK(ptrace(PTRACE_DETACH, child.pid, (void*)0, (void*)invalid_signals[i]) == -1 &&
126 errno == EIO);
127 }
128 errno = 0;
129 CHECK(ptrace(0x7fffffff, child.pid, (void*)0, (void*)0) == -1 && errno == EIO);
130 errno = 0;
131 CHECK(ptrace(PTRACE_GETFPREGS, child.pid, (void*)0, (void*)output) == -1 && errno == EOPNOTSUPP);
132 errno = 0;
133 CHECK(ptrace(PTRACE_GETREGS, (pid_t)0, (void*)0, (void*)0) == -1 && errno == ESRCH);
134 errno = 0;
135 CHECK(ptrace(PTRACE_GETREGS, (pid_t)-1, (void*)0, (void*)0) == -1 && errno == ESRCH);
136 CHECK(ptrace(PTRACE_GETREGS, child.pid, (void*)0, (void*)&after) == 0);
137 CHECK(memcmp(&original, &after, sizeof(original)) == 0);
138 CHECK(tc_resume(child.pid, PTRACE_CONT, 0) == 0 && tc_receive(child.report, 'A', NULL) == 0);
139 CHECK(tc_finish(&child) == 0);
140out:
141 if (readonly != MAP_FAILED)
142 munmap(readonly, tc_page);
143 if (inaccessible != MAP_FAILED)
144 munmap(inaccessible, tc_page);
145 tc_close(&child);
146 return failed;
147}
148
149static int replacement_quota(void) {
150 int failed = 0;
151 struct tc_child child = TC_CHILD_INIT;
152 struct tc_message message;
153 struct user_regs_struct before, after;
154 siginfo_t info;
155 const int realtime = SIGRTMIN;
156 CHECK(tc_spawn(&child, 1) == 0);
157 CHECK(tc_send(&child, 'B', realtime) == 0 && tc_receive(child.report, 'B', &message) == 0);
158 CHECK(message.result == 0);
159 for (int i = 0; i < 16; ++i) {
160 union sigval value = {.sival_int = i + 100};
161 CHECK(sigqueue(child.pid, realtime, value) == 0);
162 }
163 union sigval excess = {.sival_int = 999};
164 errno = 0;
165 CHECK(sigqueue(child.pid, realtime, excess) == -1 && errno == EAGAIN);
166 CHECK(tc_stop(&child, SIGUSR1) == 0);
167 CHECK(ptrace(PTRACE_GETREGS, child.pid, (void*)0, (void*)&before) == 0);
168 errno = 0;
169 CHECK(tc_resume(child.pid, PTRACE_CONT, realtime) == -1 && errno == EAGAIN);
170 CHECK(ptrace(PTRACE_GETREGS, child.pid, (void*)0, (void*)&after) == 0);
171 CHECK(memcmp(&before, &after, sizeof(before)) == 0);
172 CHECK(ptrace(PTRACE_GETSIGINFO, child.pid, (void*)0, (void*)&info) == 0);
173 CHECK(tc_signal_info(&info, SIGUSR1, SI_TKILL, child.pid, getuid()) == 0);
174 CHECK(tc_resume(child.pid, PTRACE_CONT, 0) == 0 && tc_receive(child.report, 'A', NULL) == 0);
175 CHECK(tc_send(&child, 'Q', realtime) == 0 && tc_receive(child.report, 'Q', &message) == 0);
176 CHECK(message.result == 16);
177 CHECK(tc_finish(&child) == 0);
178out:
179 tc_close(&child);
180 return failed;
181}
182
183int tc_errors(void) {
184 return admission() || copyout() || replacement_quota();
185}