The Pedigree Project 0.1
private-signal-test/main.c
1#define _GNU_SOURCE
2
3#include <errno.h>
4#include <pthread.h>
5#include <sched.h>
6#include <signal.h>
7#include <stdint.h>
8#include <stdio.h>
9#include <unistd.h>
10
11#include <sys/membarrier.h>
12#include <sys/syscall.h>
13#include <sys/wait.h>
14
15enum {
16 musl_sig_timer = 32,
17 musl_sig_cancel = 33,
18 musl_sig_synccall = 34,
19 first_realtime_signal = 35,
20 last_realtime_signal = 64,
21 first_unsupported_signal = 65,
22 kernel_sigset_size = 8,
23 wait_attempts = 100000,
24};
25
27 uint64_t handler;
28 uint64_t flags;
29 uint64_t restorer;
30 uint64_t mask;
31};
32
33_Static_assert(sizeof(struct kernel_sigaction) == 32,
34 "Linux amd64 kernel sigaction must be 32 bytes");
35
36static int wait_for_value(volatile int* value) {
37 for (size_t attempt = 0; attempt < wait_attempts; ++attempt) {
38 if (__atomic_load_n(value, __ATOMIC_ACQUIRE)) {
39 return 0;
40 }
41 sched_yield();
42 }
43 return -1;
44}
45
46static int raw_signal_contract(void) {
47 alarm(10);
48
49 if (SIGRTMIN != first_realtime_signal || SIGRTMAX != last_realtime_signal) {
50 return 1;
51 }
52 sigset_t public_set;
53 if (sigemptyset(&public_set)) {
54 return 2;
55 }
56 for (int signal = musl_sig_timer; signal <= musl_sig_synccall; ++signal) {
57 errno = 0;
58 if (sigaddset(&public_set, signal) != -1 || errno != EINVAL) {
59 return 3;
60 }
61 errno = 0;
62 if (sigdelset(&public_set, signal) != -1 || errno != EINVAL) {
63 return 4;
64 }
65 }
66 if (sigfillset(&public_set)) {
67 return 5;
68 }
69 for (int signal = musl_sig_timer; signal <= musl_sig_synccall; ++signal) {
70 if (sigismember(&public_set, signal)) {
71 return 6;
72 }
73 }
74
75 for (int signal = musl_sig_timer; signal <= musl_sig_synccall; ++signal) {
76 struct sigaction public_action = {0};
77 errno = 0;
78 if (sigaction(signal, 0, &public_action) != -1 || errno != EINVAL) {
79 return 10 + signal - musl_sig_timer;
80 }
81
82 struct kernel_sigaction ignored = {
83 .handler = 1,
84 .mask = UINT64_C(1) << (SIGUSR1 - 1),
85 };
86 struct kernel_sigaction previous = {
87 .handler = UINT64_MAX,
88 .flags = UINT64_MAX,
89 .restorer = UINT64_MAX,
90 .mask = UINT64_MAX,
91 };
92 if (syscall(SYS_rt_sigaction, signal, &ignored, &previous, kernel_sigset_size) ||
93 previous.handler || previous.flags || previous.restorer || previous.mask) {
94 return 20 + signal - musl_sig_timer;
95 }
96
97 struct kernel_sigaction current = {0};
98 if (syscall(SYS_rt_sigaction, signal, 0, &current, kernel_sigset_size) ||
99 current.handler != ignored.handler || current.flags != ignored.flags ||
100 current.restorer != ignored.restorer || current.mask != ignored.mask) {
101 return 30 + signal - musl_sig_timer;
102 }
103
104 if (signal == musl_sig_timer && syscall(SYS_tkill, syscall(SYS_gettid), musl_sig_timer)) {
105 return 40;
106 }
107
108 if (syscall(SYS_rt_sigaction, signal, &previous, 0, kernel_sigset_size)) {
109 return 41 + signal - musl_sig_timer;
110 }
111 }
112
113 errno = 0;
114 if (syscall(SYS_rt_sigaction, first_unsupported_signal, 0, 0, kernel_sigset_size) != -1 ||
115 errno != EINVAL) {
116 return 50;
117 }
118 errno = 0;
119 if (syscall(SYS_rt_sigaction, musl_sig_timer, 0, 0, kernel_sigset_size * 2) != -1 ||
120 errno != EINVAL) {
121 return 51;
122 }
123 errno = 0;
124 if (kill(getpid(), musl_sig_timer) != -1 || errno != EINVAL) {
125 return 52;
126 }
127 return 0;
128}
129
130static volatile int realtime_received;
131
132static void realtime_handler(int signal) {
133 __atomic_store_n(&realtime_received, signal, __ATOMIC_RELEASE);
134}
135
136static int realtime_signal_contract(void) {
137 alarm(10);
138
139 const int signals[] = {first_realtime_signal, last_realtime_signal};
140 for (size_t i = 0; i < sizeof(signals) / sizeof(signals[0]); ++i) {
141 const int signal = signals[i];
142 struct sigaction action = {.sa_handler = realtime_handler}, previous;
143 sigemptyset(&action.sa_mask);
144 if (sigaction(signal, &action, &previous)) {
145 return 80 + i;
146 }
147
148 sigset_t set;
149 if (sigemptyset(&set) || sigaddset(&set, signal) || sigismember(&set, signal) != 1 ||
150 sigdelset(&set, signal) || sigismember(&set, signal) != 0 || sigfillset(&set) ||
151 sigismember(&set, signal) != 1) {
152 return 82 + i;
153 }
154
155 __atomic_store_n(&realtime_received, 0, __ATOMIC_RELEASE);
156 if (syscall(SYS_tgkill, getpid(), syscall(SYS_gettid), signal) ||
157 wait_for_value(&realtime_received) ||
158 __atomic_load_n(&realtime_received, __ATOMIC_ACQUIRE) != signal) {
159 return 84 + i;
160 }
161 __atomic_store_n(&realtime_received, 0, __ATOMIC_RELEASE);
162 if (kill(getpid(), signal) || wait_for_value(&realtime_received) ||
163 __atomic_load_n(&realtime_received, __ATOMIC_ACQUIRE) != signal) {
164 return 86 + i;
165 }
166 if (sigaction(signal, &previous, 0)) {
167 return 88 + i;
168 }
169 }
170
171 errno = 0;
172 if (syscall(SYS_tkill, syscall(SYS_gettid), first_unsupported_signal) != -1 || errno != EINVAL) {
173 return 90;
174 }
175 errno = 0;
176 if (syscall(SYS_tgkill, getpid(), syscall(SYS_gettid), first_unsupported_signal) != -1 ||
177 errno != EINVAL) {
178 return 91;
179 }
180 errno = 0;
181 if (kill(getpid(), first_unsupported_signal) != -1 || errno != EINVAL) {
182 return 92;
183 }
184 return 0;
185}
186
188 int descriptor;
189 volatile int entered;
190};
191
192static void* cancellation_target(void* parameter) {
193 struct cancel_probe* probe = parameter;
194 char value = 0;
195 __atomic_store_n(&probe->entered, 1, __ATOMIC_RELEASE);
196 (void)read(probe->descriptor, &value, sizeof(value));
197 return 0;
198}
199
200static int cancellation_contract(void) {
201 alarm(10);
202
203 int descriptors[2];
204 if (pipe(descriptors)) {
205 return 60;
206 }
207
208 struct cancel_probe probe = {
209 .descriptor = descriptors[0],
210 .entered = 0,
211 };
212 pthread_t thread;
213 if (pthread_create(&thread, 0, cancellation_target, &probe) || wait_for_value(&probe.entered)) {
214 return 61;
215 }
216
217 void* result = 0;
218 int error = pthread_cancel(thread);
219 if (error) {
220 dprintf(STDERR_FILENO, "private-signal cancel: pthread_cancel=%d\n", error);
221 return 62;
222 }
223 error = pthread_join(thread, &result);
224 if (error) {
225 dprintf(STDERR_FILENO, "private-signal cancel: pthread_join=%d\n", error);
226 return 63;
227 }
228 if (result != PTHREAD_CANCELED) {
229 dprintf(STDERR_FILENO, "private-signal cancel: result=%p expected=%p\n", result,
230 PTHREAD_CANCELED);
231 return 64;
232 }
233 if (close(descriptors[0]) || close(descriptors[1])) {
234 return 65;
235 }
236 return 0;
237}
238
240 volatile int ready;
241 volatile int stop;
242};
243
244static void* membarrier_target(void* parameter) {
245 struct membarrier_probe* probe = parameter;
246 __atomic_store_n(&probe->ready, 1, __ATOMIC_RELEASE);
247 while (!__atomic_load_n(&probe->stop, __ATOMIC_ACQUIRE)) {
248 sched_yield();
249 }
250 return 0;
251}
252
253static int membarrier_contract(void) {
254 alarm(10);
255
256 struct membarrier_probe probe = {0};
257 pthread_t thread;
258 if (pthread_create(&thread, 0, membarrier_target, &probe) || wait_for_value(&probe.ready)) {
259 return 70;
260 }
261
262 const int result = membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0);
263 const int saved_errno = errno;
264 __atomic_store_n(&probe.stop, 1, __ATOMIC_RELEASE);
265 if (pthread_join(thread, 0)) {
266 return 71;
267 }
268 if (result) {
269 errno = saved_errno;
270 return 72;
271 }
272 return 0;
273}
274
275static int run_bounded(int (*test)(void)) {
276 pid_t child = fork();
277 if (child < 0) {
278 return 255;
279 }
280 if (!child) {
281 _exit(test());
282 }
283
284 int status = 0;
285 for (size_t attempt = 0; attempt < wait_attempts; ++attempt) {
286 pid_t waited = waitpid(child, &status, WNOHANG);
287 if (waited == child) {
288 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
289 }
290 if (waited < 0 && errno != EINTR) {
291 return 254;
292 }
293 sched_yield();
294 }
295
296 (void)kill(child, SIGKILL);
297 (void)waitpid(child, &status, 0);
298 return 253;
299}
300
301int main(void) {
302 int result = run_bounded(raw_signal_contract);
303 if (result) {
304 printf("PRIVATE-SIGNAL-TEST: FAIL raw=%d\n", result);
305 return 1;
306 }
307 result = run_bounded(realtime_signal_contract);
308 if (result) {
309 printf("PRIVATE-SIGNAL-TEST: FAIL realtime=%d\n", result);
310 return 1;
311 }
312 result = run_bounded(cancellation_contract);
313 if (result) {
314 printf("PRIVATE-SIGNAL-TEST: FAIL cancel=%d\n", result);
315 return 1;
316 }
317 result = run_bounded(membarrier_contract);
318 if (result) {
319 printf("PRIVATE-SIGNAL-TEST: FAIL membarrier=%d\n", result);
320 return 1;
321 }
322
323 puts("PRIVATE-SIGNAL-TEST: PASS");
324 return 0;
325}