diff options
author | Zack Weinberg <zackw@panix.com> | 2018-03-23 09:16:59 -0400 |
---|---|---|
committer | Zack Weinberg <zackw@panix.com> | 2018-03-26 08:30:46 -0400 |
commit | 9ea49e16c79bd2acd0d0648ca0163f26dd1c3dae (patch) | |
tree | bc5d3c3735dd1ee96b43f1dee3001dfb165d1fd8 /sysdeps/generic/internal-signals.h | |
parent | 3d8eb8099425ae4f474e97082e04784c2984ec48 (diff) | |
download | glibc-9ea49e16c79bd2acd0d0648ca0163f26dd1c3dae.tar.gz glibc-9ea49e16c79bd2acd0d0648ca0163f26dd1c3dae.tar.xz glibc-9ea49e16c79bd2acd0d0648ca0163f26dd1c3dae.zip |
[Bug 15368] Move pthread_kill to libc and use it to implement raise. zack/wip-pthread-no-dupe-defns
The fix for bug #15368 was unnecessarily Linux-specific. To recap, POSIX specifies raise to be async-signal-safe, but also specifies it to be equivalent to pthread_kill(pthread_self(), sig), which is not an async-signal-safe sequence of operations; a signal handler could run in between pthread_self and pthread_kill, and do something (such as calling fork, which is also async-signal-safe) that would invalidate the thread descriptor. This is even true in the hypothetical case of a port that doesn't implement multithreading: kill(getpid(), sig) will fire the signal twice if a signal handler runs in between, calls fork, and then returns on both sides of the fork. I don't see anything in the standards to forbid that. The Linux-specific fix was to override the definitions of raise in both libpthread and libc to the same unitary function that blocks signals, retrieves TID and PID directly from the kernel, calls tgkill, and only then unblocks signals. This patch generalizes that to any port: pthread_kill is moved from libpthread to libc, with a forwarding stub left behind. The definition of raise in libpthread is also replaced with a forwarding stub. The Linux-specific definition of raise is deleted; those ports will now use sysdeps/pthread/raise.c, which blocks signals first, then calls pthread_self and pthread_kill, and then unblocks signals. Similarly, sysdeps/posix/raise.c (which would be used on a port that didn't implement multithreading) blocks signals, calls getpid and kill, and then unblocks signals. Thus, ports need only implement the primitives correctly and do not need to worry about making raise async-signal-safe. The only wrinkle was that up till now, we did not bother initializing the ->tid field of the initial thread's descriptor unless libpthread was loaded; now that raise calls pthread_kill even in a single- threaded environment, that won't fly. This is abstractly easy to fix; the tricky part was figuring out _where_ to put the calls (two of them, as it happens) to __pthread_initialize_pids, and I'd appreciate careful eyes on those changes. You might be wondering why it's safe to rely on the TID in the thread descriptor, rather than calling gettid directly. Since all signals are blocked from before calling pthread_self until after pthread_kill uses the TID to call tgkill, the question is whether some _other_ thread could do something that would invalidate the calling thread's descriptor, and I believe there is no such thing. While I was at it I fixed another bug: raise was returning an error code on failure (like pthread_kill does) instead of setting errno as specified. This is user-visible but I don't think it's worth recording as a fixed bug, nobody bothers checking whether raise failed anyway. * nptl/pt-raise.c * sysdeps/unix/sysv/linux/pt-raise.c * sysdeps/unix/sysv/linux/raise.c: Remove file. * sysdeps/unix/sysv/linux/pthread_kill.c: Use __is_internal_signal to check for forbidden signals. Use INTERNAL_SYSCALL_CALL to call getpid. Provide __libc_pthread_kill, with __pthread_kill as strong alias and pthread_kill as weak alias. * sysdeps/posix/raise.c: Block signals around the calls to __getpid and __kill. Provide __libc_raise, with raise as strong alias, libc_hidden_def for raise, and gsignal as weak alias. * sysdeps/pthread/raise.c: New file. Implement by blocking signals, calling pthread_self and pthread_kill, and then unblocking signals again. Provide same symbols as above. * sysdeps/generic/internal-signals.h: Define all of the same functions that sysdeps/unix/sysv/linux/internal-signals.h does, with sensible default definitions. * sysdeps/unix/sysv/linux/internal-signals.h: Clarify comments. * nptl/pthread_kill.c: Define __libc_pthread_kill, with __pthread_kill as strong alias and pthread_kill as weak alias. * nptl/pthread_self.c: Define __pthread_self, with pthread_self as weak alias. * signal/raise.c: Define __libc_raise, with raise as strong alias, libc_hidden_def for raise, and gsignal as weak alias. * nptl/Makefile: Move pthread_kill from libpthread-routines to routines. Remove pt-raise from libpthread-routines. * nptl/Versions (libc/GLIBC_2.28): Add pthread_kill. (libc/GLIBC_PRIVATE): Add __libc_pthread_kill and __libc_raise. * sysdeps/generic/pt-compat-stubs.S: Add stubs for raise and pthread_kill. * nptl/nptl-init.c (__pthread_initialize_minimal_internal): Don't call __pthread_initialize_pids here. * csu/libc-tls.c (__libc_setup_tls): Call __pthread_initialize_pids after all other setup. * elf/rtld.c (init_tls): Likewise. * include/pthreadP.h: New forwarder. * include/pthread.h: Add multiple inclusion guard. Declare __pthread_self. * include/signal.h: Declare __pthread_kill. * sysdeps/**/libc.abilist (GLIBC_2.28): Add pthread_kill.
Diffstat (limited to 'sysdeps/generic/internal-signals.h')
-rw-r--r-- | sysdeps/generic/internal-signals.h | 51 |
1 files changed, 50 insertions, 1 deletions
diff --git a/sysdeps/generic/internal-signals.h b/sysdeps/generic/internal-signals.h index 01e5b75b6b..1bae0fd875 100644 --- a/sysdeps/generic/internal-signals.h +++ b/sysdeps/generic/internal-signals.h @@ -1,4 +1,4 @@ -/* Special use of signals internally. Stub version. +/* Special use of signals internally. Generic version. Copyright (C) 2014-2018 Free Software Foundation, Inc. This file is part of the GNU C Library. @@ -15,3 +15,52 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see <http://www.gnu.org/licenses/>. */ + +#ifndef __INTERNAL_SIGNALS_H +# define __INTERNAL_SIGNALS_H + +#include <signal.h> +#include <sigsetops.h> + +/* Return whether sig is used internally. */ +static inline int +__is_internal_signal (int sig) +{ + return 0; +} + +/* Remove internal glibc signals from the mask. */ +static inline void +__clear_internal_signals (sigset_t *set) +{ +} + +/* Block all signals, including internal glibc ones; write the previous + signal mask to SET. */ +static inline int +__libc_signal_block_all (sigset_t *set) +{ + sigset_t allset; + __sigfillset (&allset); + return __sigprocmask (SIG_BLOCK, &allset, set); +} + +/* Block all application signals (excluding internal glibc ones); write + the previous signal mask to SET. */ +static inline int +__libc_signal_block_app (sigset_t *set) +{ + sigset_t allset; + __sigfillset (&allset); + __clear_internal_signals (&allset); + return __sigprocmask (SIG_BLOCK, &allset, set); +} + +/* Restore process signal mask according to SET. */ +static inline int +__libc_signal_restore_set (const sigset_t *set) +{ + return __sigprocmask (SIG_SETMASK, set, NULL); +} + +#endif |