(root)/
glibc-2.38/
malloc/
tst-malloc-fork-deadlock.c
       1  /* Test concurrent fork, getline, and fflush (NULL).
       2     Copyright (C) 2016-2023 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  #include <sys/wait.h>
      20  #include <unistd.h>
      21  #include <errno.h>
      22  #include <stdio.h>
      23  #include <pthread.h>
      24  #include <stdbool.h>
      25  #include <stdlib.h>
      26  #include <malloc.h>
      27  #include <time.h>
      28  #include <string.h>
      29  #include <signal.h>
      30  
      31  #include <support/xthread.h>
      32  #include <support/temp_file.h>
      33  #include <support/test-driver.h>
      34  
      35  enum {
      36    /* Number of threads which call fork.  */
      37    fork_thread_count = 4,
      38    /* Number of threads which call getline (and, indirectly,
      39       malloc).  */
      40    read_thread_count = 8,
      41  };
      42  
      43  static bool termination_requested;
      44  
      45  static void *
      46  fork_thread_function (void *closure)
      47  {
      48    while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
      49      {
      50        pid_t pid = fork ();
      51        if (pid < 0)
      52          {
      53            printf ("error: fork: %m\n");
      54            abort ();
      55          }
      56        else if (pid == 0)
      57          _exit (17);
      58  
      59        int status;
      60        if (waitpid (pid, &status, 0) < 0)
      61          {
      62            printf ("error: waitpid: %m\n");
      63            abort ();
      64          }
      65        if (!WIFEXITED (status) || WEXITSTATUS (status) != 17)
      66          {
      67            printf ("error: waitpid returned invalid status: %d\n", status);
      68            abort ();
      69          }
      70      }
      71    return NULL;
      72  }
      73  
      74  static char *file_to_read;
      75  
      76  static void *
      77  read_thread_function (void *closure)
      78  {
      79    FILE *f = fopen (file_to_read, "r");
      80    if (f == NULL)
      81      {
      82        printf ("error: fopen (%s): %m\n", file_to_read);
      83        abort ();
      84      }
      85  
      86    while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
      87      {
      88        rewind (f);
      89        char *line = NULL;
      90        size_t line_allocated = 0;
      91        ssize_t ret = getline (&line, &line_allocated, f);
      92        if (ret < 0)
      93          {
      94            printf ("error: getline: %m\n");
      95            abort ();
      96          }
      97        free (line);
      98      }
      99    fclose (f);
     100  
     101    return NULL;
     102  }
     103  
     104  static void *
     105  flushall_thread_function (void *closure)
     106  {
     107    while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
     108      if (fflush (NULL) != 0)
     109        {
     110          printf ("error: fflush (NULL): %m\n");
     111          abort ();
     112        }
     113    return NULL;
     114  }
     115  
     116  static void
     117  create_threads (pthread_t *threads, size_t count, void *(*func) (void *))
     118  {
     119    for (size_t i = 0; i < count; ++i)
     120      threads[i] = xpthread_create (NULL, func, NULL);
     121  }
     122  
     123  static void
     124  join_threads (pthread_t *threads, size_t count)
     125  {
     126    for (size_t i = 0; i < count; ++i)
     127      xpthread_join (threads[i]);
     128  }
     129  
     130  /* Create a file which consists of a single long line, and assigns
     131     file_to_read.  The hope is that this triggers an allocation in
     132     getline which needs a lock.  */
     133  static void
     134  create_file_with_large_line (void)
     135  {
     136    int fd = create_temp_file ("bug19431-large-line", &file_to_read);
     137    if (fd < 0)
     138      {
     139        printf ("error: create_temp_file: %m\n");
     140        abort ();
     141      }
     142    FILE *f = fdopen (fd, "w+");
     143    if (f == NULL)
     144      {
     145        printf ("error: fdopen: %m\n");
     146        abort ();
     147      }
     148    for (int i = 0; i < 50000; ++i)
     149      fputc ('x', f);
     150    fputc ('\n', f);
     151    if (ferror (f))
     152      {
     153        printf ("error: fputc: %m\n");
     154        abort ();
     155      }
     156    if (fclose (f) != 0)
     157      {
     158        printf ("error: fclose: %m\n");
     159        abort ();
     160      }
     161  }
     162  
     163  static int
     164  do_test (void)
     165  {
     166    /* Make sure that we do not exceed the arena limit with the number
     167       of threads we configured.  */
     168    if (mallopt (M_ARENA_MAX, 400) == 0)
     169      {
     170        printf ("error: mallopt (M_ARENA_MAX) failed\n");
     171        return 1;
     172      }
     173  
     174    /* Leave some room for shutting down all threads gracefully.  */
     175    int timeout = 3;
     176    if (timeout > DEFAULT_TIMEOUT)
     177      timeout = DEFAULT_TIMEOUT - 1;
     178  
     179    create_file_with_large_line ();
     180  
     181    pthread_t fork_threads[fork_thread_count];
     182    create_threads (fork_threads, fork_thread_count, fork_thread_function);
     183    pthread_t read_threads[read_thread_count];
     184    create_threads (read_threads, read_thread_count, read_thread_function);
     185    pthread_t flushall_threads[1];
     186    create_threads (flushall_threads, 1, flushall_thread_function);
     187  
     188    struct timespec ts = {timeout, 0};
     189    if (nanosleep (&ts, NULL))
     190      {
     191        printf ("error: error: nanosleep: %m\n");
     192        abort ();
     193      }
     194  
     195    __atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED);
     196  
     197    join_threads (flushall_threads, 1);
     198    join_threads (read_threads, read_thread_count);
     199    join_threads (fork_threads, fork_thread_count);
     200  
     201    free (file_to_read);
     202  
     203    return 0;
     204  }
     205  
     206  #include <support/test-driver.c>