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