tst-mallocfork3.c 6.2 KB

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