11#include <sys/membarrier.h>
12#include <sys/syscall.h>
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,
34 "Linux amd64 kernel sigaction must be 32 bytes");
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)) {
46static int raw_signal_contract(
void) {
49 if (SIGRTMIN != first_realtime_signal || SIGRTMAX != last_realtime_signal) {
53 if (sigemptyset(&public_set)) {
56 for (
int signal = musl_sig_timer; signal <= musl_sig_synccall; ++signal) {
58 if (sigaddset(&public_set, signal) != -1 || errno != EINVAL) {
62 if (sigdelset(&public_set, signal) != -1 || errno != EINVAL) {
66 if (sigfillset(&public_set)) {
69 for (
int signal = musl_sig_timer; signal <= musl_sig_synccall; ++signal) {
70 if (sigismember(&public_set, signal)) {
75 for (
int signal = musl_sig_timer; signal <= musl_sig_synccall; ++signal) {
76 struct sigaction public_action = {0};
78 if (sigaction(signal, 0, &public_action) != -1 || errno != EINVAL) {
79 return 10 + signal - musl_sig_timer;
84 .mask = UINT64_C(1) << (SIGUSR1 - 1),
87 .handler = UINT64_MAX,
89 .restorer = UINT64_MAX,
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;
98 if (syscall(SYS_rt_sigaction, signal, 0, ¤t, 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;
104 if (signal == musl_sig_timer && syscall(SYS_tkill, syscall(SYS_gettid), musl_sig_timer)) {
108 if (syscall(SYS_rt_sigaction, signal, &previous, 0, kernel_sigset_size)) {
109 return 41 + signal - musl_sig_timer;
114 if (syscall(SYS_rt_sigaction, first_unsupported_signal, 0, 0, kernel_sigset_size) != -1 ||
119 if (syscall(SYS_rt_sigaction, musl_sig_timer, 0, 0, kernel_sigset_size * 2) != -1 ||
124 if (kill(getpid(), musl_sig_timer) != -1 || errno != EINVAL) {
130static volatile int realtime_received;
132static void realtime_handler(
int signal) {
133 __atomic_store_n(&realtime_received, signal, __ATOMIC_RELEASE);
136static int realtime_signal_contract(
void) {
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)) {
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) {
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) {
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) {
166 if (sigaction(signal, &previous, 0)) {
172 if (syscall(SYS_tkill, syscall(SYS_gettid), first_unsupported_signal) != -1 || errno != EINVAL) {
176 if (syscall(SYS_tgkill, getpid(), syscall(SYS_gettid), first_unsupported_signal) != -1 ||
181 if (kill(getpid(), first_unsupported_signal) != -1 || errno != EINVAL) {
189 volatile int entered;
192static void* cancellation_target(
void* parameter) {
195 __atomic_store_n(&probe->entered, 1, __ATOMIC_RELEASE);
196 (void)read(probe->descriptor, &value,
sizeof(value));
200static int cancellation_contract(
void) {
204 if (pipe(descriptors)) {
209 .descriptor = descriptors[0],
213 if (pthread_create(&thread, 0, cancellation_target, &probe) || wait_for_value(&probe.entered)) {
218 int error = pthread_cancel(thread);
220 dprintf(STDERR_FILENO,
"private-signal cancel: pthread_cancel=%d\n", error);
223 error = pthread_join(thread, &result);
225 dprintf(STDERR_FILENO,
"private-signal cancel: pthread_join=%d\n", error);
228 if (result != PTHREAD_CANCELED) {
229 dprintf(STDERR_FILENO,
"private-signal cancel: result=%p expected=%p\n", result,
233 if (close(descriptors[0]) || close(descriptors[1])) {
244static void* membarrier_target(
void* parameter) {
246 __atomic_store_n(&probe->ready, 1, __ATOMIC_RELEASE);
247 while (!__atomic_load_n(&probe->stop, __ATOMIC_ACQUIRE)) {
253static int membarrier_contract(
void) {
258 if (pthread_create(&thread, 0, membarrier_target, &probe) || wait_for_value(&probe.ready)) {
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)) {
275static int run_bounded(
int (*test)(
void)) {
276 pid_t child = fork();
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);
290 if (waited < 0 && errno != EINTR) {
296 (void)kill(child, SIGKILL);
297 (void)waitpid(child, &status, 0);
302 int result = run_bounded(raw_signal_contract);
304 printf(
"PRIVATE-SIGNAL-TEST: FAIL raw=%d\n", result);
307 result = run_bounded(realtime_signal_contract);
309 printf(
"PRIVATE-SIGNAL-TEST: FAIL realtime=%d\n", result);
312 result = run_bounded(cancellation_contract);
314 printf(
"PRIVATE-SIGNAL-TEST: FAIL cancel=%d\n", result);
317 result = run_bounded(membarrier_contract);
319 printf(
"PRIVATE-SIGNAL-TEST: FAIL membarrier=%d\n", result);
323 puts(
"PRIVATE-SIGNAL-TEST: PASS");