diff options
author | Florian Weimer <fweimer@redhat.com> | 2021-05-21 22:35:00 +0200 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2021-05-21 22:35:00 +0200 |
commit | 2f69522d460611b1018e15df6c238dda2d8d6609 (patch) | |
tree | 1e7a59afe9de337ad58dfdebab4502794db51732 /nptl/pthread_cancel.c | |
parent | 06a36b70f946548d7bc5bc1b163d1ecf877da071 (diff) | |
download | glibc-2f69522d460611b1018e15df6c238dda2d8d6609.tar.gz glibc-2f69522d460611b1018e15df6c238dda2d8d6609.tar.xz glibc-2f69522d460611b1018e15df6c238dda2d8d6609.zip |
nptl: Perform signal initialization upon pthread_create
Install signal handlers and unblock signals before pthread_create creates the first thread. create_thread in sysdeps/unix/sysv/linux/createthread.c can send SIGCANCEL to the current thread, so the SIGCANCEL handler is currently needed even if pthread_cancel is never called. (The way timer_create uses SIGCANCEL does not need a signal handler; both SIG_DFL and SIG_IGN dispositions should work.) Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Diffstat (limited to 'nptl/pthread_cancel.c')
-rw-r--r-- | nptl/pthread_cancel.c | 88 |
1 files changed, 79 insertions, 9 deletions
diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c index e4ad602900..802c691874 100644 --- a/nptl/pthread_cancel.c +++ b/nptl/pthread_cancel.c @@ -26,6 +26,63 @@ #include <unwind-link.h> #include <stdio.h> #include <gnu/lib-names.h> +#include <sys/single_threaded.h> + +/* For asynchronous cancellation we use a signal. This is the core + logic of the signal handler. */ +static void +sigcancel_handler (void) +{ + struct pthread *self = THREAD_SELF; + + int oldval = THREAD_GETMEM (self, cancelhandling); + while (1) + { + /* We are canceled now. When canceled by another thread this flag + is already set but if the signal is directly send (internally or + from another process) is has to be done here. */ + int newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK; + + if (oldval == newval || (oldval & EXITING_BITMASK) != 0) + /* Already canceled or exiting. */ + break; + + int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, + oldval); + if (curval == oldval) + { + /* Set the return value. */ + THREAD_SETMEM (self, result, PTHREAD_CANCELED); + + /* Make sure asynchronous cancellation is still enabled. */ + if ((newval & CANCELTYPE_BITMASK) != 0) + /* Run the registered destructors and terminate the thread. */ + __do_cancel (); + + break; + } + + oldval = curval; + } +} + +/* This is the actually installed SIGCANCEL handler. It adds some + safety checks before performing the cancellation. */ +void +__nptl_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; + + sigcancel_handler (); +} +libc_hidden_def (__nptl_sigcancel_handler) int __pthread_cancel (pthread_t th) @@ -72,14 +129,23 @@ __pthread_cancel (pthread_t th) oldval)) goto again; - /* The cancellation handler will take care of marking the - thread as canceled. */ - pid_t pid = __getpid (); - - int val = INTERNAL_SYSCALL_CALL (tgkill, pid, pd->tid, - SIGCANCEL); - if (INTERNAL_SYSCALL_ERROR_P (val)) - result = INTERNAL_SYSCALL_ERRNO (val); + if (pd == THREAD_SELF) + /* This is not merely an optimization: An application may + call pthread_cancel (pthread_self ()) without calling + pthread_create, so the signal handler may not have been + set up for a self-cancel. */ + sigcancel_handler (); + else + { + /* The cancellation handler will take care of marking the + thread as canceled. */ + pid_t pid = __getpid (); + + int val = INTERNAL_SYSCALL_CALL (tgkill, pid, pd->tid, + SIGCANCEL); + if (INTERNAL_SYSCALL_ERROR_P (val)) + result = INTERNAL_SYSCALL_ERRNO (val); + } break; } @@ -106,4 +172,8 @@ versioned_symbol (libc, __pthread_cancel, pthread_cancel, GLIBC_2_34); compat_symbol (libpthread, __pthread_cancel, pthread_cancel, GLIBC_2_0); #endif -PTHREAD_STATIC_FN_REQUIRE (__pthread_create) +/* 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) |