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 | |
parent | 06a36b70f946548d7bc5bc1b163d1ecf877da071 (diff) | |
download | glibc-2f69522d460611b1018e15df6c238dda2d8d6609.zip glibc-2f69522d460611b1018e15df6c238dda2d8d6609.tar.gz glibc-2f69522d460611b1018e15df6c238dda2d8d6609.tar.bz2 |
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')
-rw-r--r-- | nptl/Versions | 5 | ||||
-rw-r--r-- | nptl/nptl-init.c | 75 | ||||
-rw-r--r-- | nptl/pthreadP.h | 6 | ||||
-rw-r--r-- | nptl/pthread_cancel.c | 88 | ||||
-rw-r--r-- | nptl/pthread_create.c | 45 |
5 files changed, 131 insertions, 88 deletions
diff --git a/nptl/Versions b/nptl/Versions index e7883cb..d96b830 100644 --- a/nptl/Versions +++ b/nptl/Versions @@ -367,8 +367,6 @@ libc { tss_set; } GLIBC_PRIVATE { - __nptl_create_event; - __nptl_death_event; __default_pthread_attr; __default_pthread_attr_lock; __futex_abstimed_wait64; @@ -386,11 +384,14 @@ libc { __lll_trylock_elision; __lll_unlock_elision; __mutex_aconf; + __nptl_create_event; __nptl_deallocate_stack; __nptl_deallocate_tsd; + __nptl_death_event; __nptl_free_tcb; __nptl_nthreads; __nptl_setxid_sighandler; + __nptl_sigcancel_handler; __nptl_stack_list_add; __nptl_stack_list_del; __pthread_attr_copy; diff --git a/nptl/nptl-init.c b/nptl/nptl-init.c index f4b86fb..bc4831a 100644 --- a/nptl/nptl-init.c +++ b/nptl/nptl-init.c @@ -44,84 +44,9 @@ size_t __static_tls_align_m1; /* Version of the library, used in libthread_db to detect mismatches. */ static const char nptl_version[] __attribute_used__ = VERSION; -/* For asynchronous cancellation we use a signal. This is the handler. */ -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 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; - } -} - - -/* When using __thread for this, we do it in libc so as not - to give libpthread its own TLS segment just for this. */ -extern void **__libc_dl_error_tsd (void) __attribute__ ((const)); - - void __pthread_initialize_minimal_internal (void) { - struct sigaction sa; - __sigemptyset (&sa.sa_mask); - - /* Install the cancellation signal handler. If for some reason we - cannot install the handler we do not abort. Maybe we should, but - it is only asynchronous cancellation which is affected. */ - sa.sa_sigaction = sigcancel_handler; - sa.sa_flags = SA_SIGINFO; - (void) __libc_sigaction (SIGCANCEL, &sa, NULL); - - /* Install the handle to change the threads' uid/gid. */ - sa.sa_sigaction = __nptl_setxid_sighandler; - sa.sa_flags = SA_SIGINFO | SA_RESTART; - (void) __libc_sigaction (SIGSETXID, &sa, NULL); - - /* The parent process might have left the signals blocked. Just in - case, unblock it. We reuse the signal mask in the sigaction - structure. It is already cleared. */ - __sigaddset (&sa.sa_mask, SIGCANCEL); - __sigaddset (&sa.sa_mask, SIGSETXID); - INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_UNBLOCK, &sa.sa_mask, - NULL, __NSIG_BYTES); - /* Get the size of the static and alignment requirements for the TLS block. */ size_t static_tls_align; diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h index f93806e..497c2ad 100644 --- a/nptl/pthreadP.h +++ b/nptl/pthreadP.h @@ -571,6 +571,12 @@ libc_hidden_proto (__pthread_attr_setsigmask_internal) extern __typeof (pthread_attr_getsigmask_np) __pthread_attr_getsigmask_np; libc_hidden_proto (__pthread_attr_getsigmask_np) +/* The cancellation signal handler defined alongside with + pthread_cancel. This is included in statically linked binaries + only if pthread_cancel is linked in. */ +void __nptl_sigcancel_handler (int sig, siginfo_t *si, void *ctx); +libc_hidden_proto (__nptl_sigcancel_handler) + /* Special versions which use non-exported functions. */ extern void __pthread_cleanup_push (struct _pthread_cleanup_buffer *buffer, void (*routine) (void *), void *arg); diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c index e4ad602..802c691 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) diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c index 7706564..772b5ef 100644 --- a/nptl/pthread_create.c +++ b/nptl/pthread_create.c @@ -56,6 +56,43 @@ static struct rtld_global *__nptl_rtld_global __attribute_used__ = &_rtld_global; #endif +/* This performs the initialization necessary when going from + single-threaded to multi-threaded mode for the first time. */ +static void +late_init (void) +{ + struct sigaction sa; + __sigemptyset (&sa.sa_mask); + + /* Install the cancellation signal handler (in static builds only if + pthread_cancel has been linked in). If for some reason we cannot + install the handler we do not abort. Maybe we should, but it is + only asynchronous cancellation which is affected. */ +#ifndef SHARED + extern __typeof (__nptl_sigcancel_handler) __nptl_sigcancel_handler + __attribute__ ((weak)); + if (__nptl_sigcancel_handler != NULL) +#endif + { + sa.sa_sigaction = __nptl_sigcancel_handler; + sa.sa_flags = SA_SIGINFO; + (void) __libc_sigaction (SIGCANCEL, &sa, NULL); + } + + /* Install the handle to change the threads' uid/gid. */ + sa.sa_sigaction = __nptl_setxid_sighandler; + sa.sa_flags = SA_SIGINFO | SA_RESTART; + (void) __libc_sigaction (SIGSETXID, &sa, NULL); + + /* The parent process might have left the signals blocked. Just in + case, unblock it. We reuse the signal mask in the sigaction + structure. It is already cleared. */ + __sigaddset (&sa.sa_mask, SIGCANCEL); + __sigaddset (&sa.sa_mask, SIGSETXID); + INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_UNBLOCK, &sa.sa_mask, + NULL, __NSIG_BYTES); +} + /* Code to allocate and deallocate a stack. */ #include "allocatestack.c" @@ -459,9 +496,13 @@ __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr, { STACK_VARIABLES; - /* Avoid a data race in the multi-threaded case. */ + /* Avoid a data race in the multi-threaded case, and call the + deferred initialization only once. */ if (__libc_single_threaded) - __libc_single_threaded = 0; + { + late_init (); + __libc_single_threaded = 0; + } const struct pthread_attr *iattr = (struct pthread_attr *) attr; union pthread_attr_transparent default_attr; |