diff options
Diffstat (limited to 'sysdeps/posix/raise.c')
-rw-r--r-- | sysdeps/posix/raise.c | 47 |
1 files changed, 44 insertions, 3 deletions
diff --git a/sysdeps/posix/raise.c b/sysdeps/posix/raise.c index 1f02b201e1..f171dc2407 100644 --- a/sysdeps/posix/raise.c +++ b/sysdeps/posix/raise.c @@ -15,14 +15,55 @@ License along with the GNU C Library; if not, see <http://www.gnu.org/licenses/>. */ +#include <errno.h> #include <signal.h> #include <unistd.h> +#include <internal-signals.h> -/* Raise the signal SIG. */ +/* Raise the signal SIG. POSIX requires raise to be async-signal-safe, + but calling getpid and then raise is *not* async-signal-safe; if an + async signal handler calls fork (which is also async-signal-safe) + in between the two operations, and returns normally on both sides + of the fork, kill will be called twice. So we must block signals + around the operation. See bug 15368 for more detail. + */ int -raise (int sig) +__libc_raise (int sig) { - return __kill (__getpid (), sig); + /* Disallow sending the signals we use for cancellation, timers, + setxid, etc. This check is also performed in __kill, but + if we do it now we can avoid blocking and then unblocking signals + unnecessarily. */ + if (__glibc_unlikely (__is_internal_signal (sig))) + { + __set_errno (EINVAL); + return -1; + } + + /* We can safely assume that __libc_signal_block_app and + __libc_signal_restore_set will not fail, because + sigprocmask can only fail under three circumstances: + + 1. sigsetsize != sizeof (sigset_t) (EINVAL) + 2. a failure in copy from/to user space (EFAULT) + 3. an invalid 'how' operation (EINVAL) + + Getting any of these would indicate a bug in either the + definition of sigset_t or the implementations of the + wrappers. */ + sigset_t omask; + __libc_signal_block_app (&omask); + + int ret = __kill (__getpid (), sig); + + /* ... But just because sigprocmask will not fail here, that doesn't + mean it won't clobber errno. */ + int save_errno = errno; + __libc_signal_restore_set (&omask); + __set_errno (errno); + + return ret; } +strong_alias (__libc_raise, raise) libc_hidden_def (raise) weak_alias (raise, gsignal) |