(root)/
glibc-2.38/
sysdeps/
pthread/
tst-create-detached.c
       1  /* Bug 20116: Test rapid creation of detached threads.
       2     Copyright (C) 2017-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
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the 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  /* The goal of the test is to trigger a failure if the parent touches
      20     any part of the thread descriptor after the detached thread has
      21     exited.  We test this by creating many detached threads with large
      22     stacks.  The stacks quickly fill the the stack cache and subsequent
      23     threads will start to cause the thread stacks to be immediately
      24     unmapped to satisfy the stack cache max.  With the stacks being
      25     unmapped the parent's read of any part of the thread descriptor will
      26     trigger a segfault.  That segfault is what we are trying to cause,
      27     since any segfault is a defect in the implementation.  */
      28  
      29  #include <pthread.h>
      30  #include <stdio.h>
      31  #include <errno.h>
      32  #include <unistd.h>
      33  #include <stdbool.h>
      34  #include <sys/resource.h>
      35  #include <support/xthread.h>
      36  
      37  /* Number of threads to create.  */
      38  enum { threads_to_create = 100000 };
      39  
      40  /* Number of threads which should spawn other threads.  */
      41  enum { creator_threads  = 2 };
      42  
      43  /* Counter of threads created so far.  This is incremented by all the
      44     running creator threads.  */
      45  static unsigned threads_created;
      46  
      47  /* Thread callback which does nothing, so that the thread exits
      48     immediatedly.  */
      49  static void *
      50  do_nothing (void *arg)
      51  {
      52    return NULL;
      53  }
      54  
      55  /* Attribute indicating that the thread should be created in a detached
      56     fashion.  */
      57  static pthread_attr_t detached;
      58  
      59  /* Barrier to synchronize initialization.  */
      60  static pthread_barrier_t barrier;
      61  
      62  static void *
      63  creator_thread (void *arg)
      64  {
      65    int ret;
      66    xpthread_barrier_wait (&barrier);
      67  
      68    while (true)
      69      {
      70        pthread_t thr;
      71        /* Thread creation will fail if the kernel does not free old
      72  	 threads quickly enough, so we do not report errors.  */
      73        ret = pthread_create (&thr, &detached, do_nothing, NULL);
      74        if (ret == 0 && __atomic_add_fetch (&threads_created, 1, __ATOMIC_SEQ_CST)
      75            >= threads_to_create)
      76          break;
      77      }
      78  
      79    return NULL;
      80  }
      81  
      82  static int
      83  do_test (void)
      84  {
      85    /* Limit the size of the process, so that memory allocation will
      86       fail without impacting the entire system.  */
      87    {
      88      struct rlimit limit;
      89      if (getrlimit (RLIMIT_AS, &limit) != 0)
      90        {
      91          printf ("FAIL: getrlimit (RLIMIT_AS) failed: %m\n");
      92          return 1;
      93        }
      94      /* This limit, 800MB, is just a heuristic. Any value can be
      95         picked.  */
      96      long target = 800 * 1024 * 1024;
      97      if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target)
      98        {
      99          limit.rlim_cur = target;
     100          if (setrlimit (RLIMIT_AS, &limit) != 0)
     101            {
     102              printf ("FAIL: setrlimit (RLIMIT_AS) failed: %m\n");
     103              return 1;
     104            }
     105        }
     106    }
     107  
     108    xpthread_attr_init (&detached);
     109  
     110    xpthread_attr_setdetachstate (&detached, PTHREAD_CREATE_DETACHED);
     111  
     112    /* A large thread stack seems beneficial for reproducing a race
     113       condition in detached thread creation.  The goal is to reach the
     114       limit of the runtime thread stack cache such that the detached
     115       thread's stack is unmapped after exit and causes a segfault when
     116       the parent reads the thread descriptor data stored on the the
     117       unmapped stack.  */
     118    xpthread_attr_setstacksize (&detached, 16 * 1024 * 1024);
     119  
     120    xpthread_barrier_init (&barrier, NULL, creator_threads);
     121  
     122    pthread_t threads[creator_threads];
     123  
     124    for (int i = 0; i < creator_threads; ++i)
     125      threads[i] = xpthread_create (NULL, creator_thread, NULL);
     126  
     127    for (int i = 0; i < creator_threads; ++i)
     128      xpthread_join (threads[i]);
     129  
     130    xpthread_attr_destroy (&detached);
     131  
     132    xpthread_barrier_destroy (&barrier);
     133  
     134    return 0;
     135  }
     136  
     137  #define TIMEOUT 100
     138  #include <support/test-driver.c>