From: Adhemerval Zanella Date: Thu, 11 Dec 2025 20:47:22 +0000 (-0300) Subject: nptl: Make pthread_{clock, timed}join{_np} act on all cancellation (BZ 33717) X-Git-Tag: glibc-2.43~70 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=440108ce9e2dd02706b857c7289076a3fe286d25;p=thirdparty%2Fglibc.git nptl: Make pthread_{clock, timed}join{_np} act on all cancellation (BZ 33717) The pthread_join/pthread_timedjoin_np/pthread_clockjoin_np will not act on cancellation if 1. some other thread is already waiting on the 'joinid' or 2. If the thread has already exited. On nptl/pthread_join_common.c, the 1. is due to the CAS doing an early return: 80 else if (__glibc_unlikely (atomic_compare_exchange_weak_acquire (&pd->joinid, 81 &self, 82 NULL))) 83 /* There is already somebody waiting for the thread. */ 84 return EINVAL; And 2. is due to the pd->tid equal to 0: 99 pid_t tid; 100 while ((tid = atomic_load_acquire (&pd->tid)) != 0) 101 { The easiest solution would be to add an __pthread_testcancel () on __pthread_clockjoin_ex () if 'cancel' is true. Checked on aarch64-linux-gnu, arm-linux-gnueabihf, x86_64-linux-gnu, and i686-linux-gnu. Reviewed-by: Florian Weimer --- diff --git a/nptl/Makefile b/nptl/Makefile index 963b32123c..c7f2c24339 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -278,6 +278,7 @@ tests = \ tst-cancel24 \ tst-cancel31 \ tst-cancel33 \ + tst-cancel34 \ tst-cleanup5 \ tst-cond26 \ tst-context1 \ diff --git a/nptl/pthread_join_common.c b/nptl/pthread_join_common.c index 6cdb710bc9..ddf10133c1 100644 --- a/nptl/pthread_join_common.c +++ b/nptl/pthread_join_common.c @@ -28,6 +28,9 @@ __pthread_clockjoin_ex (pthread_t threadid, void **thread_return, const struct __timespec64 *abstime, bool cancel) { + if (cancel) + __pthread_testcancel (); + struct pthread *pd = (struct pthread *) threadid; /* Make sure the clock and time specified are valid. */ diff --git a/nptl/tst-cancel34.c b/nptl/tst-cancel34.c new file mode 100644 index 0000000000..ad050c1dbb --- /dev/null +++ b/nptl/tst-cancel34.c @@ -0,0 +1,107 @@ +/* Check if pthread_join acts a cancellation entrypoiny (BZ 33717) + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +static pthread_barrier_t b1; +static pthread_barrier_t b2; + +static void * +thr_func2 (void *arg) +{ + xpthread_barrier_wait (&b2); + return NULL; +} + +static void * +thr_func1 (void *arg) +{ + int (*join_func)(pthread_t, void **) = arg; + + xpthread_barrier_init (&b2, NULL, 2); + + pthread_t thr = xpthread_create (NULL, thr_func2, NULL); + + pid_t tid = pthread_gettid_np (thr); + /* Synchronize the inner thread so the pthread_gettid_np returns a valid + TID. */ + xpthread_barrier_wait (&b2); + + support_thread_state_wait (tid, support_process_state_dead); + + /* Synchronize with parent thread so the cancellation is issues after + the inner thread has finished. */ + xpthread_barrier_wait (&b1); + + /* Wait for the cancellation. */ + xpthread_barrier_wait (&b1); + + join_func (thr, NULL); + + support_record_failure (); + + return NULL; +} + +static void +do_test_common (int (*join_func)(pthread_t, void **)) +{ + xpthread_barrier_init (&b1, NULL, 2); + + pthread_t thr = xpthread_create (NULL, thr_func1, join_func); + + /* Wait until the inner thread is created and terminated + (support_process_state_dead) before issuing the pthread_cancel. */ + xpthread_barrier_wait (&b1); + + xpthread_cancel (thr); + + /* The deferred cancellation is pending on the thread, issue the + routine to check. */ + xpthread_barrier_wait (&b1); + + TEST_VERIFY (xpthread_join (thr) == PTHREAD_CANCELED); +} + +static int +pthread_timedjoin_np_wrapper (pthread_t thr, void **retval) +{ + return pthread_timedjoin_np (thr, retval, NULL); +} + +static int +pthread_clockjoin_np_wrapper (pthread_t thr, void **retval) +{ + return pthread_clockjoin_np (thr, retval, CLOCK_REALTIME, NULL); +} + +static int +do_test (void) +{ + do_test_common (pthread_join); + do_test_common (pthread_timedjoin_np_wrapper); + do_test_common (pthread_clockjoin_np_wrapper); + + return 0; +} + +#define TIMEOUT 3 +#include