You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
129 lines
5.0 KiB
129 lines
5.0 KiB
commit 33adeaa3e2b9143c38884bc5aa65ded222ed274e |
|
Author: Florian Weimer <fweimer@redhat.com> |
|
Date: Thu Sep 23 09:55:54 2021 +0200 |
|
|
|
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> |
|
(cherry picked from commit 2849e2f53311b66853cb5159b64cba2bddbfb854) |
|
|
|
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c |
|
index 33b426fc682300dc..bc213f0bc4e948bd 100644 |
|
--- a/nptl/pthread_create.c |
|
+++ b/nptl/pthread_create.c |
|
@@ -488,8 +488,16 @@ start_thread (void *arg) |
|
|
|
/* This prevents sending a signal from this thread to itself during |
|
its final stages. This must come after the exit call above |
|
- because atexit handlers must not run with signals blocked. */ |
|
- __libc_signal_block_all (NULL); |
|
+ because atexit handlers must not run with signals blocked. |
|
+ |
|
+ Do not block SIGSETXID. The setxid handshake below expects the |
|
+ signal to be delivered. (SIGSETXID cannot run application code, |
|
+ nor does it use pthread_kill.) Reuse the pd->sigmask space for |
|
+ computing the signal mask, to save stack space. */ |
|
+ __sigfillset (&pd->sigmask); |
|
+ __sigdelset (&pd->sigmask, SIGSETXID); |
|
+ INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_BLOCK, &pd->sigmask, NULL, |
|
+ __NSIG_BYTES); |
|
|
|
/* Tell __pthread_kill_internal that this thread is about to exit. |
|
If there is a __pthread_kill_internal in progress, this delays |
|
diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile |
|
index 48dba717a1cdc20a..d4bd2d4e3ee6a496 100644 |
|
--- a/sysdeps/pthread/Makefile |
|
+++ b/sysdeps/pthread/Makefile |
|
@@ -118,6 +118,7 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \ |
|
tst-unload \ |
|
tst-unwind-thread \ |
|
tst-pt-vfork1 tst-pt-vfork2 tst-vfork1x tst-vfork2x \ |
|
+ tst-pthread-setuid-loop \ |
|
tst-pthread_cancel-exited \ |
|
tst-pthread_cancel-select-loop \ |
|
tst-pthread_kill-exited \ |
|
diff --git a/sysdeps/pthread/tst-pthread-setuid-loop.c b/sysdeps/pthread/tst-pthread-setuid-loop.c |
|
new file mode 100644 |
|
index 0000000000000000..fda2a49b7f0ccf81 |
|
--- /dev/null |
|
+++ b/sysdeps/pthread/tst-pthread-setuid-loop.c |
|
@@ -0,0 +1,61 @@ |
|
+/* 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>
|
|
|