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