/* Copyright (C) 2002-2022 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 "pthreadP.h" #include #include #include #include #include #include #include /* For asynchronous cancellation we use a signal. */ static void sigcancel_handler (int sig, siginfo_t *si, void *ctx) { /* Safety check. It would be possible to call this function for other signals and send a signal from another process. This is not correct and might even be a security problem. Try to catch as many incorrect invocations as possible. */ if (sig != SIGCANCEL || si->si_pid != __getpid() || si->si_code != SI_TKILL) return; struct pthread *self = THREAD_SELF; int ch = atomic_load_relaxed (&self->cancelhandling); /* Cancelation not enabled, not cancelled, or already exitting. */ if (self->cancelstate == PTHREAD_CANCEL_DISABLE || (ch & CANCELED_BITMASK) == 0 || (ch & EXITING_BITMASK) != 0) return; /* Set the return value. */ THREAD_SETMEM (self, result, PTHREAD_CANCELED); /* Make sure asynchronous cancellation is still enabled. */ if (self->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS) __do_cancel (); } int __pthread_cancel (pthread_t th) { volatile struct pthread *pd = (volatile struct pthread *) th; if (pd->tid == 0) /* The thread has already exited on the kernel side. Its outcome (regular exit, other cancelation) has already been determined. */ return 0; static int init_sigcancel = 0; if (atomic_load_relaxed (&init_sigcancel) == 0) { struct sigaction sa; sa.sa_sigaction = sigcancel_handler; /* The signal handle should be non-interruptible to avoid the risk of spurious EINTR caused by SIGCANCEL sent to process or if pthread_cancel() is called while cancellation is disabled in the target thread. */ sa.sa_flags = SA_SIGINFO | SA_RESTART; __sigemptyset (&sa.sa_mask); __libc_sigaction (SIGCANCEL, &sa, NULL); atomic_store_relaxed (&init_sigcancel, 1); } #ifdef SHARED /* Trigger an error if libgcc_s cannot be loaded. */ { struct unwind_link *unwind_link = __libc_unwind_link_get (); if (unwind_link == NULL) __libc_fatal (LIBGCC_S_SO " must be installed for pthread_cancel to work\n"); } #endif int oldch = atomic_fetch_or_acquire (&pd->cancelhandling, CANCELED_BITMASK); if ((oldch & CANCELED_BITMASK) != 0) return 0; if (pd == THREAD_SELF) { /* A single-threaded process should be able to kill itself, since there is nothing in the POSIX specification that says that it cannot. So we set multiple_threads to true so that cancellation points get executed. */ THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1); #ifndef TLS_MULTIPLE_THREADS_IN_TCB __libc_multiple_threads = 1; #endif THREAD_SETMEM (pd, result, PTHREAD_CANCELED); if (pd->cancelstate == PTHREAD_CANCEL_ENABLE && pd->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS) __do_cancel (); return 0; } return __pthread_kill_internal (th, SIGCANCEL); } versioned_symbol (libc, __pthread_cancel, pthread_cancel, GLIBC_2_34); #if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_34) compat_symbol (libpthread, __pthread_cancel, pthread_cancel, GLIBC_2_0); #endif /* Ensure that the unwinder is always linked in (the __pthread_unwind reference from __do_cancel is weak). Use ___pthread_unwind_next (three underscores) to produce a strong reference to the same file. */ PTHREAD_STATIC_FN_REQUIRE (___pthread_unwind_next)