Blame nptl/tst-create-detached.c

Packit 6c4009
/* Bug 20116: Test rapid creation of detached threads.
Packit 6c4009
   Copyright (C) 2017-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; see the file COPYING.LIB.  If
Packit 6c4009
   not, see <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
/* The goal of the test is to trigger a failure if the parent touches
Packit 6c4009
   any part of the thread descriptor after the detached thread has
Packit 6c4009
   exited.  We test this by creating many detached threads with large
Packit 6c4009
   stacks.  The stacks quickly fill the the stack cache and subsequent
Packit 6c4009
   threads will start to cause the thread stacks to be immediately
Packit 6c4009
   unmapped to satisfy the stack cache max.  With the stacks being
Packit 6c4009
   unmapped the parent's read of any part of the thread descriptor will
Packit 6c4009
   trigger a segfault.  That segfault is what we are trying to cause,
Packit 6c4009
   since any segfault is a defect in the implementation.  */
Packit 6c4009
Packit 6c4009
#include <pthread.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <sys/resource.h>
Packit 6c4009
#include <support/xthread.h>
Packit 6c4009
Packit 6c4009
/* Number of threads to create.  */
Packit 6c4009
enum { threads_to_create = 100000 };
Packit 6c4009
Packit 6c4009
/* Number of threads which should spawn other threads.  */
Packit 6c4009
enum { creator_threads  = 2 };
Packit 6c4009
Packit 6c4009
/* Counter of threads created so far.  This is incremented by all the
Packit 6c4009
   running creator threads.  */
Packit 6c4009
static unsigned threads_created;
Packit 6c4009
Packit 6c4009
/* Thread callback which does nothing, so that the thread exits
Packit 6c4009
   immediatedly.  */
Packit 6c4009
static void *
Packit 6c4009
do_nothing (void *arg)
Packit 6c4009
{
Packit 6c4009
  return NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Attribute indicating that the thread should be created in a detached
Packit 6c4009
   fashion.  */
Packit 6c4009
static pthread_attr_t detached;
Packit 6c4009
Packit 6c4009
/* Barrier to synchronize initialization.  */
Packit 6c4009
static pthread_barrier_t barrier;
Packit 6c4009
Packit 6c4009
static void *
Packit 6c4009
creator_thread (void *arg)
Packit 6c4009
{
Packit 6c4009
  int ret;
Packit 6c4009
  xpthread_barrier_wait (&barrier);
Packit 6c4009
Packit 6c4009
  while (true)
Packit 6c4009
    {
Packit 6c4009
      pthread_t thr;
Packit 6c4009
      /* Thread creation will fail if the kernel does not free old
Packit 6c4009
	 threads quickly enough, so we do not report errors.  */
Packit 6c4009
      ret = pthread_create (&thr, &detached, do_nothing, NULL);
Packit 6c4009
      if (ret == 0 && __atomic_add_fetch (&threads_created, 1, __ATOMIC_SEQ_CST)
Packit 6c4009
          >= threads_to_create)
Packit 6c4009
        break;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
do_test (void)
Packit 6c4009
{
Packit 6c4009
  /* Limit the size of the process, so that memory allocation will
Packit 6c4009
     fail without impacting the entire system.  */
Packit 6c4009
  {
Packit 6c4009
    struct rlimit limit;
Packit 6c4009
    if (getrlimit (RLIMIT_AS, &limit) != 0)
Packit 6c4009
      {
Packit 6c4009
        printf ("FAIL: getrlimit (RLIMIT_AS) failed: %m\n");
Packit 6c4009
        return 1;
Packit 6c4009
      }
Packit 6c4009
    /* This limit, 800MB, is just a heuristic. Any value can be
Packit 6c4009
       picked.  */
Packit 6c4009
    long target = 800 * 1024 * 1024;
Packit 6c4009
    if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target)
Packit 6c4009
      {
Packit 6c4009
        limit.rlim_cur = target;
Packit 6c4009
        if (setrlimit (RLIMIT_AS, &limit) != 0)
Packit 6c4009
          {
Packit 6c4009
            printf ("FAIL: setrlimit (RLIMIT_AS) failed: %m\n");
Packit 6c4009
            return 1;
Packit 6c4009
          }
Packit 6c4009
      }
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
  xpthread_attr_init (&detached);
Packit 6c4009
Packit 6c4009
  xpthread_attr_setdetachstate (&detached, PTHREAD_CREATE_DETACHED);
Packit 6c4009
Packit 6c4009
  /* A large thread stack seems beneficial for reproducing a race
Packit 6c4009
     condition in detached thread creation.  The goal is to reach the
Packit 6c4009
     limit of the runtime thread stack cache such that the detached
Packit 6c4009
     thread's stack is unmapped after exit and causes a segfault when
Packit 6c4009
     the parent reads the thread descriptor data stored on the the
Packit 6c4009
     unmapped stack.  */
Packit 6c4009
  xpthread_attr_setstacksize (&detached, 16 * 1024 * 1024);
Packit 6c4009
Packit 6c4009
  xpthread_barrier_init (&barrier, NULL, creator_threads);
Packit 6c4009
Packit 6c4009
  pthread_t threads[creator_threads];
Packit 6c4009
Packit 6c4009
  for (int i = 0; i < creator_threads; ++i)
Packit 6c4009
    threads[i] = xpthread_create (NULL, creator_thread, NULL);
Packit 6c4009
Packit 6c4009
  for (int i = 0; i < creator_threads; ++i)
Packit 6c4009
    xpthread_join (threads[i]);
Packit 6c4009
Packit 6c4009
  xpthread_attr_destroy (&detached);
Packit 6c4009
Packit 6c4009
  xpthread_barrier_destroy (&barrier);
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#define TIMEOUT 100
Packit 6c4009
#include <support/test-driver.c>