The Pedigree Project 0.1
timers.c
1#define _GNU_SOURCE
2#include <errno.h>
3#include <pthread.h>
4#include <semaphore.h>
5#include <signal.h>
6#include <stdlib.h>
7#include <string.h>
8#include <unistd.h>
9
10#include "contract.h"
11#include <sys/mman.h>
12#include <sys/syscall.h>
13
14static int arm(timer_t timer, int milliseconds, int interval) {
15 struct itimerspec setting = {.it_value = st_timespec((int64_t)milliseconds * 1000000),
16 .it_interval = st_timespec((int64_t)interval * 1000000)};
17 return timer_settime(timer, 0, &setting, NULL);
18}
19static int64_t remaining(const struct itimerspec* setting) {
20 return (int64_t)setting->it_value.tv_sec * 1000000000 + setting->it_value.tv_nsec;
21}
22
23static int basic_timers(void) {
24 int failed = 0, live = 0;
25 timer_t timer;
26 struct sigevent event = {.sigev_notify = SIGEV_NONE};
27 struct itimerspec setting, replacement = {.it_value = {0, 1000000}};
28 void* bad = mmap(NULL, 4096, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
29 CHECK(bad != MAP_FAILED);
30 CHECK(timer_create(CLOCK_MONOTONIC, &event, &timer) == 0);
31 live = 1;
32 CHECK(timer_gettime(timer, &setting) == 0 && remaining(&setting) == 0);
33 CHECK(arm(timer, 3000, 0) == 0);
34 CHECK(timer_gettime(timer, &setting) == 0 && remaining(&setting) > 1000000000);
35 CHECK(timer_settime(timer, 0, &replacement, bad) == -1 && errno == EFAULT);
36 CHECK(timer_gettime(timer, &setting) == 0 && remaining(&setting) > 1000000000);
37 CHECK(timer_gettime(timer, bad) == -1 && errno == EFAULT);
38 CHECK(syscall(SYS_timer_settime, timer, 0, NULL, NULL) == -1 && errno == EINVAL);
39 CHECK(syscall(SYS_timer_settime, timer, 0, bad, NULL) == -1 && errno == EFAULT);
40 replacement.it_value.tv_nsec = 1000000000;
41 CHECK(timer_settime(timer, 0, &replacement, NULL) == -1 && errno == EINVAL);
42 CHECK(arm(timer, 0, 10) == 0);
43 CHECK(timer_gettime(timer, &setting) == 0 && remaining(&setting) == 0 &&
44 setting.it_interval.tv_sec == 0 && setting.it_interval.tv_nsec == 0);
45 CHECK(timer_delete(timer) == 0);
46 live = 0;
47 CHECK(timer_gettime(timer, &setting) == -1 && errno == EINVAL);
48 CHECK(syscall(SYS_timer_create, CLOCK_MONOTONIC, NULL, bad) == -1 && errno == EFAULT);
49 CHECK(timer_create(CLOCK_PROCESS_CPUTIME_ID, &event, &timer) == -1 && errno == EINVAL);
50out:
51 if (live)
52 timer_delete(timer);
53 if (bad != MAP_FAILED)
54 munmap(bad, 4096);
55 return failed;
56}
57
58static int delivered_timers(void) {
59 int failed = 0, live = 0, masked = 0;
60 timer_t timer;
61 sigset_t set, original;
62 siginfo_t info;
63 struct timespec zero = {0}, timeout = {2, 0};
64 struct itimerspec setting;
65 struct sigevent event = {
66 .sigev_notify = SIGEV_SIGNAL, .sigev_signo = SIGRTMIN, .sigev_value.sival_int = 0x4567};
67 sigemptyset(&set);
68 sigaddset(&set, SIGRTMIN);
69 sigaddset(&set, SIGALRM);
70 CHECK(pthread_sigmask(SIG_BLOCK, &set, &original) == 0);
71 masked = 1;
72 CHECK(timer_create(CLOCK_MONOTONIC, &event, &timer) == 0);
73 live = 1;
74 CHECK(arm(timer, 60, 0) == 0);
75 CHECK(sigtimedwait(&set, &info, &timeout) == SIGRTMIN && info.si_code == SI_TIMER &&
76 info.si_value.sival_int == 0x4567 && info.si_overrun == 0);
77 CHECK(timer_gettime(timer, &setting) == 0 && remaining(&setting) == 0);
78 CHECK(timer_getoverrun(timer) == 0);
79 CHECK(arm(timer, 30, 30) == 0);
80 st_pause(170);
81 CHECK(sigtimedwait(&set, &info, &timeout) == SIGRTMIN && info.si_code == SI_TIMER &&
82 info.si_overrun >= 1 && timer_getoverrun(timer) == info.si_overrun);
83 CHECK(arm(timer, 0, 0) == 0);
84 CHECK(sigtimedwait(&set, &info, &zero) == -1 && errno == EAGAIN);
85
86 CHECK(arm(timer, 20, 0) == 0);
87 st_pause(80);
88 CHECK(arm(timer, 500, 0) == 0);
89 CHECK(sigtimedwait(&set, &info, &zero) == -1 && errno == EAGAIN);
90 CHECK(arm(timer, 20, 0) == 0);
91 st_pause(80);
92 CHECK(timer_delete(timer) == 0);
93 live = 0;
94 CHECK(sigtimedwait(&set, &info, &zero) == -1 && errno == EAGAIN);
95
96 CHECK(timer_create(CLOCK_REALTIME, NULL, &timer) == 0);
97 live = 1;
98 CHECK(arm(timer, 40, 0) == 0);
99 CHECK(sigtimedwait(&set, &info, &timeout) == SIGALRM && info.si_code == SI_TIMER &&
100 info.si_value.sival_int == (int)(intptr_t)timer);
101out:
102 if (live)
103 timer_delete(timer);
104 if (masked) {
105 while (sigtimedwait(&set, &info, &zero) >= 0) {
106 }
107 pthread_sigmask(SIG_SETMASK, &original, NULL);
108 }
109 return failed;
110}
111
113 sigset_t set;
114 siginfo_t info;
115 volatile int tid, done;
116 int result;
117};
118static void target_done(void* argument) {
119 struct target_wait* target = argument;
120 __atomic_store_n(&target->done, 1, __ATOMIC_RELEASE);
121}
122static void* target_wait(void* argument) {
123 struct target_wait* target = argument;
124 struct timespec timeout = {2, 0};
125 pthread_cleanup_push(target_done, target);
126 __atomic_store_n(&target->tid, (int)syscall(SYS_gettid), __ATOMIC_RELEASE);
127 target->result = sigtimedwait(&target->set, &target->info, &timeout);
128 pthread_cleanup_pop(1);
129 return NULL;
130}
132 sem_t completed;
133 volatile int count, tid;
134};
135static void timer_callback(union sigval value) {
136 struct callback_state* state = value.sival_ptr;
137 __atomic_store_n(&state->tid, (int)syscall(SYS_gettid), __ATOMIC_RELEASE);
138 __atomic_add_fetch(&state->count, 1, __ATOMIC_RELEASE);
139 sem_post(&state->completed);
140}
141static int threaded_timers(void) {
142 int failed = 0, live = 0, masked = 0, worker_active = 0, sem_live = 0;
143 pthread_t worker;
144 timer_t timer;
145 struct target_wait target = {0};
146 static struct callback_state callback;
147 sigset_t original;
148 siginfo_t info;
149 struct timespec zero = {0};
150 struct itimerspec setting;
151 struct sigevent event = {
152 .sigev_notify = SIGEV_THREAD_ID, .sigev_signo = SIGRTMAX, .sigev_value.sival_int = 6401};
153 sigemptyset(&target.set);
154 sigaddset(&target.set, SIGRTMAX);
155 CHECK(pthread_sigmask(SIG_BLOCK, &target.set, &original) == 0);
156 masked = 1;
157 CHECK(pthread_create(&worker, NULL, target_wait, &target) == 0);
158 worker_active = 1;
159 CHECK(st_wait(&target.tid, 1000) == 0);
160 event.sigev_notify_thread_id = target.tid;
161 CHECK(timer_create(CLOCK_MONOTONIC, &event, &timer) == 0);
162 live = 1;
163 CHECK(arm(timer, 40, 0) == 0);
164 CHECK(st_wait(&target.done, 2000) == 0 && pthread_join(worker, NULL) == 0);
165 worker_active = 0;
166 CHECK(target.result == SIGRTMAX && target.info.si_code == SI_TIMER &&
167 target.info.si_value.sival_int == 6401);
168 CHECK(sigtimedwait(&target.set, &info, &zero) == -1 && errno == EAGAIN);
169 CHECK(timer_gettime(timer, &setting) == -1 && errno == EINVAL);
170 live = 0;
171
172 CHECK(sem_init(&callback.completed, 0, 0) == 0);
173 sem_live = 1;
174 event = (struct sigevent){.sigev_notify = SIGEV_THREAD,
175 .sigev_notify_function = timer_callback,
176 .sigev_value.sival_ptr = &callback};
177 CHECK(timer_create(CLOCK_MONOTONIC, &event, &timer) == 0);
178 live = 1;
179 CHECK(arm(timer, 40, 0) == 0);
180 struct timespec deadline = st_timespec(st_now(CLOCK_REALTIME) + 2000000000);
181 CHECK(sem_timedwait(&callback.completed, &deadline) == 0);
182 CHECK(__atomic_load_n(&callback.count, __ATOMIC_ACQUIRE) == 1 &&
183 __atomic_load_n(&callback.tid, __ATOMIC_ACQUIRE) != syscall(SYS_gettid));
184 CHECK(timer_gettime(timer, &setting) == 0 && remaining(&setting) == 0);
185 CHECK(timer_delete(timer) == 0);
186 live = 0;
187 int64_t deadline_exit = st_now(CLOCK_MONOTONIC) + 2000000000;
188 while (syscall(SYS_tkill, callback.tid, 0) == 0 && st_now(CLOCK_MONOTONIC) < deadline_exit)
189 st_pause(2);
190 CHECK(syscall(SYS_tkill, callback.tid, 0) == -1 && errno == ESRCH);
191out:
192 if (live) {
193 timer_delete(timer);
194 st_pause(60);
195 }
196 if (worker_active) {
197 pthread_cancel(worker);
198 if (st_wait(&target.done, 1000) == 0)
199 pthread_join(worker, NULL);
200 else
201 _exit(1);
202 }
203 if (sem_live)
204 sem_destroy(&callback.completed);
205 if (masked) {
206 while (sigtimedwait(&target.set, &info, &zero) >= 0) {
207 }
208 pthread_sigmask(SIG_SETMASK, &original, NULL);
209 }
210 return failed;
211}
212
213int signal_timer_exec(int argc, char** argv) {
214 if (argc != 3)
215 return 2;
216 alarm(5);
217 timer_t old = (timer_t)(intptr_t)strtol(argv[2], NULL, 10), timer;
218 struct itimerspec setting;
219 if (timer_gettime(old, &setting) != -1 || errno != EINVAL)
220 return 3;
221 struct sigevent event = {.sigev_notify = SIGEV_NONE};
222 if (timer_create(CLOCK_MONOTONIC, &event, &timer) || timer == old)
223 return 4;
224 if (timer_gettime(timer, &setting) || remaining(&setting) || timer_delete(timer))
225 return 5;
226 sigset_t set;
227 sigemptyset(&set);
228 sigaddset(&set, SIGRTMIN);
229 struct timespec zero = {0};
230 st_pause(150);
231 if (sigtimedwait(&set, NULL, &zero) != -1 || errno != EAGAIN)
232 return 6;
233 return 0;
234}
235static int timer_lifetime(void) {
236 int failed = 0, live = 0;
237 timer_t timer;
238 struct sigevent event = {.sigev_notify = SIGEV_NONE};
239 struct itimerspec setting;
240 pid_t child;
241 CHECK(timer_create(CLOCK_MONOTONIC, &event, &timer) == 0);
242 live = 1;
243 CHECK(arm(timer, 30000, 0) == 0);
244 CHECK((child = fork()) >= 0);
245 if (!child) {
246 alarm(5);
247 timer_t own;
248 if (timer_gettime(timer, &setting) != -1 || errno != EINVAL)
249 _exit(10);
250 if (timer_create(CLOCK_MONOTONIC, &event, &own) || own == timer)
251 _exit(11);
252 if (timer_delete(own))
253 _exit(12);
254 _exit(0);
255 }
256 CHECK(st_reap(child, 6000) == 0);
257 CHECK(timer_gettime(timer, &setting) == 0 && remaining(&setting) > 0);
258 CHECK((child = fork()) >= 0);
259 if (!child) {
260 alarm(5);
261 sigset_t set;
262 sigemptyset(&set);
263 sigaddset(&set, SIGRTMIN);
264 if (pthread_sigmask(SIG_BLOCK, &set, NULL))
265 _exit(20);
266 event = (struct sigevent){.sigev_notify = SIGEV_SIGNAL, .sigev_signo = SIGRTMIN};
267 timer_t own;
268 if (timer_create(CLOCK_MONOTONIC, &event, &own) || arm(own, 30, 30))
269 _exit(21);
270 st_pause(80);
271 char identifier[32];
272 snprintf(identifier, sizeof(identifier), "%ld", (long)(intptr_t)own);
273 execl("/applications/signal-timer-contract-test", "signal-timer-contract-test", "timer-exec",
274 identifier, (char*)NULL);
275 _exit(22);
276 }
277 CHECK(st_reap(child, 6000) == 0);
278out:
279 if (live)
280 timer_delete(timer);
281 return failed;
282}
283
284int signal_timer_test_timers(void) {
285 if (basic_timers() || delivered_timers() || threaded_timers() || timer_lifetime())
286 return 1;
287 return 0;
288}