glibc/sysdeps/pthread/tst-pthread-setuid-loop.c
Florian Weimer 2849e2f533 nptl: Avoid setxid deadlock with blocked signals in thread exit [BZ #28361]
As part of the fix for bug 12889, signals are blocked during
thread exit, so that application code cannot run on the thread that
is about to exit.  This would cause problems if the application
expected signals to be delivered after the signal handler revealed
the thread to still exist, despite pthread_kill can no longer be used
to send signals to it.  However, glibc internally uses the SIGSETXID
signal in a way that is incompatible with signal blocking, due to the
way the setxid handshake delays thread exit until the setxid operation
has completed.  With a blocked SIGSETXID, the handshake can never
complete, causing a deadlock.

As a band-aid, restore the previous handshake protocol by not blocking
SIGSETXID during thread exit.

The new test sysdeps/pthread/tst-pthread-setuid-loop.c is based on
a downstream test by Martin Osvald.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Tested-by: Carlos O'Donell <carlos@redhat.com>
2021-09-23 09:56:07 +02:00

62 lines
1.9 KiB
C

/* Test that setuid, pthread_create, thread exit do not deadlock (bug 28361).
Copyright (C) 2021 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <support/check.h>
#include <support/xthread.h>
#include <unistd.h>
/* How many threads to launch during each iteration. */
enum { threads = 4 };
/* How many iterations to perform. This value seems to reproduce
bug 28361 in a bout one in three runs. */
enum { iterations = 5000 };
/* Cache of the real user ID used by setuid_thread. */
static uid_t uid;
/* Start routine for the threads. */
static void *
setuid_thread (void *closure)
{
TEST_COMPARE (setuid (uid), 0);
return NULL;
}
static int
do_test (void)
{
/* The setxid machinery is still invoked even if the UID is
unchanged. (The kernel might reset other credentials as part of
the system call.) */
uid = getuid ();
for (int i = 0; i < iterations; ++i)
{
pthread_t thread_ids[threads];
for (int j = 0; j < threads; ++j)
thread_ids[j] = xpthread_create (NULL, setuid_thread, NULL);
for (int j = 0; j < threads; ++j)
xpthread_join (thread_ids[j]);
}
return 0;
}
#include <support/test-driver.c>