crashpad/util/posix/signals.cc
Mark Mentovai 6278690abe Update copyright boilerplate, 2022 edition (Crashpad)
sed -i '' -E -e 's/Copyright (.+) The Crashpad Authors\. All rights reserved\.$/Copyright \1 The Crashpad Authors/' $(git grep -El 'Copyright (.+) The Crashpad Authors\. All rights reserved\.$')

Bug: chromium:1098010
Change-Id: I8d6138469ddbe3d281a5d83f64cf918ec2491611
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3878262
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
Commit-Queue: Mark Mentovai <mark@chromium.org>
2022-09-06 23:54:07 +00:00

354 lines
12 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2017 The Crashpad Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/posix/signals.h"
#include <unistd.h>
#include <iterator>
#include <vector>
#include "base/check_op.h"
#include "base/logging.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)
#include <sys/syscall.h>
#endif
namespace crashpad {
namespace {
// These are the core-generating signals.
//
// On macOS, these come from 10.12.3 xnu-3789.41.3/bsd/sys/signalvar.h sigprop:
// entries with SA_CORE are in the set.
//
// For Linux, see linux-4.4.52/kernel/signal.c get_signal() and
// linux-4.4.52/include/linux/signal.h sig_kernel_coredump(): signals in
// SIG_KERNEL_COREDUMP_MASK are in the set.
constexpr int kCrashSignals[] = {
SIGABRT,
SIGBUS,
SIGFPE,
SIGILL,
SIGQUIT,
SIGSEGV,
SIGSYS,
SIGTRAP,
#if defined(SIGEMT)
SIGEMT,
#endif // defined(SIGEMT)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
SIGXCPU,
SIGXFSZ,
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
};
// These are the non-core-generating but terminating signals.
//
// On macOS, these come from 10.12.3 xnu-3789.41.3/bsd/sys/signalvar.h sigprop:
// entries with SA_KILL but not SA_CORE are in the set. SIGKILL is excluded
// because it is uncatchable.
//
// For Linux, see linux-4.4.52/kernel/signal.c get_signal() and
// linux-4.4.52/include/linux/signal.h sig_kernel_coredump(),
// sig_kernel_ignore(), and sig_kernel_stop(): signals not in
// SIG_KERNEL_COREDUMP_MASK, SIG_KERNEL_IGNORE_MASK, or SIG_KERNEL_STOP_MASK are
// in the set. SIGKILL is excluded because it is uncatchable (its in
// SIG_KERNEL_ONLY_MASK and qualifies for sig_kernel_only()). Real-time signals
// in the range [SIGRTMIN, SIGRTMAX) also have termination as the default
// action, although they are not listed here.
constexpr int kTerminateSignals[] = {
SIGALRM,
SIGHUP,
SIGINT,
SIGPIPE,
SIGPROF,
SIGTERM,
SIGUSR1,
SIGUSR2,
SIGVTALRM,
#if defined(SIGPWR)
SIGPWR,
#endif // defined(SIGPWR)
#if defined(SIGSTKFLT)
SIGSTKFLT,
#endif // defined(SIGSTKFLT)
#if BUILDFLAG(IS_APPLE)
SIGXCPU,
SIGXFSZ,
#endif // BUILDFLAG(IS_APPLE)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
SIGIO,
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
};
bool InstallHandlers(const std::vector<int>& signals,
Signals::Handler handler,
int flags,
Signals::OldActions* old_actions,
const std::set<int>* unhandled_signals) {
bool success = true;
for (int sig : signals) {
if (unhandled_signals &&
unhandled_signals->find(sig) != unhandled_signals->end()) {
continue;
}
success &= Signals::InstallHandler(
sig,
handler,
flags,
old_actions ? old_actions->ActionForSignal(sig) : nullptr);
}
return success;
}
bool IsSignalInSet(int sig, const int* set, size_t set_size) {
for (size_t index = 0; index < set_size; ++index) {
if (sig == set[index]) {
return true;
}
}
return false;
}
} // namespace
struct sigaction* Signals::OldActions::ActionForSignal(int sig) {
DCHECK_GT(sig, 0);
const size_t slot = sig - 1;
DCHECK_LT(slot, std::size(actions_));
return &actions_[slot];
}
// static
bool Signals::InstallHandler(int sig,
Handler handler,
int flags,
struct sigaction* old_action) {
struct sigaction action;
sigemptyset(&action.sa_mask);
action.sa_flags = flags | SA_SIGINFO;
action.sa_sigaction = handler;
if (sigaction(sig, &action, old_action) != 0) {
PLOG(ERROR) << "sigaction " << sig;
return false;
}
// Sanitizers can prevent the installation of signal handlers, but sigaction
// does not report this as failure. Attempt to detect this by checking the
// currently installed signal handler.
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
defined(THREAD_SANITIZER) || defined(LEAK_SANITIZER) || \
defined(UNDEFINED_SANITIZER)
struct sigaction installed_handler;
CHECK_EQ(sigaction(sig, nullptr, &installed_handler), 0);
// If the installed handler does not point to the just installed handler, then
// the allow_user_segv_handler sanitizer flag is (probably) disabled.
if (installed_handler.sa_sigaction != handler) {
LOG(WARNING)
<< "sanitizers are preventing signal handler installation (sig " << sig
<< ")";
return false;
}
#endif
return true;
}
// static
bool Signals::InstallDefaultHandler(int sig) {
struct sigaction action;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
action.sa_handler = SIG_DFL;
return sigaction(sig, &action, nullptr) == 0;
}
// static
bool Signals::InstallCrashHandlers(Handler handler,
int flags,
OldActions* old_actions,
const std::set<int>* unhandled_signals) {
return InstallHandlers(
std::vector<int>(kCrashSignals, kCrashSignals + std::size(kCrashSignals)),
handler,
flags,
old_actions,
unhandled_signals);
}
// static
bool Signals::InstallTerminateHandlers(Handler handler,
int flags,
OldActions* old_actions) {
return InstallHandlers(
std::vector<int>(kTerminateSignals,
kTerminateSignals + std::size(kTerminateSignals)),
handler,
flags,
old_actions,
nullptr);
}
// static
bool Signals::WillSignalReraiseAutonomously(const siginfo_t* siginfo) {
// Signals received other than via hardware faults, such as those raised
// asynchronously via kill() and raise(), and those arising via hardware traps
// such as int3 on x86 (resulting in SIGTRAP but advancing the instruction
// pointer), will not reoccur on their own when returning from the signal
// handler.
//
// Unfortunately, on macOS, when SIGBUS (on all CPUs) and SIGILL and SIGSEGV
// (on arm64) is received asynchronously via kill(), siginfo->si_code makes it
// appear as though it was actually received via a hardware fault. See 10.15.6
// xnu-6153.141.1/bsd/dev/i386/unix_signal.c sendsig() and 10.15.6
// xnu-6153.141.1/bsd/dev/arm/unix_signal.c sendsig(). Received
// asynchronously, these signals will not re-raise themselves autonomously,
// but this function (acting on information from the kernel) behaves as though
// they will. This isnt ideal, but these signals occurring asynchronously is
// an unexpected condition. The alternative, to never treat these signals as
// autonomously re-raising, is a bad idea because the explicit re-raise would
// lose properties associated with the the original signal, which are valuable
// for debugging and are visible to a Mach exception handler. Since these
// signals are normally received synchronously in response to a hardware
// fault, dont sweat the unexpected asynchronous case.
//
// SIGSEGV on macOS on x86[_64] originating from a general protection fault is
// a more difficult case: si_code is cleared, making the signal appear
// asynchronous. See 10.15.6 xnu-6153.141.1/bsd/dev/i386/unix_signal.c
// sendsig().
const int sig = siginfo->si_signo;
const int code = siginfo->si_code;
// Only these signals can be generated from hardware faults and can re-raise
// autonomously.
return (sig == SIGBUS ||
sig == SIGFPE ||
sig == SIGILL ||
sig == SIGSEGV) &&
// The signal was only generated from a hardware fault if the code is a
// positive number not matching one of these SI_* constants. See
// “Signal Actions” under XRAT “Rationale”/B.2.4 “Signal Concepts” in
// POSIX.1-2008, 2016 Edition, regarding si_code. The historical
// behavior does not use these SI_* constants and signals generated
// asynchronously show up with a code of 0. On macOS, the SI_*
// constants are defined but never used, and the historical value of 0
// remains. See 10.12.3 xnu-3789.41.3/bsd/kern/kern_sig.c
// psignal_internal().
(code > 0 &&
code != SI_ASYNCIO &&
code != SI_MESGQ &&
code != SI_QUEUE &&
code != SI_TIMER &&
code != SI_USER &&
#if defined(SI_DETHREAD)
code != SI_DETHREAD &&
#endif // defiend(SI_DETHREAD)
#if defined(SI_KERNEL)
// In Linux, SI_KERNEL is used for signals that are raised by the
// kernel in software, opposing SI_USER. See
// linux-4.4.52/kernel/signal.c __send_signal(). Signals originating
// from hardware faults do not use this SI_KERNEL, but a proper signal
// code translated in architecture-specific code from the
// characteristics of the hardware fault.
code != SI_KERNEL &&
#endif // defined(SI_KERNEL)
#if defined(SI_SIGIO)
code != SI_SIGIO &&
#endif // defined(SI_SIGIO)
#if defined(SI_TKILL)
code != SI_TKILL &&
#endif // defined(SI_TKILL)
true);
}
// static
void Signals::RestoreHandlerAndReraiseSignalOnReturn(
const siginfo_t* siginfo,
const struct sigaction* old_action) {
// Failures in this function should _exit(kFailureExitCode). This is a quick
// and quiet failure. This function runs in signal handler context, and its
// difficult to safely be loud from a signal handler.
constexpr int kFailureExitCode = 191;
struct sigaction default_action;
sigemptyset(&default_action.sa_mask);
default_action.sa_flags = 0;
default_action.sa_handler = SIG_DFL;
const struct sigaction* restore_action =
old_action ? old_action : &default_action;
// Try to restore restore_action. If that fails and restore_action was
// old_action, the problem may have been that old_action was bogus, so try to
// set the default action.
const int sig = siginfo->si_signo;
if (sigaction(sig, restore_action, nullptr) != 0 && old_action &&
sigaction(sig, &default_action, nullptr) != 0) {
_exit(kFailureExitCode);
}
// If we can raise a signal with siginfo on this platform, do so. This ensures
// that we preserve the siginfo information for asynchronous signals (i.e.
// signals that do not re-raise autonomously), such as signals delivered via
// kill() and asynchronous hardware faults such as SEGV_MTEAERR, which would
// otherwise be lost when re-raising the signal via raise().
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)
int retval = syscall(SYS_rt_tgsigqueueinfo,
getpid(),
syscall(SYS_gettid),
siginfo->si_signo,
siginfo);
if (retval == 0) {
return;
}
// Kernels without commit 66dd34ad31e5 ("signal: allow to send any siginfo to
// itself"), which was first released in kernel version 3.9, did not permit a
// process to send arbitrary signals to itself, and will reject the
// rt_tgsigqueueinfo syscall with EPERM. If that happens, follow the non-Linux
// code path. Any other errno is unexpected and will cause us to exit.
if (errno != EPERM) {
_exit(kFailureExitCode);
}
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) ||
// BUILDFLAG(IS_CHROMEOS)
// Explicitly re-raise the signal if it will not re-raise itself. Because
// signal handlers normally execute with their signal blocked, this raise()
// cannot immediately deliver the signal. Delivery is deferred until the
// signal handler returns and the signal becomes unblocked. The re-raised
// signal will appear with the same context as where it was initially
// triggered.
if (!WillSignalReraiseAutonomously(siginfo) && raise(sig) != 0) {
_exit(kFailureExitCode);
}
}
// static
bool Signals::IsCrashSignal(int sig) {
return IsSignalInSet(sig, kCrashSignals, std::size(kCrashSignals));
}
// static
bool Signals::IsTerminateSignal(int sig) {
return IsSignalInSet(sig, kTerminateSignals, std::size(kTerminateSignals));
}
} // namespace crashpad