cmake: Set public include dir for socket_wrapper_noop target
[socket_wrapper.git] / tests / test_fork_thread_deadlock.c
1 #include "config.h"
2
3 #include <stdarg.h>
4 #include <stddef.h>
5 #include <setjmp.h>
6 #include <cmocka.h>
7
8 #include <errno.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
11 #include <sys/wait.h>
12 #include <signal.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <pthread.h>
17
18 /*
19  * This reproduces and issue if we get a signal after the pthread_atfork()
20  * prepare function of socket wrapper has been called.
21  *
22  * The order how pthread_atfork() handlers are set up is:
23  *   -> application
24  *   -> preloaded libraries
25  *   -> libraries
26  *
27  * We have a library called thread_deadlock.
28  *
29  * This library registers a thread_deadlock_prepare() function via
30  * pthread_atfork().
31  *
32  * So pthread_atfork() registers the prepare function in the follow order:
33  *   -> swrap_thread_prepare()
34  *   -> thread_deadlock_prepare()
35  *
36  * In this test we fork and the swrap_thread_prepare() locks the mutex for
37  * symbol binding.
38  * Then thread_deadlock_prepare() is called which sends a signal to the parent
39  * process of this test. The signal triggers the signal handler below.
40  *
41  * When we call write() in the signal handler, we will try to bind the libc symbol
42  * and want to lock the symbol binding mutex. As it is already locked we run into
43  * a deadlock.
44  */
45
46 static void test_swrap_signal_handler(int signum)
47 {
48         ssize_t w;
49         fprintf(stderr, "PID: %u, SIGNUM: %d\n", (unsigned int)getpid(), signum);
50         w = write(1, "DEADLOCK?\n", 10);
51         fprintf(stderr, "WRITE: %zu\n", w);
52 }
53
54 static void test_swrap_fork_pthread(void **state)
55 {
56         pid_t pid;
57         struct sigaction act = {
58                 .sa_handler = test_swrap_signal_handler,
59                 .sa_flags = 0,
60         };
61
62         (void)state; /* unused */
63
64         sigemptyset(&act.sa_mask);
65         sigaction(SIGUSR1, &act, NULL);
66
67         pid = fork();
68         assert_return_code(pid, errno);
69
70         /* child */
71         if (pid == 0) {
72                 exit(0);
73         }
74
75         /* parent */
76         if (pid > 0) {
77                 pid_t child_pid;
78                 int wstatus = -1;
79
80                 child_pid = waitpid(-1, &wstatus, 0);
81                 assert_return_code(child_pid, errno);
82
83                 assert_true(WIFEXITED(wstatus));
84
85                 assert_int_equal(WEXITSTATUS(wstatus), 0);
86         }
87 }
88
89 int main(void)
90 {
91         int rc;
92
93         const struct CMUnitTest swrap_tests[] = {
94                 cmocka_unit_test(test_swrap_fork_pthread),
95         };
96
97         rc = cmocka_run_group_tests(swrap_tests, NULL, NULL);
98
99         return rc;
100 }