14#include <sys/syscall.h>
16static volatile sig_atomic_t delivered_signals;
17static volatile sig_atomic_t delivered_process_signals;
18static volatile sig_atomic_t delivered_private_signals;
19static volatile sig_atomic_t delivered_sibling_signals;
20static volatile sig_atomic_t invalid_signal_info;
24 pthread_mutex_t mutex;
25 pthread_cond_t condition;
31 volatile int read_ready;
32 volatile int condition_ready;
33 volatile int main_ready;
34 volatile int read_done;
35 volatile int condition_done;
36 volatile int race_start;
37 volatile int clone_ready;
38 volatile int signals_queued;
39 volatile int sibling_mask_checked;
44static void* fresh_thread(
void* argument) {
48static void caught_signal(
int number) {
52static void pending_signal(
int number, siginfo_t* info,
void* context) {
53 const int saved_errno = errno;
55 if (number == SIGHUP) {
56 ++delivered_sibling_signals;
60 if (number != SIGUSR1 || !info || info->si_signo != SIGUSR1 || info->si_errno ||
61 info->si_pid != getpid() || info->si_uid != getuid()) {
62 invalid_signal_info = 1;
63 }
else if (info->si_code == SI_USER) {
64 ++delivered_process_signals;
65 }
else if (info->si_code == SI_TKILL) {
66 ++delivered_private_signals;
68 invalid_signal_info = 1;
74static int pending_delivery_contract(
const sigset_t* original,
int expect_private) {
75 struct sigaction action = {.sa_sigaction = pending_signal, .sa_flags = SA_SIGINFO};
76 sigset_t unblocked = *original;
77 const long long start = test_milliseconds();
78 if (start < 0 || sigemptyset(&action.sa_mask) || sigaction(SIGUSR1, &action, 0) ||
79 sigaction(SIGHUP, &action, 0) || sigdelset(&unblocked, SIGUSR1) ||
80 sigdelset(&unblocked, SIGHUP) || pthread_sigmask(SIG_SETMASK, &unblocked, 0)) {
83 const int expected = 1 + expect_private;
84 while (delivered_signals < expected) {
85 const long long now = test_milliseconds();
86 if (now < 0 || now - start >= 3000) {
91 for (
int grace = 0; grace < 4; ++grace) {
94 if (pthread_sigmask(SIG_SETMASK, original, 0)) {
97 if (delivered_signals != expected || delivered_process_signals != 1 ||
98 delivered_private_signals != expect_private || invalid_signal_info ||
99 delivered_sibling_signals) {
100 fprintf(stderr,
"pending delivery: total=%d process=%d private=%d sibling=%d invalid=%d\n",
101 delivered_signals, delivered_process_signals, delivered_private_signals,
102 delivered_sibling_signals, invalid_signal_info);
108static int set_exec_signals(
int blocked_signal,
int pending_signals) {
109 struct sigaction caught = {.sa_handler = caught_signal, .sa_flags = SA_RESTART};
110 struct sigaction ignored = {.sa_handler = SIG_IGN};
112 return sigemptyset(&caught.sa_mask) || sigemptyset(&ignored.sa_mask) ||
113 sigaction(SIGUSR1, &caught, 0) || sigaction(SIGUSR2, &ignored, 0) || sigemptyset(&mask) ||
114 sigaddset(&mask, blocked_signal) ||
115 (pending_signals && (sigaction(SIGHUP, &caught, 0) || sigaddset(&mask, SIGUSR1) ||
116 sigaddset(&mask, SIGHUP))) ||
117 pthread_sigmask(SIG_SETMASK, &mask, 0);
120static void* blocked_read(
void* argument) {
123 probe->read_tid = syscall(SYS_gettid);
124 __atomic_store_n(&probe->read_ready, 1, __ATOMIC_RELEASE);
125 if (probe->pending_signals) {
127 if (test_wait_flag(&probe->signals_queued) || pthread_sigmask(SIG_SETMASK, 0, &mask) ||
128 sigismember(&mask, SIGHUP) != 1 || sigismember(&mask, SIGUSR1) != 1) {
131 __atomic_store_n(&probe->sibling_mask_checked, 1, __ATOMIC_RELEASE);
133 const int valid = read(probe->gate[0], &token, 1) == 1 && token ==
'x';
134 __atomic_store_n(&probe->read_done, valid ? 1 : -1, __ATOMIC_RELEASE);
138static void* blocked_condition(
void* argument) {
140 if (pthread_mutex_lock(&probe->mutex)) {
143 probe->condition_tid = syscall(SYS_gettid);
144 __atomic_store_n(&probe->condition_ready, 1, __ATOMIC_RELEASE);
145 while (!probe->stop) {
146 if (pthread_cond_wait(&probe->condition, &probe->mutex)) {
150 pthread_mutex_unlock(&probe->mutex);
151 __atomic_store_n(&probe->condition_done, 1, __ATOMIC_RELEASE);
155static int replace_image(
struct exec_probe* probe,
int blocked_signal,
int racing) {
156 char inherited[24], cloexec[24], expected_mask[24], read_tid[24], condition_tid[24], caller[24];
157 snprintf(inherited,
sizeof(inherited),
"%d", probe->inherited);
158 snprintf(cloexec,
sizeof(cloexec),
"%d", probe->cloexec);
159 snprintf(expected_mask,
sizeof(expected_mask),
"%d", blocked_signal);
160 snprintf(read_tid,
sizeof(read_tid),
"%ld", probe->read_tid);
161 snprintf(condition_tid,
sizeof(condition_tid),
"%ld", probe->condition_tid);
162 snprintf(caller,
sizeof(caller),
"%ld", syscall(SYS_gettid));
163 char* arguments[] = {(
char*)probe->program,
164 (
char*)
"--exec-image",
173 arguments[1] = (
char*)
"--exec-race-image";
175 }
else if (probe->pending_signals) {
176 arguments[1] = (
char*)
"--exec-pending-image";
179 execv(probe->program, arguments);
180 if (!racing || errno != EAGAIN) {
185 fprintf(stderr,
"threaded exec: execv errno=%d\n", errno);
189static void* exec_worker(
void* argument) {
191 if (set_exec_signals(SIGTERM, probe->pending_signals) || test_wait_flag(&probe->main_ready) ||
192 pthread_mutex_lock(&probe->mutex)) {
197 pthread_mutex_unlock(&probe->mutex);
198 _exit(replace_image(probe, SIGTERM, 0));
201static void* competing_exec(
void* argument) {
203 if (test_wait_flag(&probe->race_start) || test_wait_flag(&probe->clone_ready)) {
206 _exit(replace_image(probe, SIGUSR1, 1));
209static void* competing_clone(
void* argument) {
211 if (test_wait_flag(&probe->race_start)) {
214 for (
int attempt = 0; attempt < 16; ++attempt) {
216 const int error = pthread_create(&
worker, 0, fresh_thread, probe);
217 if (error && error != EAGAIN) {
221 if (!error && (pthread_join(
worker, &result) || result != probe)) {
225 __atomic_store_n(&probe->clone_ready, 1, __ATOMIC_RELEASE);
231static int race_contract(
struct exec_probe* probe) {
232 pthread_t first, second, cloner;
233 if (pthread_create(&first, 0, competing_exec, probe) ||
234 pthread_create(&second, 0, competing_exec, probe) ||
235 pthread_create(&cloner, 0, competing_clone, probe)) {
238 __atomic_store_n(&probe->race_start, 1, __ATOMIC_RELEASE);
245 char*
const arguments[] = {(
char*)probe->program, 0};
247 execv(
"/signal-exec-test-does-not-exist", arguments);
248 if (errno != ENOENT) {
253 snprintf(path,
sizeof(path),
"/tmp/signal-exec-invalid-%ld", (
long)getpid());
254 const int fixture = open(path, O_CREAT | O_EXCL | O_WRONLY, 0700);
258 static const char contents[] =
"not an executable\n";
259 if (write(fixture, contents,
sizeof(contents) - 1) !=
sizeof(contents) - 1 || close(fixture)) {
264 execv(path, arguments);
265 const int error = errno;
266 if (unlink(path) || error != ENOEXEC) {
270 struct sigaction current;
272 if (sigaction(SIGUSR1, 0, ¤t) || current.sa_handler != caught_signal ||
273 !(current.sa_flags & SA_RESTART) || pthread_sigmask(SIG_SETMASK, 0, &mask) ||
274 sigismember(&mask, SIGUSR1) != 1 || fcntl(probe->cloexec, F_GETFD) != FD_CLOEXEC ||
275 __atomic_load_n(&probe->read_done, __ATOMIC_ACQUIRE) ||
276 __atomic_load_n(&probe->condition_done, __ATOMIC_ACQUIRE)) {
279 if (write(probe->gate[1],
"x", 1) != 1 || pthread_mutex_lock(&probe->mutex)) {
283 pthread_cond_broadcast(&probe->condition);
284 pthread_mutex_unlock(&probe->mutex);
285 if (test_wait_flag(&probe->read_done) || test_wait_flag(&probe->condition_done) ||
286 pthread_join(
reader, 0) || pthread_join(
waiter, 0) || probe->read_done != 1 ||
287 probe->condition_done != 1) {
290 return close(probe->gate[0]) || close(probe->gate[1]) || close(probe->inherited) ||
291 close(probe->cloexec) || pthread_cond_destroy(&probe->condition) ||
292 pthread_mutex_destroy(&probe->mutex)
300 if (pthread_kill(pthread_self(), SIGUSR1) || pthread_kill(
reader, SIGUSR1) ||
301 pthread_kill(
reader, SIGHUP) || kill(getpid(), SIGUSR1)) {
304 __atomic_store_n(&probe->signals_queued, 1, __ATOMIC_RELEASE);
305 if (test_wait_flag(&probe->sibling_mask_checked)) {
309 if (__atomic_load_n(&probe->read_done, __ATOMIC_ACQUIRE)) {
316 volatile int exec_ready;
317 volatile int exit_ready;
321static void* race_exec_with_exit(
void* argument) {
323 char*
const arguments[] = {(
char*)probe->program, (
char*)
"--exec-exit-image", 0};
324 __atomic_store_n(&probe->exec_ready, 1, __ATOMIC_RELEASE);
325 if (test_wait_flag(&probe->start)) {
329 execv(probe->program, arguments);
330 if (errno != EAGAIN) {
337static void* race_exit_with_exec(
void* argument) {
339 __atomic_store_n(&probe->exit_ready, 1, __ATOMIC_RELEASE);
340 if (test_wait_flag(&probe->start)) {
346static int exec_exit_contract(
const char* program) {
348 pthread_t exec_worker, exit_worker;
349 if (pthread_create(&exec_worker, 0, race_exec_with_exit, &probe) ||
350 pthread_create(&exit_worker, 0, race_exit_with_exec, &probe) ||
351 test_wait_flag(&probe.exec_ready) || test_wait_flag(&probe.exit_ready)) {
354 __atomic_store_n(&probe.start, 1, __ATOMIC_RELEASE);
360int exec_contract(
const char* program,
const char* name) {
361 if (!strcmp(name,
"exec-exit-race")) {
362 return exec_exit_contract(program);
364 struct exec_probe probe = {.program = program,
365 .mutex = PTHREAD_MUTEX_INITIALIZER,
366 .condition = PTHREAD_COND_INITIALIZER,
367 .pending_signals = !strncmp(name,
"exec-pending-", 13)};
368 if (set_exec_signals(SIGUSR1, probe.pending_signals) || pipe(probe.gate)) {
371 probe.inherited = open(
"/dev/null", O_RDONLY);
372 probe.cloexec = open(
"/dev/null", O_RDONLY | O_CLOEXEC);
373 if (probe.inherited < 0 || probe.cloexec < 0) {
377 if (pthread_create(&
reader, 0, blocked_read, &probe) ||
378 pthread_create(&
waiter, 0, blocked_condition, &probe) || test_wait_flag(&probe.read_ready) ||
379 test_wait_flag(&probe.condition_ready) || pthread_mutex_lock(&probe.mutex)) {
382 pthread_mutex_unlock(&probe.mutex);
384 if (probe.read_tid <= 0 || probe.condition_tid <= 0 ||
385 __atomic_load_n(&probe.read_done, __ATOMIC_ACQUIRE) ||
386 __atomic_load_n(&probe.condition_done, __ATOMIC_ACQUIRE)) {
389 if (!strcmp(name,
"exec-failure")) {
392 if (!strcmp(name,
"exec-main") || !strcmp(name,
"exec-pending-main")) {
393 if (probe.pending_signals) {
394 queue_exec_signals(&probe,
reader);
396 return replace_image(&probe, SIGUSR1, 0);
398 if (!strcmp(name,
"exec-race")) {
399 return race_contract(&probe);
402 if (pthread_create(&
worker, 0, exec_worker, &probe) || pthread_mutex_lock(&probe.mutex)) {
405 if (probe.pending_signals) {
406 queue_exec_signals(&probe,
reader);
408 __atomic_store_n(&probe.main_ready, 1, __ATOMIC_RELEASE);
410 if (pthread_cond_wait(&probe.condition, &probe.mutex)) {
416int exec_image_contract(
int argc,
char* argv[]) {
417 if (!strcmp(argv[1],
"--exec-exit-image")) {
418 return argc == 2 && syscall(SYS_gettid) == getpid() ? 0 : 112;
420 const int racing = !strcmp(argv[1],
"--exec-race-image");
421 const int pending_signals = !strcmp(argv[1],
"--exec-pending-image");
422 if (argc != (racing ? 5 : 8) || syscall(SYS_gettid) != getpid()) {
425 const int inherited = atoi(argv[2]);
426 const int cloexec = atoi(argv[3]);
427 const int blocked_signal = atoi(argv[4]);
428 struct sigaction action;
430 if (sigaction(SIGUSR1, 0, &action) || action.sa_handler != SIG_DFL ||
431 sigaction(SIGUSR2, 0, &action) || action.sa_handler != SIG_IGN ||
432 pthread_sigmask(SIG_SETMASK, 0, &mask) || sigismember(&mask, blocked_signal) != 1 ||
433 sigismember(&mask, SIGTERM) != (blocked_signal == SIGTERM) ||
434 sigismember(&mask, SIGUSR1) != (pending_signals || blocked_signal == SIGUSR1)) {
437 if (pending_signals) {
438 if (sigaction(SIGHUP, 0, &action) || action.sa_handler != SIG_DFL ||
439 sigismember(&mask, SIGHUP) != 1) {
442 const int delivery_result = pending_delivery_contract(&mask, blocked_signal == SIGUSR1);
443 if (delivery_result) {
444 return delivery_result;
448 if (fcntl(inherited, F_GETFD) != 0 || read(inherited, &
byte, 1) != 0) {
452 if (fcntl(cloexec, F_GETFD) != -1 || errno != EBADF) {
455 for (
int i = 5; i < argc; ++i) {
456 const long old_tid = atol(argv[i]);
457 if (old_tid == getpid()) {
461 if (syscall(SYS_tgkill, getpid(), old_tid, 0) != -1 || errno != ESRCH) {
467 if (pthread_create(&
worker, 0, fresh_thread, &mask) || pthread_join(
worker, &result) ||
468 result != &mask || close(inherited)) {