glibc/setxid-deadlock-blocked-signals.patch
Andreas Schwab 4d4a3834ee Accepting request 923222 from home:Andreas_Schwab:Factory
- ld-show-auxv-colon.patch: elf: Fix missing colon in LD_SHOW_AUXV output
  (BZ #282539
- x86-string-control-test.patch: x86-64: Use testl to check
  __x86_string_control
- pthread-kill-fail-after-exit.patch: nptl: pthread_kill, pthread_cancel
  should not fail after exit (BZ #19193)
- pthread-kill-race-thread-exit.patch: nptl: Fix race between pthread_kill
  and thread exit (BZ #12889)
- getcwd-attribute-access.patch: posix: Fix attribute access mode on
  getcwd (BZ #27476)
- pthread-kill-return-esrch.patch: nptl: pthread_kill needs to return
  ESRCH for old programs (BZ #19193)
- pthread-mutexattr-getrobust-np-type.patch: nptl: Fix type of
  pthread_mutexattr_getrobust_np, pthread_mutexattr_setrobust_np (BZ
  #28036)
- setxid-deadlock-blocked-signals.patch: nptl: Avoid setxid deadlock with
  blocked signals in thread exit (BZ #28361)
- pthread-kill-send-specific-thread.patch: nptl: pthread_kill must send
  signals to a specific thread (BZ #28407)
- sysconf-nprocessors-affinity.patch: linux: Revert the use of
  sched_getaffinity on get_nproc (BZ #28310)
- iconv-charmap-close-output.patch: renamed from
  icon-charmap-close-output.patch

OBS-URL: https://build.opensuse.org/request/show/923222
OBS-URL: https://build.opensuse.org/package/show/Base:System/glibc?expand=0&rev=604
2021-10-05 12:30:15 +00:00

136 lines
5.2 KiB
Diff

From 33adeaa3e2b9143c38884bc5aa65ded222ed274e Mon Sep 17 00:00:00 2001
From: Florian Weimer <fweimer@redhat.com>
Date: Thu, 23 Sep 2021 09:55:54 +0200
Subject: [PATCH] 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)
---
NEWS | 1 +
nptl/pthread_create.c | 12 ++++-
sysdeps/pthread/Makefile | 1 +
sysdeps/pthread/tst-pthread-setuid-loop.c | 61 +++++++++++++++++++++++
4 files changed, 73 insertions(+), 2 deletions(-)
create mode 100644 sysdeps/pthread/tst-pthread-setuid-loop.c
Index: glibc-2.34/nptl/pthread_create.c
===================================================================
--- glibc-2.34.orig/nptl/pthread_create.c
+++ glibc-2.34/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
Index: glibc-2.34/sysdeps/pthread/Makefile
===================================================================
--- glibc-2.34.orig/sysdeps/pthread/Makefile
+++ glibc-2.34/sysdeps/pthread/Makefile
@@ -118,6 +118,7 @@ tests += tst-cnd-basic tst-mtx-trylock t
tst-unload \
tst-unwind-thread \
tst-pt-vfork1 tst-pt-vfork2 tst-vfork1x tst-vfork2x \
+ tst-pthread-setuid-loop \
tst-pthread_cancel-select-loop \
tst-pthread_kill-exiting \
Index: glibc-2.34/sysdeps/pthread/tst-pthread-setuid-loop.c
===================================================================
--- /dev/null
+++ glibc-2.34/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>