0b26f7
commit 3abf3bd4edc86fb28c099cc85203cb46a811e0b8
0b26f7
Author: Florian Weimer <fweimer@redhat.com>
0b26f7
Date:   Mon Sep 13 11:06:08 2021 +0200
0b26f7
0b26f7
    nptl: pthread_kill, pthread_cancel should not fail after exit (bug 19193)
0b26f7
    
0b26f7
    This closes one remaining race condition related to bug 12889: if
0b26f7
    the thread already exited on the kernel side, returning ESRCH
0b26f7
    is not correct because that error is reserved for the thread IDs
0b26f7
    (pthread_t values) whose lifetime has ended.  In case of a
0b26f7
    kernel-side exit and a valid thread ID, no signal needs to be sent
0b26f7
    and cancellation does not have an effect, so just return 0.
0b26f7
    
0b26f7
    sysdeps/pthread/tst-kill4.c triggers undefined behavior and is
0b26f7
    removed with this commit.
0b26f7
    
0b26f7
    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
0b26f7
    (cherry picked from commit 8af8456004edbab71f8903a60a3cae442cf6fe69)
0b26f7
0b26f7
diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c
0b26f7
index cc25ff21f364e8a4..9bac6e3b76a20312 100644
0b26f7
--- a/nptl/pthread_cancel.c
0b26f7
+++ b/nptl/pthread_cancel.c
0b26f7
@@ -62,10 +62,11 @@ __pthread_cancel (pthread_t th)
0b26f7
 {
0b26f7
   volatile struct pthread *pd = (volatile struct pthread *) th;
0b26f7
 
0b26f7
-  /* Make sure the descriptor is valid.  */
0b26f7
-  if (INVALID_TD_P (pd))
0b26f7
-    /* Not a valid thread handle.  */
0b26f7
-    return ESRCH;
0b26f7
+  if (pd->tid == 0)
0b26f7
+    /* The thread has already exited on the kernel side.  Its outcome
0b26f7
+       (regular exit, other cancelation) has already been
0b26f7
+       determined.  */
0b26f7
+    return 0;
0b26f7
 
0b26f7
   static int init_sigcancel = 0;
0b26f7
   if (atomic_load_relaxed (&init_sigcancel) == 0)
0b26f7
diff --git a/nptl/pthread_kill.c b/nptl/pthread_kill.c
0b26f7
index f79a2b26fc7f72e5..5d4c86f9205a6fb5 100644
0b26f7
--- a/nptl/pthread_kill.c
0b26f7
+++ b/nptl/pthread_kill.c
0b26f7
@@ -46,7 +46,12 @@ __pthread_kill_internal (pthread_t threadid, int signo)
0b26f7
 	    ? INTERNAL_SYSCALL_ERRNO (val) : 0);
0b26f7
     }
0b26f7
   else
0b26f7
-    val = ESRCH;
0b26f7
+    /* The kernel reports that the thread has exited.  POSIX specifies
0b26f7
+       the ESRCH error only for the case when the lifetime of a thread
0b26f7
+       ID has ended, but calling pthread_kill on such a thread ID is
0b26f7
+       undefined in glibc.  Therefore, do not treat kernel thread exit
0b26f7
+       as an error.  */
0b26f7
+    val = 0;
0b26f7
 
0b26f7
   return val;
0b26f7
 }
0b26f7
diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile
0b26f7
index 42f9fc507263657d..dedfa0d290da4949 100644
0b26f7
--- a/sysdeps/pthread/Makefile
0b26f7
+++ b/sysdeps/pthread/Makefile
0b26f7
@@ -89,7 +89,7 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \
0b26f7
 	 tst-join8 tst-join9 tst-join10 tst-join11 tst-join12 tst-join13 \
0b26f7
 	 tst-join14 tst-join15 \
0b26f7
 	 tst-key1 tst-key2 tst-key3 tst-key4 \
0b26f7
-	 tst-kill1 tst-kill2 tst-kill3 tst-kill4 tst-kill5 tst-kill6 \
0b26f7
+	 tst-kill1 tst-kill2 tst-kill3 tst-kill5 tst-kill6 \
0b26f7
 	 tst-locale1 tst-locale2 \
0b26f7
 	 tst-memstream \
0b26f7
 	 tst-mutex-errorcheck tst-mutex1 tst-mutex2 tst-mutex3 tst-mutex4 \
0b26f7
@@ -118,6 +118,9 @@ 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_cancel-exited \
0b26f7
+	 tst-pthread_kill-exited \
0b26f7
+	 # tests
0b26f7
 
0b26f7
 tests-time64 := \
0b26f7
   tst-abstime-time64 \
