94084c
commit 40bade26d5bcbda3d21fb598c5063d9df62de966
94084c
Author: Florian Weimer <fweimer@redhat.com>
94084c
Date:   Fri Oct 1 18:16:41 2021 +0200
94084c
94084c
    nptl: pthread_kill must send signals to a specific thread [BZ #28407]
94084c
    
94084c
    The choice between the kill vs tgkill system calls is not just about
94084c
    the TID reuse race, but also about whether the signal is sent to the
94084c
    whole process (and any thread in it) or to a specific thread.
94084c
    
94084c
    This was caught by the openposix test suite:
94084c
    
94084c
      LTP: openposix test suite - FAIL: SIGUSR1 is member of new thread pendingset.
94084c
      <https://gitlab.com/cki-project/kernel-tests/-/issues/764>
94084c
    
94084c
    Fixes commit 526c3cf11ee9367344b6b15d669e4c3cb461a2be ("nptl: Fix race
94084c
    between pthread_kill and thread exit (bug 12889)").
94084c
    
94084c
    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
94084c
    Tested-by: Carlos O'Donell <carlos@redhat.com>
94084c
    (cherry picked from commit eae81d70574e923ce3c59078b8df857ae192efa6)
94084c
94084c
diff --git a/nptl/pthread_kill.c b/nptl/pthread_kill.c
94084c
index a44dc8f2d9baa925..35bf1f973eaeda90 100644
94084c
--- a/nptl/pthread_kill.c
94084c
+++ b/nptl/pthread_kill.c
94084c
@@ -40,7 +40,7 @@ __pthread_kill_implementation (pthread_t threadid, int signo, int no_tid)
94084c
          below.  POSIX only guarantees delivery of a single signal,
94084c
          which may not be the right one.)  */
94084c
       pid_t tid = INTERNAL_SYSCALL_CALL (gettid);
94084c
-      int ret = INTERNAL_SYSCALL_CALL (kill, tid, signo);
94084c
+      int ret = INTERNAL_SYSCALL_CALL (tgkill, __getpid (), tid, signo);
94084c
       return INTERNAL_SYSCALL_ERROR_P (ret) ? INTERNAL_SYSCALL_ERRNO (ret) : 0;
94084c
     }
94084c
 
94084c
@@ -59,8 +59,6 @@ __pthread_kill_implementation (pthread_t threadid, int signo, int no_tid)
94084c
     ret = no_tid;
94084c
   else
94084c
     {
94084c
-      /* Using tgkill is a safety measure.  pd->exit_lock ensures that
94084c
-	 the target thread cannot exit.  */
94084c
       ret = INTERNAL_SYSCALL_CALL (tgkill, __getpid (), pd->tid, signo);
94084c
       ret = INTERNAL_SYSCALL_ERROR_P (ret) ? INTERNAL_SYSCALL_ERRNO (ret) : 0;
94084c
     }
94084c
diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile
94084c
index d4bd2d4e3ee6a496..0af9c59b425aefb1 100644
94084c
--- a/sysdeps/pthread/Makefile
94084c
+++ b/sysdeps/pthread/Makefile
94084c
@@ -121,6 +121,7 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \
94084c
 	 tst-pthread-setuid-loop \
94084c
 	 tst-pthread_cancel-exited \
94084c
 	 tst-pthread_cancel-select-loop \
94084c
+	 tst-pthread-raise-blocked-self \
94084c
 	 tst-pthread_kill-exited \
94084c
 	 tst-pthread_kill-exiting \
94084c
 	 # tests
94084c
diff --git a/sysdeps/pthread/tst-pthread-raise-blocked-self.c b/sysdeps/pthread/tst-pthread-raise-blocked-self.c
94084c
new file mode 100644
94084c
index 0000000000000000..128e1a6071c0b15f
94084c
--- /dev/null
94084c
+++ b/sysdeps/pthread/tst-pthread-raise-blocked-self.c
94084c
@@ -0,0 +1,92 @@
94084c
+/* Test that raise sends signal to current thread even if blocked.
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 <signal.h>
94084c
+#include <support/check.h>
94084c
+#include <support/xsignal.h>
94084c
+#include <support/xthread.h>
94084c
+#include <pthread.h>
94084c
+#include <unistd.h>
94084c
+
94084c
+/* Used to create a dummy thread ID distinct from all other thread
94084c
+   IDs.  */
94084c
+static void *
94084c
+noop (void *ignored)
94084c
+{
94084c
+  return NULL;
94084c
+}
94084c
+
94084c
+static volatile pthread_t signal_thread;
94084c
+
94084c
+static void
94084c
+signal_handler (int signo)
94084c
+{
94084c
+  signal_thread = pthread_self ();
94084c
+}
94084c
+
94084c
+/* Used to ensure that waiting_thread has launched and can accept
94084c
+   signals.  */
94084c
+static pthread_barrier_t barrier;
94084c
+
94084c
+static void *
94084c
+waiting_thread (void *ignored)
94084c
+{
94084c
+  xpthread_barrier_wait (&barrier);
94084c
+  pause ();
94084c
+  return NULL;
94084c
+}
94084c
+
94084c
+static int
94084c
+do_test (void)
94084c
+{
94084c
+  xsignal (SIGUSR1, signal_handler);
94084c
+  xpthread_barrier_init (&barrier, NULL, 2);
94084c
+
94084c
+  /* Distinct thread ID value to */
94084c
+  pthread_t dummy = xpthread_create (NULL, noop, NULL);
94084c
+  signal_thread = dummy;
94084c
+
94084c
+  pthread_t helper = xpthread_create (NULL, waiting_thread, NULL);
94084c
+
94084c
+  /* Make sure that the thread is running.  */
94084c
+  xpthread_barrier_wait (&barrier);
94084c
+
94084c
+  /* Block signals on this thread.  */
94084c
+  sigset_t set;
94084c
+  sigfillset (&set);
94084c
+  xpthread_sigmask (SIG_BLOCK, &set, NULL);
94084c
+
94084c
+  /* Send the signal to this thread.  It must not be delivered.  */
94084c
+  raise (SIGUSR1);
94084c
+  TEST_VERIFY (signal_thread == dummy);
94084c
+
94084c
+  /* Wait a bit to give a chance for signal delivery (increases
94084c
+     chances of failure with bug 28407).  */
94084c
+  usleep (50 * 1000);
94084c
+
94084c
+  /* Unblocking should cause synchronous delivery of the signal.  */
94084c
+  xpthread_sigmask (SIG_UNBLOCK, &set, NULL);
94084c
+  TEST_VERIFY (signal_thread == pthread_self ());
94084c
+
94084c
+  xpthread_cancel (helper);
94084c
+  xpthread_join (helper);
94084c
+  xpthread_join (dummy);
94084c
+  return 0;
94084c
+}
94084c
+
94084c
+#include <support/test-driver.c>