0b26f7
commit 33adeaa3e2b9143c38884bc5aa65ded222ed274e
0b26f7
Author: Florian Weimer <fweimer@redhat.com>
0b26f7
Date:   Thu Sep 23 09:55:54 2021 +0200
0b26f7
0b26f7
    nptl: Avoid setxid deadlock with blocked signals in thread exit [BZ #28361]
0b26f7
    
0b26f7
    As part of the fix for bug 12889, signals are blocked during
0b26f7
    thread exit, so that application code cannot run on the thread that
0b26f7
    is about to exit.  This would cause problems if the application
0b26f7
    expected signals to be delivered after the signal handler revealed
0b26f7
    the thread to still exist, despite pthread_kill can no longer be used
0b26f7
    to send signals to it.  However, glibc internally uses the SIGSETXID
0b26f7
    signal in a way that is incompatible with signal blocking, due to the
0b26f7
    way the setxid handshake delays thread exit until the setxid operation
0b26f7
    has completed.  With a blocked SIGSETXID, the handshake can never
0b26f7
    complete, causing a deadlock.
0b26f7
    
0b26f7
    As a band-aid, restore the previous handshake protocol by not blocking
0b26f7
    SIGSETXID during thread exit.
0b26f7
    
0b26f7
    The new test sysdeps/pthread/tst-pthread-setuid-loop.c is based on
0b26f7
    a downstream test by Martin Osvald.
0b26f7
    
0b26f7
    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
0b26f7
    Tested-by: Carlos O'Donell <carlos@redhat.com>
0b26f7
    (cherry picked from commit 2849e2f53311b66853cb5159b64cba2bddbfb854)
0b26f7
0b26f7
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
0b26f7
index 33b426fc682300dc..bc213f0bc4e948bd 100644
0b26f7
--- a/nptl/pthread_create.c
0b26f7
+++ b/nptl/pthread_create.c
0b26f7
@@ -488,8 +488,16 @@ start_thread (void *arg)
0b26f7
 
0b26f7
   /* This prevents sending a signal from this thread to itself during
0b26f7
      its final stages.  This must come after the exit call above
0b26f7
-     because atexit handlers must not run with signals blocked.  */
0b26f7
-  __libc_signal_block_all (NULL);
0b26f7
+     because atexit handlers must not run with signals blocked.
0b26f7
+
0b26f7
+     Do not block SIGSETXID.  The setxid handshake below expects the
0b26f7
+     signal to be delivered.  (SIGSETXID cannot run application code,
0b26f7
+     nor does it use pthread_kill.)  Reuse the pd->sigmask space for
0b26f7
+     computing the signal mask, to save stack space.  */
0b26f7
+  __sigfillset (&pd->sigmask);
0b26f7
+  __sigdelset (&pd->sigmask, SIGSETXID);
0b26f7
+  INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_BLOCK, &pd->sigmask, NULL,
0b26f7
+			 __NSIG_BYTES);
0b26f7
 
0b26f7
   /* Tell __pthread_kill_internal that this thread is about to exit.
0b26f7
      If there is a __pthread_kill_internal in progress, this delays
0b26f7
diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile
0b26f7
index 48dba717a1cdc20a..d4bd2d4e3ee6a496 100644
0b26f7
--- a/sysdeps/pthread/Makefile
0b26f7
+++ b/sysdeps/pthread/Makefile
0b26f7
@@ -118,6 +118,7 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \
0b26f7
 	 tst-unload \
0b26f7
 	 tst-unwind-thread \
0b26f7
 	 tst-pt-vfork1 tst-pt-vfork2 tst-vfork1x tst-vfork2x \
0b26f7
+	 tst-pthread-setuid-loop \
0b26f7
 	 tst-pthread_cancel-exited \
0b26f7
 	 tst-pthread_cancel-select-loop \
0b26f7
 	 tst-pthread_kill-exited \
0b26f7
diff --git a/sysdeps/pthread/tst-pthread-setuid-loop.c b/sysdeps/pthread/tst-pthread-setuid-loop.c
0b26f7
new file mode 100644
0b26f7
index 0000000000000000..fda2a49b7f0ccf81
0b26f7
--- /dev/null
0b26f7
+++ b/sysdeps/pthread/tst-pthread-setuid-loop.c
0b26f7
@@ -0,0 +1,61 @@
0b26f7
+/* Test that setuid, pthread_create, thread exit do not deadlock (bug 28361).
0b26f7
+   Copyright (C) 2021 Free Software Foundation, Inc.
0b26f7
+   This file is part of the GNU C Library.
0b26f7
+
0b26f7
+   The GNU C Library is free software; you can redistribute it and/or
0b26f7
+   modify it under the terms of the GNU Lesser General Public
0b26f7
+   License as published by the Free Software Foundation; either
0b26f7
+   version 2.1 of the License, or (at your option) any later version.
0b26f7
+
0b26f7
+   The GNU C Library is distributed in the hope that it will be useful,
0b26f7
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
0b26f7
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0b26f7
+   Lesser General Public License for more details.
0b26f7
+
0b26f7
+   You should have received a copy of the GNU Lesser General Public
0b26f7
+   License along with the GNU C Library; if not, see
0b26f7
+   <https://www.gnu.org/licenses/>.  */
0b26f7
+
0b26f7
+#include <support/check.h>
0b26f7
+#include <support/xthread.h>
0b26f7
+#include <unistd.h>
0b26f7
+
0b26f7
+/* How many threads to launch during each iteration.  */
0b26f7
+enum { threads = 4 };
0b26f7
+
0b26f7
+/* How many iterations to perform.  This value seems to reproduce
0b26f7
+   bug 28361 in a bout one in three runs.  */
0b26f7
+enum { iterations = 5000 };
0b26f7
+
0b26f7
+/* Cache of the real user ID used by setuid_thread.  */
0b26f7
+static uid_t uid;
0b26f7
+
0b26f7
+/* Start routine for the threads.  */
0b26f7
+static void *
0b26f7
+setuid_thread (void *closure)
0b26f7
+{
0b26f7
+  TEST_COMPARE (setuid (uid), 0);
0b26f7
+  return NULL;
0b26f7
+}
0b26f7
+
0b26f7
+static int
0b26f7
+do_test (void)
0b26f7
+{
0b26f7
+  /* The setxid machinery is still invoked even if the UID is
0b26f7
+     unchanged.  (The kernel might reset other credentials as part of
0b26f7
+     the system call.)  */
0b26f7
+  uid = getuid ();
0b26f7
+
0b26f7
+  for (int i = 0; i < iterations; ++i)
0b26f7
+    {
0b26f7
+      pthread_t thread_ids[threads];
0b26f7
+      for (int j = 0; j < threads; ++j)
0b26f7
+        thread_ids[j] = xpthread_create (NULL, setuid_thread, NULL);
0b26f7
+      for (int j = 0; j < threads; ++j)
0b26f7
+        xpthread_join (thread_ids[j]);
0b26f7
+    }
0b26f7
+
0b26f7
+  return 0;
0b26f7
+}
0b26f7
+
0b26f7
+#include <support/test-driver.c>