0b26f7
diff --git a/sysdeps/pthread/tst-kill4.c b/sysdeps/pthread/tst-kill4.c
0b26f7
deleted file mode 100644
0b26f7
index 9563939792b96ebd..0000000000000000
0b26f7
--- a/sysdeps/pthread/tst-kill4.c
0b26f7
+++ /dev/null
0b26f7
@@ -1,90 +0,0 @@
0b26f7
-/* Copyright (C) 2003-2021 Free Software Foundation, Inc.
0b26f7
-   This file is part of the GNU C Library.
0b26f7
-   Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
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 <errno.h>
0b26f7
-#include <pthread.h>
0b26f7
-#include <signal.h>
0b26f7
-#include <stdio.h>
0b26f7
-#include <stdlib.h>
0b26f7
-#include <unistd.h>
0b26f7
-
0b26f7
-
0b26f7
-static void *
0b26f7
-tf (void *a)
0b26f7
-{
0b26f7
-  return NULL;
0b26f7
-}
0b26f7
-
0b26f7
-
0b26f7
-int
0b26f7
-do_test (void)
0b26f7
-{
0b26f7
-  pthread_attr_t at;
0b26f7
-  if (pthread_attr_init (&at) != 0)
0b26f7
-    {
0b26f7
-      puts ("attr_create failed");
0b26f7
-      exit (1);
0b26f7
-    }
0b26f7
-
0b26f7
-  /* Limit thread stack size, because if it is too large, pthread_join
0b26f7
-     will free it immediately rather than put it into stack cache.  */
0b26f7
-  if (pthread_attr_setstacksize (&at, 2 * 1024 * 1024) != 0)
0b26f7
-    {
0b26f7
-      puts ("setstacksize failed");
0b26f7
-      exit (1);
0b26f7
-    }
0b26f7
-
0b26f7
-  pthread_t th;
0b26f7
-  if (pthread_create (&th, &at, tf, NULL) != 0)
0b26f7
-    {
0b26f7
-      puts ("create failed");
0b26f7
-      exit (1);
0b26f7
-    }
0b26f7
-
0b26f7
-  pthread_attr_destroy (&at);
0b26f7
-
0b26f7
-  if (pthread_join (th, NULL) != 0)
0b26f7
-    {
0b26f7
-      puts ("join failed");
0b26f7
-      exit (1);
0b26f7
-    }
0b26f7
-
0b26f7
-  /* The following only works because we assume here something about
0b26f7
-     the implementation.  Namely, that the memory allocated for the
0b26f7
-     thread descriptor is not going away, that the TID field is
0b26f7
-     cleared and therefore the signal is sent to process 0, and that
0b26f7
-     we can savely assume there is no other process with this ID at
0b26f7
-     that time.  */
0b26f7
-  int e = pthread_kill (th, 0);
0b26f7
-  if (e == 0)
0b26f7
-    {
0b26f7
-      puts ("pthread_kill succeeded");
0b26f7
-      exit (1);
0b26f7
-    }
0b26f7
-  if (e != ESRCH)
0b26f7
-    {
0b26f7
-      puts ("pthread_kill didn't return ESRCH");
0b26f7
-      exit (1);
0b26f7
-    }
0b26f7
-
0b26f7
-  return 0;
0b26f7
-}
0b26f7
-
0b26f7
-
0b26f7
-#define TEST_FUNCTION do_test ()
0b26f7
-#include "../test-skeleton.c"
0b26f7
diff --git a/sysdeps/pthread/tst-pthread_cancel-exited.c b/sysdeps/pthread/tst-pthread_cancel-exited.c
0b26f7
new file mode 100644
0b26f7
index 0000000000000000..811c9bee07ab2638
0b26f7
--- /dev/null
0b26f7
+++ b/sysdeps/pthread/tst-pthread_cancel-exited.c
0b26f7
@@ -0,0 +1,45 @@
0b26f7
+/* Test that pthread_kill succeeds for an exited thread.
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
+/* This test verifies that pthread_kill returns 0 (and not ESRCH) for
0b26f7
+   a thread that has exited on the kernel side.  */
0b26f7
+
0b26f7
+#include <stddef.h>
0b26f7
+#include <support/support.h>
0b26f7
+#include <support/xthread.h>
0b26f7
+
0b26f7
+static void *
0b26f7
+noop_thread (void *closure)
0b26f7
+{
0b26f7
+  return NULL;
0b26f7
+}
0b26f7
+
0b26f7
+static int
0b26f7
+do_test (void)
0b26f7
+{
0b26f7
+  pthread_t thr = xpthread_create (NULL, noop_thread, NULL);
0b26f7
+
0b26f7
+  support_wait_for_thread_exit ();
0b26f7
+
0b26f7
+  xpthread_cancel (thr);
0b26f7
+  xpthread_join (thr);
0b26f7
+
0b26f7
+  return 0;
0b26f7
+}
0b26f7
+
0b26f7
+#include <support/test-driver.c>
0b26f7
diff --git a/sysdeps/pthread/tst-pthread_kill-exited.c b/sysdeps/pthread/tst-pthread_kill-exited.c
0b26f7
new file mode 100644
0b26f7
index 0000000000000000..7575fb6d58cae99c
0b26f7
--- /dev/null
0b26f7
+++ b/sysdeps/pthread/tst-pthread_kill-exited.c
0b26f7
@@ -0,0 +1,46 @@
0b26f7
+/* Test that pthread_kill succeeds for an exited thread.
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
+/* This test verifies that pthread_kill returns 0 (and not ESRCH) for
0b26f7
+   a thread that has exited on the kernel side.  */
0b26f7
+
0b26f7
+#include <signal.h>
0b26f7
+#include <stddef.h>
0b26f7
+#include <support/support.h>
0b26f7
+#include <support/xthread.h>
0b26f7
+
0b26f7
+static void *
0b26f7
+noop_thread (void *closure)
0b26f7
+{
0b26f7
+  return NULL;
0b26f7
+}
0b26f7
+
0b26f7
+static int
0b26f7
+do_test (void)
0b26f7
+{
0b26f7
+  pthread_t thr = xpthread_create (NULL, noop_thread, NULL);
0b26f7
+
0b26f7
+  support_wait_for_thread_exit ();
0b26f7
+
0b26f7
+  xpthread_kill (thr, SIGUSR1);
0b26f7
+  xpthread_join (thr);
0b26f7
+
0b26f7
+  return 0;
0b26f7
+}
0b26f7
+
0b26f7
+#include <support/test-driver.c>