]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
nptl: Use exit_lock when accessing TID on pthread_sigqueue
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>
Thu, 9 Sep 2021 20:13:05 +0000 (17:13 -0300)
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>
Wed, 9 Jul 2025 22:57:21 +0000 (19:57 -0300)
Also return EINVAL if the thread is already terminated at the time
of the call.

Checked on x86_64-linux-gnu.

nptl/pthread_sigqueue.c
sysdeps/pthread/tst-pthread-exited.c

index cd7d8cc77532575469f8fb554d043ceae1f23a00..195bc6ca798f7d7316b605e79ed05b70c32fcdd9 100644 (file)
@@ -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>
 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.  */
+  internal_sigset_t old_mask;
+  internal_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 = EINVAL;
+
+  __libc_lock_unlock (pd->exit_lock);
+  internal_signal_restore_set (&old_mask);
+
+  return res;
 }
 versioned_symbol (libc, __pthread_sigqueue, pthread_sigqueue, GLIBC_2_34);
 
index 6a602afe3ff45eceaf004bbe002882d2f32d4a89..ec8403eeb7529a256d3da24a156c2f1e2f26b13a 100644 (file)
@@ -75,6 +75,12 @@ do_test (void)
     TEST_COMPARE (r, EINVAL);
   }
 
+  {
+    union sigval value = { 0 };
+    int r = pthread_sigqueue (thr, SIGUSR1, value);
+    TEST_COMPARE (r, EINVAL);
+  }
+
   xpthread_join (thr);
 
   return 0;