1 /* Test case for async-signal-safe _Fork (with respect to malloc).
2 Copyright (C) 2021-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation; either version 2.1 of the
8 License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If
17 not, see <https://www.gnu.org/licenses/>. */
18
19 /* This test is similar to tst-mallocfork2.c, but specifically stress
20 the async-signal-safeness of _Fork on multithread environment. */
21
22 #include <array_length.h>
23 #include <errno.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <support/check.h>
28 #include <support/support.h>
29 #include <support/xsignal.h>
30 #include <support/xthread.h>
31 #include <support/xunistd.h>
32 #include <sys/wait.h>
33
34 /* How many malloc objects to keep arond. */
35 enum { malloc_objects = 1009 };
36
37 /* The maximum size of an object. */
38 enum { malloc_maximum_size = 70000 };
39
40 /* How many iterations the test performs before exiting. */
41 enum { iterations = 10000 };
42
43 /* Barrier for synchronization with the threads sending SIGUSR1
44 signals, to make it more likely that the signals arrive during a
45 fork/free/malloc call. */
46 static pthread_barrier_t barrier;
47
48 /* Set to 1 if SIGUSR1 is received. Used to detect a signal during
49 fork/free/malloc. */
50 static volatile sig_atomic_t sigusr1_received;
51
52 /* Periodically set to 1, to indicate that the thread is making
53 progress. Checked by liveness_signal_handler. */
54 static volatile sig_atomic_t progress_indicator = 1;
55
56 /* Set to 1 if an error occurs in the signal handler. */
57 static volatile sig_atomic_t error_indicator = 0;
58
59 static void
sigusr1_handler(int signo)60 sigusr1_handler (int signo)
61 {
62 sigusr1_received = 1;
63
64 /* Perform a fork with a trivial subprocess. */
65 pid_t pid = _Fork ();
66 if (pid == -1)
67 {
68 write_message ("error: fork\n");
69 error_indicator = 1;
70 return;
71 }
72 if (pid == 0)
73 _exit (0);
74 int status;
75 int ret = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
76 if (ret < 0)
77 {
78 write_message ("error: waitpid\n");
79 error_indicator = 1;
80 return;
81 }
82 if (status != 0)
83 {
84 write_message ("error: unexpected exit status from subprocess\n");
85 error_indicator = 1;
86 return;
87 }
88 }
89
90 static void
liveness_signal_handler(int signo)91 liveness_signal_handler (int signo)
92 {
93 if (progress_indicator)
94 progress_indicator = 0;
95 else
96 write_message ("warning: thread seems to be stuck\n");
97 }
98
99 struct signal_send_args
100 {
101 pthread_t target;
102 int signo;
103 bool sleep;
104 };
105 #define SIGNAL_SEND_GET_ARG(arg, field) \
106 (((struct signal_send_args *)(arg))->field)
107
108 /* Send SIGNO to the parent thread. If SLEEP, wait a second between
109 signals, otherwise use barriers to delay sending signals. */
110 static void *
signal_sender(void * args)111 signal_sender (void *args)
112 {
113 int signo = SIGNAL_SEND_GET_ARG (args, signo);
114 bool sleep = SIGNAL_SEND_GET_ARG (args, sleep);
115
116 pthread_t target = SIGNAL_SEND_GET_ARG (args, target);
117 while (true)
118 {
119 if (!sleep)
120 xpthread_barrier_wait (&barrier);
121 xpthread_kill (target, signo);
122 if (sleep)
123 usleep (1 * 1000 * 1000);
124 else
125 xpthread_barrier_wait (&barrier);
126 }
127 return NULL;
128 }
129
130 static pthread_t sigusr1_sender[5];
131 static pthread_t sigusr2_sender;
132
133 static int
do_test(void)134 do_test (void)
135 {
136 xsignal (SIGUSR1, sigusr1_handler);
137 xsignal (SIGUSR2, liveness_signal_handler);
138
139 pthread_t self = pthread_self ();
140
141 struct signal_send_args sigusr2_args = { self, SIGUSR2, true };
142 sigusr2_sender = xpthread_create (NULL, signal_sender, &sigusr2_args);
143
144 /* Send SIGUSR1 signals from several threads. Hopefully, one
145 signal will hit one of the ciritical functions. Use a barrier to
146 avoid sending signals while not running fork/free/malloc. */
147 struct signal_send_args sigusr1_args = { self, SIGUSR1, false };
148 xpthread_barrier_init (&barrier, NULL,
149 array_length (sigusr1_sender) + 1);
150 for (size_t i = 0; i < array_length (sigusr1_sender); ++i)
151 sigusr1_sender[i] = xpthread_create (NULL, signal_sender, &sigusr1_args);
152
153 void *objects[malloc_objects] = {};
154 unsigned int fork_signals = 0;
155 unsigned int free_signals = 0;
156 unsigned int malloc_signals = 0;
157 unsigned int seed = 1;
158 for (int i = 0; i < iterations; ++i)
159 {
160 progress_indicator = 1;
161 int slot = rand_r (&seed) % malloc_objects;
162 size_t size = rand_r (&seed) % malloc_maximum_size;
163
164 /* Occasionally do a fork first, to catch deadlocks there as
165 well (see bug 24161). */
166 bool do_fork = (rand_r (&seed) % 7) == 0;
167
168 xpthread_barrier_wait (&barrier);
169 if (do_fork)
170 {
171 sigusr1_received = 0;
172 pid_t pid = _Fork ();
173 TEST_VERIFY_EXIT (pid != -1);
174 if (sigusr1_received)
175 ++fork_signals;
176 if (pid == 0)
177 _exit (0);
178 int status;
179 int ret = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
180 if (ret < 0)
181 FAIL_EXIT1 ("waitpid: %m");
182 TEST_COMPARE (status, 0);
183 }
184 sigusr1_received = 0;
185 free (objects[slot]);
186 if (sigusr1_received)
187 ++free_signals;
188 sigusr1_received = 0;
189 objects[slot] = malloc (size);
190 if (sigusr1_received)
191 ++malloc_signals;
192 xpthread_barrier_wait (&barrier);
193
194 if (objects[slot] == NULL || error_indicator != 0)
195 {
196 printf ("error: malloc: %m\n");
197 return 1;
198 }
199 }
200
201 /* Clean up allocations. */
202 for (int slot = 0; slot < malloc_objects; ++slot)
203 free (objects[slot]);
204
205 printf ("info: signals received during fork: %u\n", fork_signals);
206 printf ("info: signals received during free: %u\n", free_signals);
207 printf ("info: signals received during malloc: %u\n", malloc_signals);
208
209 return 0;
210 }
211
212 #define TIMEOUT 100
213 #include <support/test-driver.c>
214