[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