The Pedigree Project 0.1
signal-timer-contract-test/signals.c
1#define _GNU_SOURCE
2#include <errno.h>
3#include <pthread.h>
4#include <signal.h>
5#include <string.h>
6#include <unistd.h>
7
8#include "contract.h"
9#include <sys/mman.h>
10#include <sys/syscall.h>
11
12struct waiter {
13 sigset_t set;
14 siginfo_t info;
15 volatile int entered, done;
16 int result;
17};
18static void finished(void* argument) {
19 struct waiter* waiter = argument;
20 __atomic_store_n(&waiter->done, 1, __ATOMIC_RELEASE);
21}
22static void* wait_for_signal(void* argument) {
23 struct waiter* waiter = argument;
24 pthread_cleanup_push(finished, waiter);
25 __atomic_store_n(&waiter->entered, 1, __ATOMIC_RELEASE);
26 waiter->result = sigwaitinfo(&waiter->set, &waiter->info);
27 pthread_cleanup_pop(1);
28 return NULL;
29}
30static volatile int handled;
31static siginfo_t handler_info;
32static void handler(int signal, siginfo_t* info, void* context) {
33 (void)signal;
34 (void)context;
35 handler_info = *info;
36 __atomic_store_n(&handled, 1, __ATOMIC_RELEASE);
37}
38
39static int blocked_ignored_signals(void) {
40 int failed = 0, masked = 0, installed = 0;
41 const int signals[] = {SIGCHLD, SIGUSR2};
42 sigset_t set, original, pending, one;
43 struct sigaction previous[2];
44 const struct timespec zero = {0};
45 siginfo_t info;
46 CHECK(sigemptyset(&set) == 0 && sigaddset(&set, SIGCHLD) == 0 && sigaddset(&set, SIGUSR2) == 0);
47 CHECK(pthread_sigmask(SIG_BLOCK, &set, &original) == 0);
48 masked = 1;
49 for (int n = 0; n < 2; ++n) {
50 struct sigaction action = {.sa_handler = n ? SIG_IGN : SIG_DFL};
51 sigemptyset(&action.sa_mask);
52 CHECK(sigaction(signals[n], &action, &previous[n]) == 0);
53 ++installed;
54 }
55 for (int n = 0; n < 2; ++n) {
56 CHECK(n ? sigqueue(getpid(), signals[n], (union sigval){.sival_int = 77}) == 0
57 : kill(getpid(), signals[n]) == 0);
58 CHECK(sigpending(&pending) == 0 && sigismember(&pending, signals[n]) == 1);
59 CHECK(sigemptyset(&one) == 0 && sigaddset(&one, signals[n]) == 0);
60 CHECK(sigtimedwait(&one, &info, &zero) == signals[n] && info.si_signo == signals[n]);
61 CHECK(sigpending(&pending) == 0 && sigismember(&pending, signals[n]) == 0);
62 }
63
64out:
65 if (masked) {
66 // Both signals remain blocked until any test-generated pending signal is
67 // consumed, so restoring SIGUSR2's original disposition cannot deliver it.
68 while (sigtimedwait(&set, &info, &zero) >= 0) {
69 }
70 for (int n = 0; n < installed; ++n)
71 if (sigaction(signals[n], &previous[n], NULL))
72 failed = 1;
73 if (pthread_sigmask(SIG_SETMASK, &original, NULL))
74 failed = 1;
75 }
76 return failed;
77}
78
79int signal_timer_test_signals(void) {
80 int failed = 0, masked = 0, worker_active = 0, handler_installed = 0;
81 sigset_t set, original, pending, one;
82 struct sigaction action = {.sa_sigaction = handler, .sa_flags = SA_SIGINFO}, old_action;
83 struct timespec zero = {0}, timeout = {0, 150000000};
84 siginfo_t info;
85 pthread_t worker;
86 struct waiter waiter = {0};
87 void* bad = MAP_FAILED;
88 const int low = SIGRTMIN, high = SIGRTMAX;
89 const pid_t pid = getpid();
90 const uid_t uid = getuid();
91 sigemptyset(&set);
92 CHECK(blocked_ignored_signals() == 0);
93 CHECK(high == 64 && low > 34);
94 CHECK(sigaddset(&set, SIGUSR1) == 0 && sigaddset(&set, low) == 0 &&
95 sigaddset(&set, low + 1) == 0 && sigaddset(&set, high) == 0);
96 CHECK(pthread_sigmask(SIG_BLOCK, &set, &original) == 0);
97 masked = 1;
98 sigemptyset(&one);
99 sigaddset(&one, SIGUSR1);
100 CHECK(sigqueue(pid, SIGUSR1, (union sigval){.sival_int = 101}) == 0);
101 CHECK(sigqueue(pid, SIGUSR1, (union sigval){.sival_int = 202}) == 0);
102 CHECK(sigpending(&pending) == 0 && sigismember(&pending, SIGUSR1) == 1);
103 CHECK(sigwaitinfo(&one, &info) == SIGUSR1 && info.si_code == SI_QUEUE && info.si_pid == pid &&
104 info.si_uid == uid && info.si_value.sival_int == 101);
105 CHECK(sigtimedwait(&one, &info, &zero) == -1 && errno == EAGAIN);
106 CHECK(sigpending(&pending) == 0 && sigismember(&pending, SIGUSR1) == 0);
107
108 CHECK(sigqueue(pid, high, (union sigval){.sival_int = 640}) == 0);
109 CHECK(sigqueue(pid, low, (union sigval){.sival_int = 350}) == 0);
110 CHECK(sigqueue(pid, low, (union sigval){.sival_int = 351}) == 0);
111 CHECK(sigwaitinfo(&set, &info) == low && info.si_value.sival_int == 350);
112 CHECK(sigwaitinfo(&set, &info) == low && info.si_value.sival_int == 351);
113 CHECK(sigwaitinfo(&set, &info) == high && info.si_value.sival_int == 640);
114 sigemptyset(&one);
115 sigaddset(&one, low);
116 for (int n = 0; n < 16; ++n)
117 CHECK(sigqueue(pid, low, (union sigval){.sival_int = n}) == 0);
118 CHECK(sigqueue(pid, low, (union sigval){.sival_int = 16}) == -1 && errno == EAGAIN);
119 CHECK(kill(pid, low) == -1 && errno == EAGAIN);
120 CHECK(pthread_kill(pthread_self(), low) == EAGAIN);
121 CHECK(sigwaitinfo(&one, &info) == low && info.si_value.sival_int == 0);
122 CHECK(sigqueue(pid, low, (union sigval){.sival_int = 16}) == 0);
123 for (int n = 1; n <= 16; ++n)
124 CHECK(sigwaitinfo(&one, &info) == low && info.si_value.sival_int == n);
125
126 bad = mmap(NULL, 4096, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
127 CHECK(bad != MAP_FAILED);
128 CHECK(sigpending(bad) == -1 && errno == EFAULT);
129 CHECK(sigqueue(pid, low, (union sigval){.sival_int = 999}) == 0);
130 CHECK(sigtimedwait(&one, bad, &zero) == -1 && errno == EFAULT);
131 CHECK(sigpending(&pending) == 0 && sigismember(&pending, low) == 1);
132 CHECK(sigwaitinfo(&one, &info) == low && info.si_value.sival_int == 999);
133 CHECK(syscall(SYS_rt_sigpending, &pending, 16) == -1 && errno == EINVAL);
134 struct timespec invalid = {0, 1000000000};
135 CHECK(sigtimedwait(&one, &info, &invalid) == -1 && errno == EINVAL);
136 int64_t started = st_now(CLOCK_MONOTONIC);
137 CHECK(sigtimedwait(&one, &info, &timeout) == -1 && errno == EAGAIN);
138 int64_t elapsed = st_now(CLOCK_MONOTONIC) - started;
139 CHECK(elapsed >= 100000000 && elapsed < 2000000000);
140
141 waiter.set = one;
142 CHECK(pthread_create(&worker, NULL, wait_for_signal, &waiter) == 0);
143 worker_active = 1;
144 CHECK(st_wait(&waiter.entered, 1000) == 0);
145 st_pause(20);
146 CHECK(sigqueue(pid, low, (union sigval){.sival_int = 501}) == 0);
147 CHECK(st_wait(&waiter.done, 2000) == 0 && pthread_join(worker, NULL) == 0);
148 worker_active = 0;
149 CHECK(waiter.result == low && waiter.info.si_value.sival_int == 501);
150 memset(&waiter, 0, sizeof(waiter));
151 waiter.set = one;
152 CHECK(pthread_create(&worker, NULL, wait_for_signal, &waiter) == 0);
153 worker_active = 1;
154 CHECK(st_wait(&waiter.entered, 1000) == 0);
155 st_pause(20);
156 CHECK(pthread_cancel(worker) == 0 && st_wait(&waiter.done, 2000) == 0);
157 void* result = NULL;
158 CHECK(pthread_join(worker, &result) == 0 && result == PTHREAD_CANCELED);
159 worker_active = 0;
160
161 CHECK(sigaction(high, &action, &old_action) == 0);
162 handler_installed = 1;
163 sigemptyset(&one);
164 sigaddset(&one, high);
165 CHECK(pthread_sigmask(SIG_UNBLOCK, &one, NULL) == 0);
166 CHECK(sigqueue(pid, high, (union sigval){.sival_int = 7654}) == 0 &&
167 st_wait(&handled, 2000) == 0);
168 CHECK(handler_info.si_signo == high && handler_info.si_code == SI_QUEUE &&
169 handler_info.si_value.sival_int == 7654 && handler_info.si_pid == pid &&
170 handler_info.si_uid == uid);
171
172out:
173 if (worker_active) {
174 pthread_cancel(worker);
175 if (st_wait(&waiter.done, 1000) == 0)
176 pthread_join(worker, NULL);
177 else
178 _exit(1);
179 }
180 if (masked) {
181 pthread_sigmask(SIG_BLOCK, &set, NULL);
182 while (sigtimedwait(&set, &info, &zero) >= 0) {
183 }
184 if (handler_installed)
185 sigaction(high, &old_action, NULL);
186 pthread_sigmask(SIG_SETMASK, &original, NULL);
187 }
188 if (bad != MAP_FAILED)
189 munmap(bad, 4096);
190 return failed;
191}