2023-01-28 04:54:20 +00:00
|
|
|
|
// Copyright 2017 The Crashpad Authors
|
2022-04-02 01:21:55 +00:00
|
|
|
|
//
|
|
|
|
|
// 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 <fcntl.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#include <iterator>
|
2022-04-02 01:21:55 +00:00
|
|
|
|
#include <limits>
|
|
|
|
|
|
|
|
|
|
#include "base/files/scoped_file.h"
|
|
|
|
|
#include "base/logging.h"
|
|
|
|
|
#include "base/strings/stringprintf.h"
|
|
|
|
|
#include "build/build_config.h"
|
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
#include "test/errors.h"
|
|
|
|
|
#include "test/multiprocess.h"
|
|
|
|
|
#include "test/scoped_temp_dir.h"
|
|
|
|
|
#include "util/posix/scoped_mmap.h"
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
#include <sys/auxv.h>
|
|
|
|
|
#include <sys/prctl.h>
|
|
|
|
|
|
|
|
|
|
#if defined(ARCH_CPU_ARM64)
|
|
|
|
|
#ifndef HWCAP2_MTE
|
|
|
|
|
#define HWCAP2_MTE (1 << 18)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef SEGV_MTEAERR
|
|
|
|
|
#define SEGV_MTEAERR 8
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef PROT_MTE
|
|
|
|
|
#define PROT_MTE 0x20
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef PR_SET_TAGGED_ADDR_CTRL
|
|
|
|
|
#define PR_SET_TAGGED_ADDR_CTRL 55
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef PR_TAGGED_ADDR_ENABLE
|
|
|
|
|
#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef PR_MTE_TCF_ASYNC
|
|
|
|
|
#define PR_MTE_TCF_ASYNC (1UL << 2)
|
|
|
|
|
#endif
|
|
|
|
|
#endif // defined(ARCH_CPU_ARM64)
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) ||
|
|
|
|
|
// BUILDFLAG(IS_CHROMEOS)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
|
namespace test {
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
constexpr int kUnexpectedExitStatus = 3;
|
|
|
|
|
|
|
|
|
|
struct TestableSignal {
|
|
|
|
|
int sig, code;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Keep synchronized with CauseSignal().
|
|
|
|
|
std::vector<TestableSignal> TestableSignals() {
|
|
|
|
|
std::vector<TestableSignal> signals;
|
|
|
|
|
signals.push_back({SIGABRT, 0});
|
|
|
|
|
signals.push_back({SIGALRM, 0});
|
|
|
|
|
signals.push_back({SIGBUS, 0});
|
|
|
|
|
/* According to DDI0487D (Armv8 Architecture Reference Manual) the expected
|
|
|
|
|
* behavior for division by zero (Section 3.4.8) is: "... results in a
|
|
|
|
|
* zero being written to the destination register, without any
|
|
|
|
|
* indication that the division by zero occurred.".
|
|
|
|
|
* This applies to Armv8 (and not earlier) for both 32bit and 64bit app code.
|
|
|
|
|
*/
|
|
|
|
|
#if defined(ARCH_CPU_X86_FAMILY)
|
|
|
|
|
signals.push_back({SIGFPE, 0});
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
|
|
|
|
|
signals.push_back({SIGILL, 0});
|
|
|
|
|
#endif // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
|
|
|
|
|
signals.push_back({SIGPIPE, 0});
|
|
|
|
|
signals.push_back({SIGSEGV, 0});
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || \
|
|
|
|
|
BUILDFLAG(IS_CHROMEOS)) && \
|
2022-04-02 01:21:55 +00:00
|
|
|
|
defined(ARCH_CPU_ARM64)
|
|
|
|
|
if (getauxval(AT_HWCAP2) & HWCAP2_MTE) {
|
|
|
|
|
signals.push_back({SIGSEGV, SEGV_MTEAERR});
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#if BUILDFLAG(IS_APPLE)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
signals.push_back({SIGSYS, 0});
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#endif // BUILDFLAG(IS_APPLE)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
|
|
|
|
|
signals.push_back({SIGTRAP, 0});
|
|
|
|
|
#endif // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
|
|
|
|
|
return signals;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Keep synchronized with TestableSignals().
|
|
|
|
|
void CauseSignal(int sig, int code) {
|
|
|
|
|
switch (sig) {
|
|
|
|
|
case SIGABRT: {
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SIGALRM: {
|
|
|
|
|
struct itimerval itimer = {};
|
|
|
|
|
itimer.it_value.tv_usec = 1E3; // 1 millisecond
|
|
|
|
|
if (setitimer(ITIMER_REAL, &itimer, nullptr) != 0) {
|
|
|
|
|
PLOG(ERROR) << "setitimer";
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
sleep(std::numeric_limits<unsigned int>::max());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SIGBUS: {
|
|
|
|
|
ScopedMmap mapped_file;
|
|
|
|
|
{
|
|
|
|
|
base::ScopedFD fd;
|
|
|
|
|
{
|
|
|
|
|
ScopedTempDir temp_dir;
|
|
|
|
|
fd.reset(open(temp_dir.path().Append("empty").value().c_str(),
|
|
|
|
|
O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_CLOEXEC,
|
|
|
|
|
0644));
|
|
|
|
|
if (fd.get() < 0) {
|
|
|
|
|
PLOG(ERROR) << "open";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (fd.get() < 0) {
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!mapped_file.ResetMmap(nullptr,
|
|
|
|
|
getpagesize(),
|
|
|
|
|
PROT_READ | PROT_WRITE,
|
|
|
|
|
MAP_PRIVATE,
|
|
|
|
|
fd.get(),
|
|
|
|
|
0)) {
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*mapped_file.addr_as<char*>() = 0;
|
|
|
|
|
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SIGFPE: {
|
|
|
|
|
/* Enabled only for x86, since a division by zero won't raise a signal
|
|
|
|
|
* on Armv8, please see comment at the top of file concerning the
|
|
|
|
|
* Arm architecture.
|
|
|
|
|
*/
|
|
|
|
|
#if defined(ARCH_CPU_X86_FAMILY)
|
2022-08-16 00:48:53 +00:00
|
|
|
|
[[maybe_unused]] volatile int a = 42;
|
2022-04-02 01:21:55 +00:00
|
|
|
|
volatile int b = 0;
|
2022-08-16 00:48:53 +00:00
|
|
|
|
a = a / b;
|
2022-04-02 01:21:55 +00:00
|
|
|
|
#endif
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
|
|
|
|
|
case SIGILL: {
|
|
|
|
|
// __builtin_trap() causes SIGTRAP on arm64 on Android.
|
|
|
|
|
__builtin_trap();
|
|
|
|
|
}
|
|
|
|
|
#endif // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
|
|
|
|
|
|
|
|
|
|
case SIGPIPE: {
|
|
|
|
|
int pipe_fds[2];
|
|
|
|
|
if (pipe(pipe_fds) != 0) {
|
|
|
|
|
PLOG(ERROR) << "pipe";
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (close(pipe_fds[0]) != 0) {
|
|
|
|
|
PLOG(ERROR) << "close";
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char c = 0;
|
|
|
|
|
ssize_t rv = write(pipe_fds[1], &c, sizeof(c));
|
|
|
|
|
if (rv < 0) {
|
|
|
|
|
PLOG(ERROR) << "write";
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
} else if (rv != sizeof(c)) {
|
|
|
|
|
LOG(ERROR) << "write";
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SIGSEGV: {
|
|
|
|
|
switch (code) {
|
|
|
|
|
case 0: {
|
|
|
|
|
volatile int* i = nullptr;
|
|
|
|
|
*i = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || \
|
|
|
|
|
BUILDFLAG(IS_CHROMEOS)) && \
|
2022-04-02 01:21:55 +00:00
|
|
|
|
defined(ARCH_CPU_ARM64)
|
|
|
|
|
case SEGV_MTEAERR: {
|
|
|
|
|
ScopedMmap mapping;
|
|
|
|
|
if (!mapping.ResetMmap(nullptr,
|
|
|
|
|
getpagesize(),
|
|
|
|
|
PROT_READ | PROT_WRITE | PROT_MTE,
|
|
|
|
|
MAP_PRIVATE | MAP_ANON,
|
|
|
|
|
-1,
|
|
|
|
|
0)) {
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
}
|
|
|
|
|
if (prctl(PR_SET_TAGGED_ADDR_CTRL,
|
|
|
|
|
PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_ASYNC,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0) != 0) {
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
}
|
|
|
|
|
mapping.addr_as<char*>()[1ULL << 56] = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#endif // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) ||
|
|
|
|
|
// BUILDFLAG(IS_CHROMEOS)) && defined(ARCH_CPU_ARM64)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#if BUILDFLAG(IS_APPLE)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
case SIGSYS: {
|
|
|
|
|
#pragma clang diagnostic push
|
|
|
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
|
|
|
int rv = syscall(4095);
|
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
|
if (rv != 0) {
|
|
|
|
|
PLOG(ERROR) << "syscall";
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#endif // BUILDFLAG(IS_APPLE)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
|
|
|
|
|
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
|
|
|
|
|
case SIGTRAP: {
|
|
|
|
|
#if defined(ARCH_CPU_X86_FAMILY)
|
|
|
|
|
asm("int3");
|
|
|
|
|
#elif defined(ARCH_CPU_ARM64)
|
|
|
|
|
// bkpt #0 should work for 32-bit ARCH_CPU_ARMEL, but according to
|
|
|
|
|
// https://crrev.com/f53167270c44, it only causes SIGTRAP on Linux under a
|
|
|
|
|
// 64-bit kernel. For a pure 32-bit armv7 system, it generates SIGBUS.
|
|
|
|
|
asm("brk #0");
|
|
|
|
|
#endif
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#endif // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
|
|
|
|
|
|
|
|
|
|
default: {
|
|
|
|
|
LOG(ERROR) << "unexpected signal " << sig;
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class SignalsTest : public Multiprocess {
|
|
|
|
|
public:
|
|
|
|
|
enum class SignalSource {
|
|
|
|
|
kCause,
|
|
|
|
|
kRaise,
|
|
|
|
|
};
|
|
|
|
|
enum class TestType {
|
|
|
|
|
kDefaultHandler,
|
|
|
|
|
kHandlerExits,
|
|
|
|
|
kHandlerReraisesToDefault,
|
|
|
|
|
kHandlerReraisesToPrevious,
|
|
|
|
|
};
|
|
|
|
|
static constexpr int kExitingHandlerExitStatus = 2;
|
|
|
|
|
|
|
|
|
|
SignalsTest(TestType test_type, SignalSource signal_source, int sig, int code)
|
|
|
|
|
: Multiprocess(),
|
|
|
|
|
sig_(sig),
|
|
|
|
|
code_(code),
|
|
|
|
|
test_type_(test_type),
|
|
|
|
|
signal_source_(signal_source) {}
|
|
|
|
|
|
|
|
|
|
SignalsTest(const SignalsTest&) = delete;
|
|
|
|
|
SignalsTest& operator=(const SignalsTest&) = delete;
|
|
|
|
|
|
|
|
|
|
~SignalsTest() {}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
static void SignalHandler_Exit(int sig, siginfo_t* siginfo, void* context) {
|
|
|
|
|
_exit(kExitingHandlerExitStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SignalHandler_ReraiseToDefault(int sig,
|
|
|
|
|
siginfo_t* siginfo,
|
|
|
|
|
void* context) {
|
|
|
|
|
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SignalHandler_ReraiseToPrevious(int sig,
|
|
|
|
|
siginfo_t* siginfo,
|
|
|
|
|
void* context) {
|
|
|
|
|
Signals::RestoreHandlerAndReraiseSignalOnReturn(
|
|
|
|
|
siginfo, old_actions_.ActionForSignal(sig));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Multiprocess:
|
|
|
|
|
void MultiprocessParent() override {}
|
|
|
|
|
|
|
|
|
|
void MultiprocessChild() override {
|
|
|
|
|
bool (*install_handlers)(Signals::Handler, int, Signals::OldActions*);
|
|
|
|
|
if (Signals::IsCrashSignal(sig_)) {
|
|
|
|
|
install_handlers = [](Signals::Handler handler,
|
|
|
|
|
int flags,
|
|
|
|
|
Signals::OldActions* old_actions) {
|
|
|
|
|
return Signals::InstallCrashHandlers(
|
|
|
|
|
handler, flags, old_actions, nullptr);
|
|
|
|
|
};
|
|
|
|
|
} else if (Signals::IsTerminateSignal(sig_)) {
|
|
|
|
|
install_handlers = Signals::InstallTerminateHandlers;
|
|
|
|
|
} else {
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (test_type_) {
|
|
|
|
|
case TestType::kDefaultHandler: {
|
|
|
|
|
// Don’t rely on the default handler being active. Something may have
|
|
|
|
|
// changed it (particularly on Android).
|
|
|
|
|
struct sigaction action;
|
|
|
|
|
sigemptyset(&action.sa_mask);
|
|
|
|
|
action.sa_flags = 0;
|
|
|
|
|
action.sa_handler = SIG_DFL;
|
|
|
|
|
ASSERT_EQ(sigaction(sig_, &action, nullptr), 0)
|
|
|
|
|
<< ErrnoMessage("sigaction");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TestType::kHandlerExits: {
|
|
|
|
|
ASSERT_TRUE(install_handlers(SignalHandler_Exit, 0, nullptr));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TestType::kHandlerReraisesToDefault: {
|
|
|
|
|
ASSERT_TRUE(
|
|
|
|
|
install_handlers(SignalHandler_ReraiseToDefault, 0, nullptr));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TestType::kHandlerReraisesToPrevious: {
|
|
|
|
|
ASSERT_TRUE(install_handlers(SignalHandler_Exit, 0, nullptr));
|
|
|
|
|
ASSERT_TRUE(install_handlers(
|
|
|
|
|
SignalHandler_ReraiseToPrevious, 0, &old_actions_));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (signal_source_) {
|
|
|
|
|
case SignalSource::kCause:
|
|
|
|
|
CauseSignal(sig_, code_);
|
|
|
|
|
break;
|
|
|
|
|
case SignalSource::kRaise:
|
|
|
|
|
raise(sig_);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_exit(kUnexpectedExitStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sig_;
|
|
|
|
|
int code_;
|
|
|
|
|
TestType test_type_;
|
|
|
|
|
SignalSource signal_source_;
|
|
|
|
|
static Signals::OldActions old_actions_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Signals::OldActions SignalsTest::old_actions_;
|
|
|
|
|
|
|
|
|
|
bool ShouldTestSignal(int sig) {
|
|
|
|
|
return Signals::IsCrashSignal(sig) || Signals::IsTerminateSignal(sig);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Signals, WillSignalReraiseAutonomously) {
|
|
|
|
|
const struct {
|
|
|
|
|
int sig;
|
|
|
|
|
int code;
|
|
|
|
|
bool result;
|
|
|
|
|
} kTestData[] = {
|
|
|
|
|
{SIGBUS, BUS_ADRALN, true},
|
|
|
|
|
{SIGFPE, FPE_FLTDIV, true},
|
|
|
|
|
{SIGILL, ILL_ILLOPC, true},
|
|
|
|
|
{SIGSEGV, SEGV_MAPERR, true},
|
|
|
|
|
{SIGBUS, 0, false},
|
|
|
|
|
{SIGFPE, -1, false},
|
|
|
|
|
{SIGILL, SI_USER, false},
|
|
|
|
|
{SIGSEGV, SI_QUEUE, false},
|
|
|
|
|
{SIGTRAP, TRAP_BRKPT, false},
|
|
|
|
|
{SIGHUP, SEGV_MAPERR, false},
|
|
|
|
|
{SIGINT, SI_USER, false},
|
|
|
|
|
};
|
2022-08-16 00:48:53 +00:00
|
|
|
|
for (size_t index = 0; index < std::size(kTestData); ++index) {
|
2022-04-02 01:21:55 +00:00
|
|
|
|
const auto test_data = kTestData[index];
|
|
|
|
|
SCOPED_TRACE(base::StringPrintf(
|
|
|
|
|
"index %zu, sig %d, code %d", index, test_data.sig, test_data.code));
|
|
|
|
|
siginfo_t siginfo = {};
|
|
|
|
|
siginfo.si_signo = test_data.sig;
|
|
|
|
|
siginfo.si_code = test_data.code;
|
|
|
|
|
EXPECT_EQ(Signals::WillSignalReraiseAutonomously(&siginfo),
|
|
|
|
|
test_data.result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Signals, Cause_DefaultHandler) {
|
|
|
|
|
for (TestableSignal s : TestableSignals()) {
|
|
|
|
|
SCOPED_TRACE(base::StringPrintf(
|
|
|
|
|
"sig %d (%s), code %d", s.sig, strsignal(s.sig), s.code));
|
|
|
|
|
|
|
|
|
|
SignalsTest test(SignalsTest::TestType::kDefaultHandler,
|
|
|
|
|
SignalsTest::SignalSource::kCause,
|
|
|
|
|
s.sig,
|
|
|
|
|
s.code);
|
|
|
|
|
test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, s.sig);
|
|
|
|
|
test.Run();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Signals, Cause_HandlerExits) {
|
|
|
|
|
for (TestableSignal s : TestableSignals()) {
|
|
|
|
|
SCOPED_TRACE(base::StringPrintf(
|
|
|
|
|
"sig %d (%s), code %d", s.sig, strsignal(s.sig), s.code));
|
|
|
|
|
|
|
|
|
|
SignalsTest test(SignalsTest::TestType::kHandlerExits,
|
|
|
|
|
SignalsTest::SignalSource::kCause,
|
|
|
|
|
s.sig,
|
|
|
|
|
s.code);
|
|
|
|
|
test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,
|
|
|
|
|
SignalsTest::kExitingHandlerExitStatus);
|
|
|
|
|
test.Run();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Signals, Cause_HandlerReraisesToDefault) {
|
|
|
|
|
for (TestableSignal s : TestableSignals()) {
|
|
|
|
|
SCOPED_TRACE(base::StringPrintf(
|
|
|
|
|
"sig %d (%s), code %d", s.sig, strsignal(s.sig), s.code));
|
|
|
|
|
|
|
|
|
|
SignalsTest test(SignalsTest::TestType::kHandlerReraisesToDefault,
|
|
|
|
|
SignalsTest::SignalSource::kCause,
|
|
|
|
|
s.sig,
|
|
|
|
|
s.code);
|
|
|
|
|
test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, s.sig);
|
|
|
|
|
test.Run();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Signals, Cause_HandlerReraisesToPrevious) {
|
|
|
|
|
for (TestableSignal s : TestableSignals()) {
|
|
|
|
|
SCOPED_TRACE(base::StringPrintf(
|
|
|
|
|
"sig %d (%s), code %d", s.sig, strsignal(s.sig), s.code));
|
|
|
|
|
|
|
|
|
|
SignalsTest test(SignalsTest::TestType::kHandlerReraisesToPrevious,
|
|
|
|
|
SignalsTest::SignalSource::kCause,
|
|
|
|
|
s.sig,
|
|
|
|
|
s.code);
|
|
|
|
|
test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,
|
|
|
|
|
SignalsTest::kExitingHandlerExitStatus);
|
|
|
|
|
test.Run();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Signals, Raise_DefaultHandler) {
|
|
|
|
|
for (int sig = 1; sig < NSIG; ++sig) {
|
|
|
|
|
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
|
|
|
|
|
|
|
|
|
|
if (!ShouldTestSignal(sig)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SignalsTest test(SignalsTest::TestType::kDefaultHandler,
|
|
|
|
|
SignalsTest::SignalSource::kRaise,
|
|
|
|
|
sig,
|
|
|
|
|
0);
|
|
|
|
|
test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);
|
|
|
|
|
test.Run();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Signals, Raise_HandlerExits) {
|
|
|
|
|
for (int sig = 1; sig < NSIG; ++sig) {
|
|
|
|
|
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
|
|
|
|
|
|
|
|
|
|
if (!ShouldTestSignal(sig)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SignalsTest test(SignalsTest::TestType::kHandlerExits,
|
|
|
|
|
SignalsTest::SignalSource::kRaise,
|
|
|
|
|
sig,
|
|
|
|
|
0);
|
|
|
|
|
test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,
|
|
|
|
|
SignalsTest::kExitingHandlerExitStatus);
|
|
|
|
|
test.Run();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Signals, Raise_HandlerReraisesToDefault) {
|
|
|
|
|
for (int sig = 1; sig < NSIG; ++sig) {
|
|
|
|
|
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
|
|
|
|
|
|
|
|
|
|
if (!ShouldTestSignal(sig)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#if BUILDFLAG(IS_APPLE)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
if (sig == SIGBUS
|
|
|
|
|
#if defined(ARCH_CPU_ARM64)
|
|
|
|
|
|| sig == SIGILL || sig == SIGSEGV
|
|
|
|
|
#endif // defined(ARCH_CPU_ARM64)
|
|
|
|
|
) {
|
|
|
|
|
// Signal handlers can’t distinguish between these signals arising out of
|
|
|
|
|
// hardware faults and raised asynchronously.
|
|
|
|
|
// Signals::RestoreHandlerAndReraiseSignalOnReturn() assumes that they
|
|
|
|
|
// come from hardware faults, but this test uses raise(), so the re-raise
|
|
|
|
|
// test must be skipped.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#endif // BUILDFLAG(IS_APPLE)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
|
|
|
|
|
SignalsTest test(SignalsTest::TestType::kHandlerReraisesToDefault,
|
|
|
|
|
SignalsTest::SignalSource::kRaise,
|
|
|
|
|
sig,
|
|
|
|
|
0);
|
|
|
|
|
test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);
|
|
|
|
|
test.Run();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Signals, Raise_HandlerReraisesToPrevious) {
|
|
|
|
|
for (int sig = 1; sig < NSIG; ++sig) {
|
|
|
|
|
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
|
|
|
|
|
|
|
|
|
|
if (!ShouldTestSignal(sig)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#if BUILDFLAG(IS_APPLE)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
if (sig == SIGBUS
|
|
|
|
|
#if defined(ARCH_CPU_ARM64)
|
|
|
|
|
|| sig == SIGILL || sig == SIGSEGV
|
|
|
|
|
#endif // defined(ARCH_CPU_ARM64)
|
|
|
|
|
) {
|
|
|
|
|
// Signal handlers can’t distinguish between these signals arising out of
|
|
|
|
|
// hardware faults and raised asynchronously.
|
|
|
|
|
// Signals::RestoreHandlerAndReraiseSignalOnReturn() assumes that they
|
|
|
|
|
// come from hardware faults, but this test uses raise(), so the re-raise
|
|
|
|
|
// test must be skipped.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#endif // BUILDFLAG(IS_APPLE)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
|
|
|
|
|
SignalsTest test(SignalsTest::TestType::kHandlerReraisesToPrevious,
|
|
|
|
|
SignalsTest::SignalSource::kRaise,
|
|
|
|
|
sig,
|
|
|
|
|
0);
|
|
|
|
|
test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,
|
|
|
|
|
SignalsTest::kExitingHandlerExitStatus);
|
|
|
|
|
test.Run();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Signals, IsCrashSignal) {
|
|
|
|
|
// Always crash signals.
|
|
|
|
|
EXPECT_TRUE(Signals::IsCrashSignal(SIGABRT));
|
|
|
|
|
EXPECT_TRUE(Signals::IsCrashSignal(SIGBUS));
|
|
|
|
|
EXPECT_TRUE(Signals::IsCrashSignal(SIGFPE));
|
|
|
|
|
EXPECT_TRUE(Signals::IsCrashSignal(SIGILL));
|
|
|
|
|
EXPECT_TRUE(Signals::IsCrashSignal(SIGQUIT));
|
|
|
|
|
EXPECT_TRUE(Signals::IsCrashSignal(SIGSEGV));
|
|
|
|
|
EXPECT_TRUE(Signals::IsCrashSignal(SIGSYS));
|
|
|
|
|
EXPECT_TRUE(Signals::IsCrashSignal(SIGTRAP));
|
|
|
|
|
|
|
|
|
|
// Always terminate signals.
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGALRM));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGHUP));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGINT));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGPIPE));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGPROF));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGTERM));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGUSR1));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGUSR2));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGVTALRM));
|
|
|
|
|
|
|
|
|
|
// Never crash or terminate signals.
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGCHLD));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGCONT));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGTSTP));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGTTIN));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGTTOU));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGURG));
|
|
|
|
|
EXPECT_FALSE(Signals::IsCrashSignal(SIGWINCH));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Signals, IsTerminateSignal) {
|
|
|
|
|
// Always terminate signals.
|
|
|
|
|
EXPECT_TRUE(Signals::IsTerminateSignal(SIGALRM));
|
|
|
|
|
EXPECT_TRUE(Signals::IsTerminateSignal(SIGHUP));
|
|
|
|
|
EXPECT_TRUE(Signals::IsTerminateSignal(SIGINT));
|
|
|
|
|
EXPECT_TRUE(Signals::IsTerminateSignal(SIGPIPE));
|
|
|
|
|
EXPECT_TRUE(Signals::IsTerminateSignal(SIGPROF));
|
|
|
|
|
EXPECT_TRUE(Signals::IsTerminateSignal(SIGTERM));
|
|
|
|
|
EXPECT_TRUE(Signals::IsTerminateSignal(SIGUSR1));
|
|
|
|
|
EXPECT_TRUE(Signals::IsTerminateSignal(SIGUSR2));
|
|
|
|
|
EXPECT_TRUE(Signals::IsTerminateSignal(SIGVTALRM));
|
|
|
|
|
|
|
|
|
|
// Always crash signals.
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGABRT));
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGBUS));
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGFPE));
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGILL));
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGQUIT));
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGSEGV));
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGSYS));
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGTRAP));
|
|
|
|
|
|
|
|
|
|
// Never crash or terminate signals.
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGCHLD));
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGCONT));
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGTSTP));
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGTTIN));
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGTTOU));
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGURG));
|
|
|
|
|
EXPECT_FALSE(Signals::IsTerminateSignal(SIGWINCH));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
} // namespace test
|
|
|
|
|
} // namespace crashpad
|