[13/15] nptl: Use exit_lock when accessing TID on pthread_sigqueue

Message ID 20210930200051.1017457-14-adhemerval.zanella@linaro.org
State New
Headers show
Series
  • Fix various NPTL synchronization issues
Related show

Commit Message

Noah Goldstein via Libc-alpha Sept. 30, 2021, 8 p.m.
Checked on x86_64-linux-gnu.
---
 nptl/pthread_sigqueue.c              | 56 ++++++++++++++--------------
 sysdeps/pthread/tst-pthread-exited.c |  6 +++
 2 files changed, 34 insertions(+), 28 deletions(-)

-- 
2.30.2

Patch

diff --git a/nptl/pthread_sigqueue.c b/nptl/pthread_sigqueue.c
index edc18bf4c0..c4a1e0a04d 100644
--- a/nptl/pthread_sigqueue.c
+++ b/nptl/pthread_sigqueue.c
@@ -16,6 +16,7 @@ 
    <https://www.gnu.org/licenses/>.  */
 
 #include <errno.h>
+#include <libc-lock.h>
 #include <signal.h>
 #include <string.h>
 #include <unistd.h>
@@ -27,41 +28,40 @@ 
 int
 __pthread_sigqueue (pthread_t threadid, int signo, const union sigval value)
 {
-#ifdef __NR_rt_tgsigqueueinfo
-  struct pthread *pd = (struct pthread *) threadid;
-
-  /* Force load of pd->tid into local variable or register.  Otherwise
-     if a thread exits between ESRCH test and tgkill, we might return
-     EINVAL, because pd->tid would be cleared by the kernel.  */
-  pid_t tid = atomic_forced_read (pd->tid);
-  if (__glibc_unlikely (tid <= 0))
-    /* Not a valid thread handle.  */
-    return ESRCH;
-
   /* Disallow sending the signal we use for cancellation, timers,
      for the setxid implementation.  */
   if (signo == SIGCANCEL || signo == SIGTIMER || signo == SIGSETXID)
     return EINVAL;
 
-  pid_t pid = getpid ();
+  struct pthread *pd = (struct pthread *) threadid;
 
-  /* Set up the siginfo_t structure.  */
-  siginfo_t info;
-  memset (&info, '\0', sizeof (siginfo_t));
-  info.si_signo = signo;
-  info.si_code = SI_QUEUE;
-  info.si_pid = pid;
-  info.si_uid = __getuid ();
-  info.si_value = value;
+  /* Block all signals, as required by pd->exit_lock.  */
+  sigset_t old_mask;
+  __libc_signal_block_all (&old_mask);
+  __libc_lock_lock (pd->exit_lock);
 
-  /* We have a special syscall to do the work.  */
-  int val = INTERNAL_SYSCALL_CALL (rt_tgsigqueueinfo, pid, tid, signo,
-				   &info);
-  return (INTERNAL_SYSCALL_ERROR_P (val)
-	  ? INTERNAL_SYSCALL_ERRNO (val) : 0);
-#else
-  return ENOSYS;
-#endif
+  int res;
+  if (pd->tid != 0)
+    {
+      pid_t pid = getpid ();
+
+      siginfo_t info = { 0 };
+      info.si_signo = signo;
+      info.si_code = SI_QUEUE;
+      info.si_pid = pid;
+      info.si_uid = __getuid ();
+      info.si_value = value;
+
+      res = -INTERNAL_SYSCALL_CALL (rt_tgsigqueueinfo, pid, pd->tid, signo,
+				    &info);
+    }
+  else
+    res = ESRCH;
+
+  __libc_lock_unlock (pd->exit_lock);
+  __libc_signal_restore_set (&old_mask);
+
+  return res;
 }
 versioned_symbol (libc, __pthread_sigqueue, pthread_sigqueue, GLIBC_2_34);
 
diff --git a/sysdeps/pthread/tst-pthread-exited.c b/sysdeps/pthread/tst-pthread-exited.c
index eaaf00a9b4..bfb3870cb6 100644
--- a/sysdeps/pthread/tst-pthread-exited.c
+++ b/sysdeps/pthread/tst-pthread-exited.c
@@ -82,6 +82,12 @@  do_test (void)
     TEST_COMPARE (r, ESRCH);
   }
 
+  {
+    union sigval value = { 0 };
+    int r = pthread_sigqueue (thr, SIGUSR1, value);
+    TEST_COMPARE (r, ESRCH);
+  }
+
   xpthread_join (thr);
 
   return 0;