The Pedigree Project 0.1
signal-timer-contract-test/clock.c
1#define _GNU_SOURCE
2#include <errno.h>
3#include <fcntl.h>
4#include <mqueue.h>
5#include <pthread.h>
6#include <signal.h>
7#include <string.h>
8#include <unistd.h>
9
10#include "contract.h"
11#include <sys/mman.h>
12
13struct clock_wait {
14 clockid_t clock;
15 int flags, result;
16 struct timespec request;
17 volatile int *release, ready, entered, done;
18 int64_t deadline_delay, started, finished, realtime_started, realtime_finished;
19};
20static void wait_done(void* argument) {
21 __atomic_store_n((volatile int*)argument, 1, __ATOMIC_RELEASE);
22}
23static void* clock_wait(void* argument) {
24 struct clock_wait* waiter = argument;
25 pthread_cleanup_push(wait_done, (void*)&waiter->done);
26 __atomic_store_n(&waiter->ready, 1, __ATOMIC_RELEASE);
27 if (!waiter->release || st_wait(waiter->release, 5000) == 0) {
28 waiter->started = st_now(CLOCK_MONOTONIC);
29 waiter->realtime_started = st_now(CLOCK_REALTIME);
30 if (waiter->deadline_delay)
31 waiter->request = st_timespec(
32 (waiter->clock == CLOCK_REALTIME ? waiter->realtime_started : waiter->started) +
33 waiter->deadline_delay);
34 __atomic_store_n(&waiter->entered, 1, __ATOMIC_RELEASE);
35 waiter->result = clock_nanosleep(waiter->clock, waiter->flags, &waiter->request, NULL);
36 waiter->finished = st_now(CLOCK_MONOTONIC);
37 waiter->realtime_finished = st_now(CLOCK_REALTIME);
38 } else {
39 waiter->result = ETIMEDOUT;
40 }
41 pthread_cleanup_pop(1);
42 return NULL;
43}
44
45struct clock_jump {
46 int64_t before_real, before_mono, target, after_real, after_mono, observed_real, observed_mono;
47};
48static void diagnose_backward(const char* stage, const struct clock_wait waits[3],
49 const struct clock_jump* jump) {
50 fprintf(stderr,
51 "SIGNAL-TIMER-CONTRACT: clock backward stage=%s real=%lld mono=%lld "
52 "jump_before=%lld/%lld target=%lld jump_after=%lld/%lld observed=%lld/%lld\n",
53 stage, (long long)st_now(CLOCK_REALTIME), (long long)st_now(CLOCK_MONOTONIC),
54 (long long)jump->before_real, (long long)jump->before_mono, (long long)jump->target,
55 (long long)jump->after_real, (long long)jump->after_mono, (long long)jump->observed_real,
56 (long long)jump->observed_mono);
57 for (int n = 0; n < 3; ++n) {
58 const int entered = __atomic_load_n(&waits[n].entered, __ATOMIC_ACQUIRE);
59 const int done = __atomic_load_n(&waits[n].done, __ATOMIC_ACQUIRE);
60 // A running worker has not published its result or finish timestamps yet.
61 fprintf(
62 stderr,
63 "SIGNAL-TIMER-CONTRACT: clock worker=%d clock=%d flags=%d ready=%d entered=%d "
64 "done=%d result=%d start=%lld/%lld finish=%lld/%lld request=%lld.%09ld\n",
65 n, (int)waits[n].clock, waits[n].flags, __atomic_load_n(&waits[n].ready, __ATOMIC_ACQUIRE),
66 entered, done, done ? waits[n].result : -1,
67 entered ? (long long)waits[n].realtime_started : -1,
68 entered ? (long long)waits[n].started : -1,
69 done ? (long long)waits[n].realtime_finished : -1, done ? (long long)waits[n].finished : -1,
70 entered ? (long long)waits[n].request.tv_sec : -1, entered ? waits[n].request.tv_nsec : 0L);
71 }
72}
73static int restore_clock(int64_t realtime, int64_t monotonic) {
74 struct timespec target = st_timespec(realtime + st_now(CLOCK_MONOTONIC) - monotonic);
75 return clock_settime(CLOCK_REALTIME, &target);
76}
77
79 mqd_t queue;
80 int sending, result, error;
81 struct timespec deadline;
82 volatile int entered, done;
83 int64_t finished;
84};
85static void* mqueue_wait(void* argument) {
86 struct mqueue_wait* waiter = argument;
87 char byte;
88 pthread_cleanup_push(wait_done, (void*)&waiter->done);
89 __atomic_store_n(&waiter->entered, 1, __ATOMIC_RELEASE);
90 waiter->result = waiter->sending
91 ? mq_timedsend(waiter->queue, "x", 1, 1, &waiter->deadline)
92 : mq_timedreceive(waiter->queue, &byte, 1, NULL, &waiter->deadline);
93 waiter->error = errno;
94 waiter->finished = st_now(CLOCK_MONOTONIC);
95 pthread_cleanup_pop(1);
96 return NULL;
97}
98static int mqueue_clock_change(void) {
99 int failed = 0, changed = 0, active[2] = {0};
100 pthread_t workers[2];
101 struct mqueue_wait waits[2] = {{.queue = (mqd_t)-1}, {.queue = (mqd_t)-1, .sending = 1}};
102 char names[2][64];
103 struct mq_attr attributes = {.mq_maxmsg = 1, .mq_msgsize = 1}, observed;
104 const int64_t realtime = st_now(CLOCK_REALTIME), monotonic = st_now(CLOCK_MONOTONIC);
105 for (int n = 0; n < 2; ++n)
106 snprintf(names[n], sizeof(names[n]), "/clock-contract-%ld-%d", (long)getpid(), n);
107 for (int n = 0; n < 2; ++n) {
108 waits[n].queue = mq_open(names[n], O_CREAT | O_EXCL | O_RDWR, 0600, &attributes);
109 CHECK(waits[n].queue != (mqd_t)-1);
110 waits[n].deadline = st_timespec(st_now(CLOCK_REALTIME) + 5000000000);
111 if (n == 1)
112 CHECK(mq_send(waits[n].queue, "q", 1, 7) == 0);
113 CHECK(pthread_create(&workers[n], NULL, mqueue_wait, &waits[n]) == 0);
114 active[n] = 1;
115 CHECK(st_wait(&waits[n].entered, 1000) == 0);
116 }
117 st_pause(50);
118 CHECK(!__atomic_load_n(&waits[0].done, __ATOMIC_ACQUIRE) &&
119 !__atomic_load_n(&waits[1].done, __ATOMIC_ACQUIRE));
120 struct timespec target = st_timespec(st_now(CLOCK_REALTIME) + 10000000000);
121 const int64_t jumped = st_now(CLOCK_MONOTONIC);
122 CHECK(clock_settime(CLOCK_REALTIME, &target) == 0);
123 changed = 1;
124 for (int n = 0; n < 2; ++n) {
125 CHECK(st_wait(&waits[n].done, 500) == 0);
126 CHECK(waits[n].result == -1 && waits[n].error == ETIMEDOUT);
127 // A one-second polling recheck cannot satisfy this clock-change wake contract.
128 CHECK(waits[n].finished - jumped >= 0 && waits[n].finished - jumped < 500000000);
129 CHECK(pthread_join(workers[n], NULL) == 0);
130 active[n] = 0;
131 CHECK(mq_getattr(waits[n].queue, &observed) == 0 && observed.mq_curmsgs == n);
132 attributes.mq_flags = O_NONBLOCK;
133 CHECK(mq_setattr(waits[n].queue, &attributes, NULL) == 0);
134 }
135 char byte;
136 unsigned priority;
137 CHECK(mq_receive(waits[0].queue, &byte, 1, NULL) == -1 && errno == EAGAIN);
138 CHECK(mq_receive(waits[1].queue, &byte, 1, &priority) == 1 && byte == 'q' && priority == 7);
139 CHECK(mq_receive(waits[1].queue, &byte, 1, NULL) == -1 && errno == EAGAIN);
140out:
141 if (changed && restore_clock(realtime, monotonic))
142 failed = 1;
143 for (int n = 0; n < 2; ++n)
144 if (waits[n].queue != (mqd_t)-1)
145 mq_unlink(names[n]);
146 for (int n = 0; n < 2; ++n) {
147 if (active[n]) {
148 pthread_cancel(workers[n]);
149 if (st_wait(&waits[n].done, 1000) == 0)
150 pthread_join(workers[n], NULL);
151 else
152 _exit(1);
153 }
154 if (waits[n].queue != (mqd_t)-1)
155 mq_close(waits[n].queue);
156 }
157 return failed;
158}
159
160int signal_timer_test_clock(void) {
161 int failed = 0, changed = 0, active[3] = {0}, timer_live[3] = {0};
162 volatile int release = 0;
163 const char* backward_stage = NULL;
164 struct clock_jump jump = {0};
165 pthread_t workers[3];
166 struct clock_wait waits[3] = {0};
167 timer_t timers[3];
168 struct sigevent event = {.sigev_notify = SIGEV_NONE};
169 struct itimerspec setting = {0}, observed;
170 const int64_t realtime = st_now(CLOCK_REALTIME), monotonic = st_now(CLOCK_MONOTONIC);
171 struct timespec target = st_timespec(realtime), invalid = {0, 1000000000};
172 void* bad = mmap(NULL, 4096, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
173 CHECK(realtime > 20000000000 && monotonic >= 0 && bad != MAP_FAILED);
174 CHECK(clock_settime(CLOCK_MONOTONIC, &target) == -1 && errno == EINVAL);
175 CHECK(clock_settime(CLOCK_REALTIME, &invalid) == -1 && errno == EINVAL);
176 CHECK(clock_settime(CLOCK_REALTIME, bad) == -1 && errno == EFAULT);
177 if (geteuid() != 0) {
178 CHECK(clock_settime(CLOCK_REALTIME, &target) == -1 && errno == EPERM);
179 puts("SIGNAL-TIMER-CONTRACT: SKIP clock changes require uid 0");
180 goto out;
181 }
182 pid_t child = fork();
183 CHECK(child >= 0);
184 if (!child) {
185 alarm(5);
186 if (setuid(65534))
187 _exit(10);
188 _exit(clock_settime(CLOCK_REALTIME, &target) == -1 && errno == EPERM ? 0 : 11);
189 }
190 CHECK(st_reap(child, 6000) == 0);
191 CHECK(mqueue_clock_change() == 0);
192
193 int64_t before_real = st_now(CLOCK_REALTIME), before_mono = st_now(CLOCK_MONOTONIC);
194 target = st_timespec(before_real + 10000000000);
195 CHECK(clock_settime(CLOCK_REALTIME, &target) == 0);
196 changed = 1;
197 CHECK(st_now(CLOCK_REALTIME) >= before_real + 10000000000 &&
198 st_now(CLOCK_REALTIME) < before_real + 12000000000);
199 CHECK(st_now(CLOCK_MONOTONIC) >= before_mono &&
200 st_now(CLOCK_MONOTONIC) - before_mono < 2000000000);
201 target = st_timespec(before_real - 10000000000);
202 CHECK(clock_settime(CLOCK_REALTIME, &target) == 0);
203 CHECK(st_now(CLOCK_REALTIME) >= before_real - 10000000000 &&
204 st_now(CLOCK_REALTIME) < before_real - 8000000000);
205 CHECK(restore_clock(realtime, monotonic) == 0);
206
207 // A past deadline in the final signed-nanosecond second must remain past.
208 target = (struct timespec){9223372036, 250000000};
209 CHECK(clock_settime(CLOCK_REALTIME, &target) == 0);
210 struct timespec past = {9223372036, 0};
211 before_mono = st_now(CLOCK_MONOTONIC);
212 CHECK(clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &past, NULL) == 0);
213 CHECK(st_now(CLOCK_MONOTONIC) - before_mono < 300000000);
214 CHECK(restore_clock(realtime, monotonic) == 0);
215
216 backward_stage = "prepare";
217 waits[0] = (struct clock_wait){
218 .clock = CLOCK_REALTIME, .flags = TIMER_ABSTIME, .deadline_delay = 1000000000};
219 waits[1] = (struct clock_wait){
220 .clock = CLOCK_MONOTONIC, .flags = TIMER_ABSTIME, .deadline_delay = 250000000};
221 waits[2] = (struct clock_wait){.clock = CLOCK_REALTIME, .request = {0, 250000000}};
222 for (int n = 0; n < 3; ++n) {
223 waits[n].release = &release;
224 CHECK(pthread_create(&workers[n], NULL, clock_wait, &waits[n]) == 0);
225 active[n] = 1;
226 CHECK(st_wait(&waits[n].ready, 1000) == 0);
227 }
228 // Thread creation must not consume the deadline being tested. Each worker
229 // samples its absolute deadline only after all three workers exist.
230 backward_stage = "start";
231 __atomic_store_n(&release, 1, __ATOMIC_RELEASE);
232 for (int n = 0; n < 3; ++n)
233 CHECK(st_wait(&waits[n].entered, 1000) == 0);
234 st_pause(30);
235 backward_stage = "before-jump";
236 jump.before_real = st_now(CLOCK_REALTIME);
237 jump.before_mono = st_now(CLOCK_MONOTONIC);
238 const int64_t original_deadline =
239 (int64_t)waits[0].request.tv_sec * 1000000000 + waits[0].request.tv_nsec;
240 CHECK(original_deadline - jump.before_real >= 500000000);
241 CHECK(__atomic_load_n(&waits[0].done, __ATOMIC_ACQUIRE) == 0);
242 jump.target = jump.before_real - 3000000000;
243 target = st_timespec(jump.target);
244 CHECK(clock_settime(CLOCK_REALTIME, &target) == 0);
245 jump.after_real = st_now(CLOCK_REALTIME);
246 jump.after_mono = st_now(CLOCK_MONOTONIC);
247 backward_stage = "after-jump";
248 st_pause(1200);
249 jump.observed_real = st_now(CLOCK_REALTIME);
250 jump.observed_mono = st_now(CLOCK_MONOTONIC);
251 // Observe after the original deadline but before the shifted deadline, so
252 // neither an already-expired setup nor a late supervisor can yield a pass.
253 CHECK(jump.observed_mono - waits[0].started >= waits[0].deadline_delay);
254 CHECK(jump.observed_real < original_deadline);
255 CHECK(__atomic_load_n(&waits[0].done, __ATOMIC_ACQUIRE) == 0);
256 backward_stage = "unaffected-waits";
257 for (int n = 1; n < 3; ++n) {
258 CHECK(st_wait(&waits[n].done, 1000) == 0 && waits[n].result == 0);
259 CHECK(waits[n].finished - waits[n].started >= 200000000 &&
260 waits[n].finished - waits[n].started < 2000000000);
261 }
262 backward_stage = "restore";
263 CHECK(restore_clock(realtime, monotonic) == 0);
264 CHECK(st_wait(&waits[0].done, 1500) == 0 && waits[0].result == 0);
265 for (int n = 0; n < 3; ++n) {
266 CHECK(pthread_join(workers[n], NULL) == 0);
267 active[n] = 0;
268 }
269 backward_stage = NULL;
270
271 waits[0] = (struct clock_wait){.clock = CLOCK_REALTIME,
272 .flags = TIMER_ABSTIME,
273 .request = st_timespec(st_now(CLOCK_REALTIME) + 5000000000)};
274 CHECK(pthread_create(&workers[0], NULL, clock_wait, &waits[0]) == 0);
275 active[0] = 1;
276 CHECK(st_wait(&waits[0].entered, 1000) == 0);
277 for (int n = 0; n < 3; ++n) {
278 CHECK(timer_create(n == 2 ? CLOCK_MONOTONIC : CLOCK_REALTIME, &event, &timers[n]) == 0);
279 timer_live[n] = 1;
280 setting.it_value = n == 0 ? waits[0].request : (struct timespec){3, 0};
281 CHECK(timer_settime(timers[n], n == 0 ? TIMER_ABSTIME : 0, &setting, NULL) == 0);
282 }
283 st_pause(30);
284 target = st_timespec(st_now(CLOCK_REALTIME) + 10000000000);
285 before_mono = st_now(CLOCK_MONOTONIC);
286 CHECK(clock_settime(CLOCK_REALTIME, &target) == 0);
287 CHECK(st_wait(&waits[0].done, 1500) == 0 && waits[0].result == 0);
288 CHECK(st_now(CLOCK_MONOTONIC) - before_mono < 1500000000);
289 CHECK(pthread_join(workers[0], NULL) == 0);
290 active[0] = 0;
291 CHECK(timer_gettime(timers[0], &observed) == 0 && observed.it_value.tv_sec == 0 &&
292 observed.it_value.tv_nsec == 0);
293 for (int n = 1; n < 3; ++n)
294 CHECK(timer_gettime(timers[n], &observed) == 0 && observed.it_value.tv_sec >= 1);
295
296out:
297 if (failed && backward_stage)
298 diagnose_backward(backward_stage, waits, &jump);
299 if (changed && restore_clock(realtime, monotonic))
300 failed = 1;
301 for (int n = 0; n < 3; ++n) {
302 if (timer_live[n])
303 timer_delete(timers[n]);
304 if (active[n]) {
305 pthread_cancel(workers[n]);
306 if (st_wait(&waits[n].done, 1000) == 0)
307 pthread_join(workers[n], NULL);
308 else
309 _exit(1);
310 }
311 }
312 if (bad != MAP_FAILED)
313 munmap(bad, 4096);
314 return failed;
315}