[PATCH] pthread_detach: add a comment about a known segfault bug.

Florian Weimer fweimer@redhat.com
Wed Nov 26 11:11:49 GMT 2025


* Paul Pluzhnikov:

> On Tue, Nov 25, 2025 at 11:26 AM Florian Weimer <fweimer@redhat.com> wrote:
>>
>> It looks okay to me, but I haven't seen it reproducing the problem.
>
> Just to set the expectations: the race window is quite small, and our
> reproduction rate on Google infra is about 1 in 25000 executions.
>
> I just ran the original test for 100,000 executions and saw 4
> SIGSEGVs, and 2 failed pthread_detach with error 22 (EINVAL).
>
> The pthread_detach errors seem to indicate a separate bug :-(

I'm not sure?  If there is a synchronization defect, who knows what ends
up at the address where struct pthread is stored?

The trick to make the test start a new iteration triggers the EINVAL
failure much more frequently:

error: xpthread_check_return.c:32: pthread_detach: Invalid argument
*** stack smashing detected ***: terminated

error: xpthread_check_return.c:32: pthread_detach: Invalid argument
error: 1 test failures

Is it really distinct from the segfault bug?

Thanks,
Florian

/* Creates and then detaches a bunch of threads, each of which creates
   and detaches other threads, and so on, up to a certain number of
   total threads.  */

#include <stdatomic.h>
#include <stdint.h>
#include <stdlib.h>
#include <support/xdirent.h>
#include <support/xthread.h>
#include <unistd.h>

static atomic_int threads_left = 500;

static void *
spawn_some_threads (void *ignored)
{
  pthread_attr_t attr;
  xpthread_attr_init (&attr);
  xpthread_attr_setstacksize (&attr, 1 << 18);

  int to_spawn = 1 + arc4random_uniform (10);
  for (int i = 0; i < to_spawn; ++i)
    {
      /* Do not go beyond the global limit.  */
      if (atomic_fetch_sub_explicit (&threads_left, 1,
                                     memory_order_relaxed) <= 0)
        break;

      xpthread_detach (xpthread_create (&attr, spawn_some_threads, NULL));
    }

  xpthread_attr_destroy (&attr);
  return NULL;
}

/* Returns the number of threads in this process, by counting the
   entries in /proc/self/task.  */
static int
current_thread_count (void)
{
  DIR *tasks = xopendir ("/proc/self/task");
  int count = 0;
  struct dirent *d;
  while ((d = xreaddir (tasks)) != NULL)
    if (d->d_name[0] != '.')
      ++count;
  xclosedir (tasks);
  return count;
}

static int
do_test (void)
{
  for (int j = 0; j < 100; ++j)
    {
      /* Keep spawning threads until the limit has been reached.  */
      while (atomic_load_explicit (&threads_left, memory_order_relaxed) > 0)
        {
          spawn_some_threads (NULL);
          usleep (10 * 1000);  /* 10 ms */
        }

      /* Wait until all detached threads have exited.  */
      while (current_thread_count () > 1)
        usleep (1000);  /* 1 ms */

      /* Next iteration will count down from 500 again.  */
      atomic_store_explicit (&threads_left, 500, memory_order_relaxed);
    }

  return 0;
}

/* Use a 60-second timeout to account for slow machines.  */
#define TIMEOUT 60
#include <support/test-driver.c>



More information about the Libc-alpha mailing list