Merge master 6128f38e28eb into doc

This commit is contained in:
Mark Mentovai 2017-03-03 13:07:08 -05:00
commit 5fcdc7528b
26 changed files with 1594 additions and 189 deletions

28
DEPS
View File

@ -38,7 +38,7 @@ deps = {
'crashpad/third_party/mini_chromium/mini_chromium':
Var('chromium_git') + '/chromium/mini_chromium@' +
'f65519e442d23498937251e680a3b113927613b0',
'3a2d52d74c9af5277bf6456cc00ae728f89c4898',
'crashpad/third_party/zlib/zlib':
Var('chromium_git') + '/chromium/src/third_party/zlib@' +
'13dc246a58e4b72104d35f9b1809af95221ebda7',
@ -71,6 +71,19 @@ hooks = [
'buildtools/win/clang-format.exe.sha1',
],
},
{
'name': 'clang_format_linux',
'pattern': '.',
'action': [
'download_from_google_storage',
'--platform=^linux2?$',
'--no_resume',
'--no_auth',
'--bucket=chromium-clang-format',
'--sha1_file',
'buildtools/linux64/clang-format.sha1',
],
},
{
'name': 'gn_mac',
'pattern': '.',
@ -97,6 +110,19 @@ hooks = [
'buildtools/win/gn.exe.sha1',
],
},
{
'name': 'gn_linux',
'pattern': '.',
'action': [
'download_from_google_storage',
'--platform=^linux2?$',
'--no_resume',
'--no_auth',
'--bucket=chromium-gn',
'--sha1_file',
'buildtools/linux64/gn.sha1',
],
},
{
'name': 'gyp',
'pattern': '\.gypi?$',

View File

@ -299,9 +299,10 @@ CrashReportDatabaseMac::PrepareNewCrashReport(NewReport** out_report) {
base_dir_.Append(kWriteDirectory)
.Append(report->uuid.ToString() + "." + kCrashReportFileExtension);
report->handle = HANDLE_EINTR(open(report->path.value().c_str(),
O_CREAT | O_WRONLY | O_EXCL | O_EXLOCK,
0600));
report->handle = HANDLE_EINTR(
open(report->path.value().c_str(),
O_WRONLY | O_EXLOCK | O_CREAT | O_EXCL | O_NOCTTY | O_CLOEXEC,
0600));
if (report->handle < 0) {
PLOG(ERROR) << "open " << report->path.value();
return kFileSystemError;
@ -612,8 +613,9 @@ CrashReportDatabase::OperationStatus CrashReportDatabaseMac::RequestUpload(
// static
base::ScopedFD CrashReportDatabaseMac::ObtainReportLock(
const base::FilePath& path) {
int fd = HANDLE_EINTR(open(path.value().c_str(),
O_RDONLY | O_EXLOCK | O_NONBLOCK));
int fd = HANDLE_EINTR(
open(path.value().c_str(),
O_RDONLY | O_NONBLOCK | O_EXLOCK | O_NOCTTY | O_CLOEXEC));
PLOG_IF(ERROR, fd < 0) << "open lock " << path.value();
return base::ScopedFD(fd);
}

View File

@ -164,7 +164,7 @@ enum MINIDUMP_STREAM_TYPE {
UnloadedModuleListStream = 14,
//! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,
//! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4.
//! MINIDUMP_MISC_INFO_3, MINIDUMP_MISC_INFO_4, and MINIDUMP_MISC_INFO_5.
//!
//! More recent versions of this stream are supersets of earlier versions.
//!
@ -753,6 +753,33 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE_LIST {
uint32_t NumberOfEntries;
};
//! \brief Information about XSAVE-managed state stored within CPU-specific
//! context structures.
struct __attribute__((packed, aligned(4))) XSTATE_CONFIG_FEATURE_MSC_INFO {
//! \brief The size of this structure, in bytes. This value is
//! `sizeof(XSTATE_CONFIG_FEATURE_MSC_INFO)`.
uint32_t SizeOfInfo;
//! \brief The size of a CPU-specific context structure carrying all XSAVE
//! state components described by this structure.
//!
//! Equivalent to the value returned by `InitializeContext()` in \a
//! ContextLength.
uint32_t ContextSize;
//! \brief The XSAVE state-component bitmap, XSAVE_BV.
//!
//! See Intel Software Developers Manual, Volume 1: Basic Architecture
//! (253665-060), 13.4.2 “XSAVE Header”.
uint64_t EnabledFeatures;
//! \brief The location of each state component within a CPU-specific context
//! structure.
//!
//! This array is indexed by bit position numbers used in #EnabledFeatures.
XSTATE_FEATURE Features[MAXIMUM_XSTATE_FEATURES];
};
//! \anchor MINIDUMP_MISCx
//! \name MINIDUMP_MISC*
//!
@ -805,6 +832,10 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE_LIST {
//! - MINIDUMP_MISC_INFO_4::BuildString
//! - MINIDUMP_MISC_INFO_4::DbgBldStr
#define MINIDUMP_MISC4_BUILDSTRING 0x00000100
//! \brief MINIDUMP_MISC_INFO_5::ProcessCookie is valid.
#define MINIDUMP_MISC5_PROCESS_COOKIE 0x00000200
//! \}
//! \brief Information about the process that the minidump file contains a
@ -814,6 +845,7 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE_LIST {
//! \sa MINIDUMP_MISC_INFO_2
//! \sa MINIDUMP_MISC_INFO_3
//! \sa MINIDUMP_MISC_INFO_4
//! \sa MINIDUMP_MISC_INFO_5
//! \sa MINIDUMP_MISC_INFO_N
struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO {
//! \brief The size of the structure.
@ -854,6 +886,7 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO {
//! \sa MINIDUMP_MISC_INFO
//! \sa MINIDUMP_MISC_INFO_3
//! \sa MINIDUMP_MISC_INFO_4
//! \sa MINIDUMP_MISC_INFO_5
//! \sa MINIDUMP_MISC_INFO_N
struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_2
: public MINIDUMP_MISC_INFO {
@ -885,6 +918,7 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_2
//! \sa MINIDUMP_MISC_INFO
//! \sa MINIDUMP_MISC_INFO_2
//! \sa MINIDUMP_MISC_INFO_4
//! \sa MINIDUMP_MISC_INFO_5
//! \sa MINIDUMP_MISC_INFO_N
struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_3
: public MINIDUMP_MISC_INFO_2 {
@ -946,6 +980,7 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_3
//! \sa MINIDUMP_MISC_INFO
//! \sa MINIDUMP_MISC_INFO_2
//! \sa MINIDUMP_MISC_INFO_3
//! \sa MINIDUMP_MISC_INFO_5
//! \sa MINIDUMP_MISC_INFO_N
struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_4
: public MINIDUMP_MISC_INFO_3 {
@ -968,8 +1003,31 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_4
base::char16 DbgBldStr[40];
};
//! \brief Information about the process that the minidump file contains a
//! snapshot of, as well as the system that hosted that process.
//!
//! This structure variant is used on Windows 10 and later.
//!
//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*"
//! \sa MINIDUMP_MISC_INFO
//! \sa MINIDUMP_MISC_INFO_2
//! \sa MINIDUMP_MISC_INFO_3
//! \sa MINIDUMP_MISC_INFO_4
//! \sa MINIDUMP_MISC_INFO_N
struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_5
: public MINIDUMP_MISC_INFO_4 {
//! \brief Information about XSAVE-managed state stored within CPU-specific
//! context structures.
//!
//! This information can be used to locate state components within
//! CPU-specific context structures.
XSTATE_CONFIG_FEATURE_MSC_INFO XStateData;
uint32_t ProcessCookie;
};
//! \brief The latest known version of the MINIDUMP_MISC_INFO structure.
typedef MINIDUMP_MISC_INFO_4 MINIDUMP_MISC_INFO_N;
typedef MINIDUMP_MISC_INFO_5 MINIDUMP_MISC_INFO_N;
//! \brief Describes a region of memory.
struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_INFO {

View File

@ -112,6 +112,66 @@
#define PF_RDTSCP_INSTRUCTION_AVAILABLE 32
//! \}
//! \anchor PAGE_x
//! \name PAGE_*
//!
//! \brief Memory protection constants for MINIDUMP_MEMORY_INFO::Protect and
//! MINIDUMP_MEMORY_INFO::AllocationProtect.
//! \{
#define PAGE_NOACCESS 0x1
#define PAGE_READONLY 0x2
#define PAGE_READWRITE 0x4
#define PAGE_WRITECOPY 0x8
#define PAGE_EXECUTE 0x10
#define PAGE_EXECUTE_READ 0x20
#define PAGE_EXECUTE_READWRITE 0x40
#define PAGE_EXECUTE_WRITECOPY 0x80
#define PAGE_GUARD 0x100
#define PAGE_NOCACHE 0x200
#define PAGE_WRITECOMBINE 0x400
//! \}
//! \anchor MEM_x
//! \name MEM_*
//!
//! \brief Memory state and type constants for MINIDUMP_MEMORY_INFO::State and
//! MINIDUMP_MEMORY_INFO::Type.
//! \{
#define MEM_COMMIT 0x1000
#define MEM_RESERVE 0x2000
#define MEM_DECOMMIT 0x4000
#define MEM_RELEASE 0x8000
#define MEM_FREE 0x10000
#define MEM_PRIVATE 0x20000
#define MEM_MAPPED 0x40000
#define MEM_RESET 0x80000
//! \}
//! \brief The maximum number of distinct identifiable features that could
//! possibly be carried in an XSAVE area.
//!
//! This corresponds to the number of bits in the XSAVE state-component bitmap,
//! XSAVE_BV. See Intel Software Developers Manual, Volume 1: Basic
//! Architecture (253665-060), 13.4.2 “XSAVE Header”.
#define MAXIMUM_XSTATE_FEATURES (64)
//! \brief The location of a single state component within an XSAVE area.
struct XSTATE_FEATURE {
//! \brief The location of a state component within a CPU-specific context
//! structure.
//!
//! This is equivalent to the difference (`ptrdiff_t`) between the return
//! value of `LocateXStateFeature()` and its \a Context argument.
uint32_t Offset;
//! \brief The size of a state component with a CPU-specific context
//! structure.
//!
//! This is equivalent to the size returned by `LocateXStateFeature()` in \a
//! Length.
uint32_t Size;
};
//! \anchor IMAGE_DEBUG_MISC_x
//! \name IMAGE_DEBUG_MISC_*
//!
@ -187,39 +247,4 @@ struct IMAGE_DEBUG_MISC {
#define VER_PLATFORM_WIN32_NT 2
//! \}
//! \anchor PAGE_x
//! \name PAGE_*
//!
//! \brief Memory protection constants for MINIDUMP_MEMORY_INFO::Protect and
//! MINIDUMP_MEMORY_INFO::AllocationProtect.
//! \{
#define PAGE_NOACCESS 0x1
#define PAGE_READONLY 0x2
#define PAGE_READWRITE 0x4
#define PAGE_WRITECOPY 0x8
#define PAGE_EXECUTE 0x10
#define PAGE_EXECUTE_READ 0x20
#define PAGE_EXECUTE_READWRITE 0x40
#define PAGE_EXECUTE_WRITECOPY 0x80
#define PAGE_GUARD 0x100
#define PAGE_NOCACHE 0x200
#define PAGE_WRITECOMBINE 0x400
//! \}
//! \anchor MEM_x
//! \name MEM_*
//!
//! \brief Memory state and type constants for MINIDUMP_MEMORY_INFO::State and
//! MINIDUMP_MEMORY_INFO::Type.
//! \{
#define MEM_COMMIT 0x1000
#define MEM_RESERVE 0x2000
#define MEM_DECOMMIT 0x4000
#define MEM_RELEASE 0x8000
#define MEM_FREE 0x10000
#define MEM_PRIVATE 0x20000
#define MEM_MAPPED 0x40000
#define MEM_RESET 0x80000
//! \}
#endif // CRASHPAD_COMPAT_NON_WIN_WINNT_H_

View File

@ -1,4 +1,4 @@
# Doxyfile 1.8.12
# Doxyfile 1.8.13
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@ -2362,6 +2362,11 @@ DIAFILE_DIRS =
PLANTUML_JAR_PATH =
# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
# configuration file for plantuml.
PLANTUML_CFG_FILE =
# When using plantuml, the specified paths are searched for files specified by
# the !include statement in a plantuml block.

View File

@ -59,6 +59,7 @@
#include "util/mach/child_port_handshake.h"
#include "util/mach/mach_extensions.h"
#include "util/posix/close_stdio.h"
#include "util/posix/signals.h"
#elif defined(OS_WIN)
#include <windows.h>
@ -145,28 +146,6 @@ class CallMetricsRecordNormalExit {
#if defined(OS_MACOSX)
void InstallSignalHandler(const std::vector<int>& signals,
void (*handler)(int, siginfo_t*, void*)) {
struct sigaction sa = {};
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handler;
for (int sig : signals) {
int rv = sigaction(sig, &sa, nullptr);
PCHECK(rv == 0) << "sigaction " << sig;
}
}
void RestoreDefaultSignalHandler(int sig) {
struct sigaction sa = {};
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = SIG_DFL;
int rv = sigaction(sig, &sa, nullptr);
DPLOG_IF(ERROR, rv != 0) << "sigaction " << sig;
}
void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed);
@ -201,97 +180,19 @@ void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
}
Metrics::HandlerCrashed(metrics_code);
RestoreDefaultSignalHandler(sig);
// If the signal was received synchronously resulting from a hardware fault,
// returning from the signal handler will cause the kernel to re-raise it,
// because this handler hasnt done anything to alleviate the condition that
// caused the signal to be raised in the first place. With the default signal
// handler in place, it will cause the same behavior to be taken as though
// this signal handler had never been installed at all (expected to be a
// crash). This is ideal, because the signal is re-raised with the same
// properties and from the same context that initially triggered it, providing
// the best debugging experience.
if ((sig != SIGILL && sig != SIGFPE && sig != SIGBUS && sig != SIGSEGV) ||
!si_code_valid) {
// 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 (resulting in SIGTRAP but advancing the instruction
// pointer), will not reoccur on their own when returning from the signal
// handler. Re-raise them.
//
// Unfortunately, when SIGBUS is received asynchronously via kill(),
// siginfo->si_code makes it appear as though it was actually received via a
// hardware fault. See 10.12.3 xnu-3789.41.3/bsd/dev/i386/unix_signal.c
// sendsig(). An asynchronous SIGBUS will thus cause the handler-crashed
// metric to be logged but will not cause the process to terminate. This
// isnt ideal, but asynchronous SIGBUS is an unexpected condition. The
// alternative, to re-raise here on any SIGBUS, is a bad idea because it
// would lose properties associated with the the original signal, which are
// very valuable for debugging and are visible to a Mach exception handler.
// Since SIGBUS is normally received synchronously in response to a hardware
// fault, dont sweat the unexpected asynchronous case.
//
// Because this signal handler executes with the signal blocked, this
// raise() cannot immediately deliver the signal. Delivery is deferred until
// this signal handler returns and the signal becomes unblocked. The
// re-raised signal will appear with the same context as where it was
// initially triggered.
int rv = raise(sig);
DPLOG_IF(ERROR, rv != 0) << "raise";
}
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
}
void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) {
MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated);
RestoreDefaultSignalHandler(sig);
// Re-raise the signal. See the explanation in HandleCrashSignal(). Note that
// no checks for signals arising from synchronous hardware faults are made
// because termination signals never originate in that way.
int rv = raise(sig);
DPLOG_IF(ERROR, rv != 0) << "raise";
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
}
void InstallCrashHandler() {
// These are the core-generating signals from 10.12.3
// xnu-3789.41.3/bsd/sys/signalvar.h sigprop: entries with SA_CORE are in the
// set.
const int kCrashSignals[] = {SIGQUIT,
SIGILL,
SIGTRAP,
SIGABRT,
SIGEMT,
SIGFPE,
SIGBUS,
SIGSEGV,
SIGSYS};
InstallSignalHandler(
std::vector<int>(&kCrashSignals[0],
&kCrashSignals[arraysize(kCrashSignals)]),
HandleCrashSignal);
Signals::InstallCrashHandlers(HandleCrashSignal, 0, nullptr);
// Not a crash handler, but close enough. These are non-core-generating but
// terminating signals 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.
const int kTerminateSignals[] = {SIGHUP,
SIGINT,
SIGPIPE,
SIGALRM,
SIGTERM,
SIGXCPU,
SIGXFSZ,
SIGVTALRM,
SIGPROF,
SIGUSR1,
SIGUSR2};
InstallSignalHandler(
std::vector<int>(&kTerminateSignals[0],
&kTerminateSignals[arraysize(kTerminateSignals)]),
HandleTerminateSignal);
// Not a crash handler, but close enough.
Signals::InstallTerminateHandlers(HandleTerminateSignal, 0, nullptr);
}
struct ResetSIGTERMTraits {
@ -614,7 +515,7 @@ int HandlerMain(int argc, char* argv[]) {
base::AutoReset<ExceptionHandlerServer*> reset_g_exception_handler_server(
&g_exception_handler_server, &exception_handler_server);
struct sigaction old_sa;
struct sigaction old_sigterm_action;
ScopedResetSIGTERM reset_sigterm;
if (!options.mach_service.empty()) {
// When running from launchd, no no-senders notification could ever be
@ -624,13 +525,10 @@ int HandlerMain(int argc, char* argv[]) {
//
// Set up a SIGTERM handler that will call exception_handler_server.Stop().
// This replaces the HandleTerminateSignal handler for SIGTERM.
struct sigaction sa = {};
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = HandleSIGTERM;
int rv = sigaction(SIGTERM, &sa, &old_sa);
PCHECK(rv == 0) << "sigaction";
reset_sigterm.reset(&old_sa);
if (Signals::InstallHandler(
SIGTERM, HandleSIGTERM, 0, &old_sigterm_action)) {
reset_sigterm.reset(&old_sigterm_action);
}
}
#elif defined(OS_WIN)
// Shut down as late as possible relative to programs we're watching.
@ -692,9 +590,6 @@ int HandlerMain(int argc, char* argv[]) {
upload_thread.Stop();
prune_thread.Stop();
if (histogram_allocator)
histogram_allocator->DeletePersistentLocation();
return EXIT_SUCCESS;
}

46
infra/config/cq.cfg Normal file
View File

@ -0,0 +1,46 @@
# Copyright 2017 The Crashpad Authors. All rights reserved.
#
# 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.
# See https://luci-config.appspot.com/schemas/projects/refs:cq.cfg for the
# documentation of this file format.
version: 1
cq_name: "crashpad"
cq_status_url: "https://chromium-cq-status.appspot.com"
# This is required for gerrit projects.
git_repo_url: "https://chromium.googlesource.com/crashpad/crashpad"
# Crashpad uses gerrit with no special options.
gerrit {}
verifiers {
gerrit_cq_ability {
committer_list: "project-crashpad-committers"
dry_run_access_list: "project-crashpad-committers"
}
try_job {
buckets {
name: "master.client.crashpad"
builders { name: "crashpad_try_mac_dbg" }
builders { name: "crashpad_try_mac_rel" }
builders { name: "crashpad_try_win_x64_dbg" }
builders { name: "crashpad_try_win_x64_rel" }
builders { name: "crashpad_try_win_x86_dbg" }
builders { name: "crashpad_try_win_x86_rel" }
builders { name: "crashpad_try_win_x86_wow64_dbg" }
builders { name: "crashpad_try_win_x86_wow64_rel" }
}
}
}

View File

@ -145,7 +145,7 @@ std::string MinidumpMiscInfoDebugBuildString() {
} // namespace internal
MinidumpMiscInfoWriter::MinidumpMiscInfoWriter()
: MinidumpStreamWriter(), misc_info_() {
: MinidumpStreamWriter(), misc_info_(), has_xstate_data_(false) {
}
MinidumpMiscInfoWriter::~MinidumpMiscInfoWriter() {
@ -330,6 +330,21 @@ void MinidumpMiscInfoWriter::SetBuildString(
debug_build_string);
}
void MinidumpMiscInfoWriter::SetXStateData(
const XSTATE_CONFIG_FEATURE_MSC_INFO& xstate_data) {
DCHECK_EQ(state(), kStateMutable);
misc_info_.XStateData = xstate_data;
has_xstate_data_ = true;
}
void MinidumpMiscInfoWriter::SetProcessCookie(uint32_t process_cookie) {
DCHECK_EQ(state(), kStateMutable);
misc_info_.ProcessCookie = process_cookie;
misc_info_.Flags1 |= MINIDUMP_MISC5_PROCESS_COOKIE;
}
bool MinidumpMiscInfoWriter::Freeze() {
DCHECK_EQ(state(), kStateMutable);
@ -365,6 +380,9 @@ MinidumpStreamType MinidumpMiscInfoWriter::StreamType() const {
size_t MinidumpMiscInfoWriter::CalculateSizeOfObjectFromFlags() const {
DCHECK_GE(state(), kStateFrozen);
if (has_xstate_data_ || (misc_info_.Flags1 & MINIDUMP_MISC5_PROCESS_COOKIE)) {
return sizeof(MINIDUMP_MISC_INFO_5);
}
if (misc_info_.Flags1 & MINIDUMP_MISC4_BUILDSTRING) {
return sizeof(MINIDUMP_MISC_INFO_4);
}

View File

@ -51,10 +51,10 @@ std::string MinidumpMiscInfoDebugBuildString();
//! minidump file.
//!
//! The actual stream written will be a MINIDUMP_MISC_INFO,
//! MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, or MINIDUMP_MISC_INFO_4 stream.
//! Later versions of MINIDUMP_MISC_INFO are supersets of earlier versions. The
//! earliest version that supports all of the information that an object of this
//! class contains will be used.
//! MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, MINIDUMP_MISC_INFO_4, or
//! MINIDUMP_MISC_INFO_5 stream. Later versions of MINIDUMP_MISC_INFO are
//! supersets of earlier versions. The earliest version that supports all of the
//! information that an object of this class contains will be used.
class MinidumpMiscInfoWriter final : public internal::MinidumpStreamWriter {
public:
MinidumpMiscInfoWriter();
@ -107,6 +107,15 @@ class MinidumpMiscInfoWriter final : public internal::MinidumpStreamWriter {
void SetBuildString(const std::string& build_string,
const std::string& debug_build_string);
// TODO(mark): Provide a better interface than this. Dont force callers to
// build their own XSTATE_CONFIG_FEATURE_MSC_INFO structure.
//
//! \brief Sets MINIDUMP_MISC_INFO_5::XStateData.
void SetXStateData(const XSTATE_CONFIG_FEATURE_MSC_INFO& xstate_data);
//! \brief Sets the field referenced by #MINIDUMP_MISC5_PROCESS_COOKIE.
void SetProcessCookie(uint32_t process_cookie);
protected:
// MinidumpWritable:
bool Freeze() override;
@ -123,6 +132,7 @@ class MinidumpMiscInfoWriter final : public internal::MinidumpStreamWriter {
size_t CalculateSizeOfObjectFromFlags() const;
MINIDUMP_MISC_INFO_N misc_info_;
bool has_xstate_data_;
DISALLOW_COPY_AND_ASSIGN(MinidumpMiscInfoWriter);
};

View File

@ -21,9 +21,11 @@
#include <utility>
#include "base/compiler_specific.h"
#include "base/format_macros.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "gtest/gtest.h"
#include "minidump/minidump_file_writer.h"
@ -164,6 +166,29 @@ void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_4>(
}
}
template <>
void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_5>(
const MINIDUMP_MISC_INFO_5* expected,
const MINIDUMP_MISC_INFO_5* observed) {
ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_4>(
reinterpret_cast<const MINIDUMP_MISC_INFO_4*>(expected),
reinterpret_cast<const MINIDUMP_MISC_INFO_4*>(observed));
EXPECT_EQ(expected->XStateData.SizeOfInfo, observed->XStateData.SizeOfInfo);
EXPECT_EQ(expected->XStateData.ContextSize, observed->XStateData.ContextSize);
EXPECT_EQ(expected->XStateData.EnabledFeatures,
observed->XStateData.EnabledFeatures);
for (size_t feature_index = 0;
feature_index < arraysize(observed->XStateData.Features);
++feature_index) {
SCOPED_TRACE(base::StringPrintf("feature_index %" PRIuS, feature_index));
EXPECT_EQ(expected->XStateData.Features[feature_index].Offset,
observed->XStateData.Features[feature_index].Offset);
EXPECT_EQ(expected->XStateData.Features[feature_index].Size,
observed->XStateData.Features[feature_index].Size);
}
EXPECT_EQ(expected->ProcessCookie, observed->ProcessCookie);
}
TEST(MinidumpMiscInfoWriter, Empty) {
MinidumpFileWriter minidump_file_writer;
auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter());
@ -490,7 +515,7 @@ TEST(MinidumpMiscInfoWriter, BuildStringsOverflow) {
MinidumpFileWriter minidump_file_writer;
auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter());
MINIDUMP_MISC_INFO_4 tmp;
MINIDUMP_MISC_INFO_N tmp;
ALLOW_UNUSED_LOCAL(tmp);
std::string build_string(arraysize(tmp.BuildString) + 1, 'B');
std::string debug_build_string(arraysize(tmp.DbgBldStr), 'D');
@ -520,6 +545,63 @@ TEST(MinidumpMiscInfoWriter, BuildStringsOverflow) {
ExpectMiscInfoEqual(&expected, observed);
}
TEST(MinidumpMiscInfoWriter, XStateData) {
MinidumpFileWriter minidump_file_writer;
auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter());
const XSTATE_CONFIG_FEATURE_MSC_INFO kXStateData = {
sizeof(XSTATE_CONFIG_FEATURE_MSC_INFO),
1024,
0x000000000000005f,
{
{0, 512},
{512, 256},
{768, 128},
{896, 64},
{960, 32},
{0, 0},
{992, 32},
}};
misc_info_writer->SetXStateData(kXStateData);
minidump_file_writer.AddStream(std::move(misc_info_writer));
StringFile string_file;
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
const MINIDUMP_MISC_INFO_5* observed = nullptr;
ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
MINIDUMP_MISC_INFO_5 expected = {};
expected.XStateData = kXStateData;
ExpectMiscInfoEqual(&expected, observed);
}
TEST(MinidumpMiscInfoWriter, ProcessCookie) {
MinidumpFileWriter minidump_file_writer;
auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter());
const uint32_t kProcessCookie = 0x12345678;
misc_info_writer->SetProcessCookie(kProcessCookie);
minidump_file_writer.AddStream(std::move(misc_info_writer));
StringFile string_file;
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
const MINIDUMP_MISC_INFO_5* observed = nullptr;
ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
MINIDUMP_MISC_INFO_5 expected = {};
expected.Flags1 = MINIDUMP_MISC5_PROCESS_COOKIE;
expected.ProcessCookie = kProcessCookie;
ExpectMiscInfoEqual(&expected, observed);
}
TEST(MinidumpMiscInfoWriter, Everything) {
MinidumpFileWriter minidump_file_writer;
auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter());

View File

@ -19,7 +19,7 @@
#define PACKAGE_COPYRIGHT \
"Copyright " PACKAGE_COPYRIGHT_YEAR " " PACKAGE_COPYRIGHT_OWNER
#define PACKAGE_COPYRIGHT_OWNER "The Crashpad Authors"
#define PACKAGE_COPYRIGHT_YEAR "2016"
#define PACKAGE_COPYRIGHT_YEAR "2017"
#define PACKAGE_NAME "Crashpad"
#define PACKAGE_STRING PACKAGE_NAME " " PACKAGE_VERSION
#define PACKAGE_TARNAME "crashpad"

View File

@ -127,7 +127,6 @@
'link_settings': {
'libraries': [
'-lpowrprof.lib',
'-lversion.lib',
],
},
}],

View File

@ -106,17 +106,17 @@ void SystemSnapshotWin::Initialize(ProcessReaderWin* process_reader) {
os_version_major_ = ffi.dwFileVersionMS >> 16;
os_version_minor_ = ffi.dwFileVersionMS & 0xffff;
os_version_bugfix_ = ffi.dwFileVersionLS >> 16;
os_version_build_ =
base::StringPrintf("%d", ffi.dwFileVersionLS & 0xffff);
os_version_build_ = base::StringPrintf("%u", ffi.dwFileVersionLS & 0xffff);
os_version_full_ = base::StringPrintf(
"%s %d.%d.%d.%s%s",
"%s %u.%u.%u.%s%s",
os_name.c_str(),
os_version_major_,
os_version_minor_,
os_version_bugfix_,
os_version_build_.c_str(),
flags_string.empty() ? "" : (std::string(" (") + flags_string + ")")
.c_str());
flags_string.empty()
? ""
: (std::string(" (") + flags_string + ")").c_str());
}
INITIALIZATION_STATE_SET_VALID(initialized_);

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <fcntl.h>
#include <getopt.h>
#include <libgen.h>
#include <stdio.h>
@ -279,6 +280,10 @@ int CatchExceptionToolMain(int argc, char* argv[]) {
return EXIT_FAILURE;
}
options.file = file_owner.get();
if (fcntl(fileno(options.file), F_SETFD, FD_CLOEXEC) == -1) {
PLOG(ERROR) << "fcntl " << options.file_path;
return EXIT_FAILURE;
}
}
int exceptions_handled = 0;

View File

@ -80,10 +80,11 @@ FileHandle OpenFileForOutput(int rdwr_or_wronly,
const base::FilePath& path,
FileWriteMode mode,
FilePermissions permissions) {
int flags = O_NOCTTY | O_CLOEXEC;
DCHECK(rdwr_or_wronly & (O_RDWR | O_WRONLY));
DCHECK_EQ(rdwr_or_wronly & ~(O_RDWR | O_WRONLY), 0);
int flags = rdwr_or_wronly;
flags |= rdwr_or_wronly;
switch (mode) {
case FileWriteMode::kReuseOrFail:
@ -118,7 +119,8 @@ FileOperationResult WriteFile(FileHandle file,
}
FileHandle OpenFileForRead(const base::FilePath& path) {
return HANDLE_EINTR(open(path.value().c_str(), O_RDONLY));
return HANDLE_EINTR(
open(path.value().c_str(), O_RDONLY | O_NOCTTY | O_CLOEXEC));
}
FileHandle OpenFileForWrite(const base::FilePath& path,

View File

@ -37,8 +37,10 @@ class Xattr : public testing::Test {
void SetUp() override {
path_ = temp_dir_.path().Append("xattr_file");
base::ScopedFD tmp(HANDLE_EINTR(
open(path_.value().c_str(), O_CREAT | O_TRUNC, 0644)));
base::ScopedFD tmp(
HANDLE_EINTR(open(path_.value().c_str(),
O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY | O_CLOEXEC,
0644)));
EXPECT_GE(tmp.get(), 0) << ErrnoMessage("open");
}

View File

@ -16,11 +16,14 @@
#include <CoreFoundation/CoreFoundation.h>
#import <Foundation/Foundation.h>
#include <sys/utsname.h>
#include "base/mac/foundation_util.h"
#import "base/mac/scoped_nsobject.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "build/build_config.h"
#include "package.h"
#include "third_party/apple_cf/CFStreamAbstract.h"
#include "util/file/file_io.h"
#include "util/misc/implicit_cast.h"
@ -30,6 +33,80 @@ namespace crashpad {
namespace {
NSString* AppendEscapedFormat(NSString* base,
NSString* format,
NSString* data) {
return [base stringByAppendingFormat:
format,
[data stringByAddingPercentEncodingWithAllowedCharacters:
[[NSCharacterSet
characterSetWithCharactersInString:
@"()<>@,;:\\\"/[]?={} \t"] invertedSet]]];
}
// This builds the same User-Agent string that CFNetwork would build internally,
// but it uses PACKAGE_NAME and PACKAGE_VERSION in place of values obtained from
// the main bundles Info.plist.
NSString* UserAgentString() {
NSString* user_agent = [NSString string];
// CFNetwork would use the main bundles CFBundleName, or the main
// executables filename if none.
user_agent = AppendEscapedFormat(
user_agent, @"%@", [NSString stringWithUTF8String:PACKAGE_NAME]);
// CFNetwork would use the main bundles CFBundleVersion, or the string
// “(unknown version)” if none.
user_agent = AppendEscapedFormat(
user_agent, @"/%@", [NSString stringWithUTF8String:PACKAGE_VERSION]);
// Expected to be CFNetwork.
NSBundle* nsurl_bundle = [NSBundle bundleForClass:[NSURLRequest class]];
NSString* bundle_name = base::mac::ObjCCast<NSString>([nsurl_bundle
objectForInfoDictionaryKey:base::mac::CFToNSCast(kCFBundleNameKey)]);
if (bundle_name) {
user_agent = AppendEscapedFormat(user_agent, @" %@", bundle_name);
NSString* bundle_version = base::mac::ObjCCast<NSString>([nsurl_bundle
objectForInfoDictionaryKey:base::mac::CFToNSCast(kCFBundleVersionKey)]);
if (bundle_version) {
user_agent = AppendEscapedFormat(user_agent, @"/%@", bundle_version);
}
}
utsname os;
if (uname(&os) != 0) {
PLOG(WARNING) << "uname";
} else {
user_agent = AppendEscapedFormat(
user_agent, @" %@", [NSString stringWithUTF8String:os.sysname]);
user_agent = AppendEscapedFormat(
user_agent, @"/%@", [NSString stringWithUTF8String:os.release]);
// CFNetwork just uses the equivalent of os.machine to obtain the native
// (kernel) architecture. Here, give the process architecture as well as
// the native architecture. Use the same strings that the kernel would, so
// that they can be de-duplicated.
#if defined(ARCH_CPU_X86)
NSString* arch = @"i386";
#elif defined(ARCH_CPU_X86_64)
NSString* arch = @"x86_64";
#else
#error Port
#endif
user_agent = AppendEscapedFormat(user_agent, @" (%@", arch);
NSString* machine = [NSString stringWithUTF8String:os.machine];
if (![machine isEqualToString:arch]) {
user_agent = AppendEscapedFormat(user_agent, @"; %@", machine);
}
user_agent = [user_agent stringByAppendingString:@")"];
}
return user_agent;
}
// An implementation of CFReadStream. This implements the V0 callback
// scheme.
class HTTPBodyStreamCFReadStream {
@ -171,6 +248,13 @@ bool HTTPTransportMac::ExecuteSynchronously(std::string* response_body) {
timeoutInterval:timeout()];
[request setHTTPMethod:base::SysUTF8ToNSString(method())];
// If left to its own devices, CFNetwork would build a user-agent string
// based on keys in the main bundles Info.plist, giving ugly results if
// there is no Info.plist. Provide a User-Agent string similar to the one
// that CFNetwork would use, but with appropriate values in place of the
// Info.plist-derived strings.
[request setValue:UserAgentString() forHTTPHeaderField:@"User-Agent"];
for (const auto& pair : headers()) {
[request setValue:base::SysUTF8ToNSString(pair.second)
forHTTPHeaderField:base::SysUTF8ToNSString(pair.first)];

View File

@ -28,15 +28,59 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "package.h"
#include "util/file/file_io.h"
#include "util/numeric/safe_assignment.h"
#include "util/net/http_body.h"
#include "util/win/module_version.h"
namespace crashpad {
namespace {
const wchar_t kWinHttpDll[] = L"winhttp.dll";
std::string UserAgent() {
std::string user_agent =
base::StringPrintf("%s/%s WinHTTP", PACKAGE_NAME, PACKAGE_VERSION);
VS_FIXEDFILEINFO version;
if (GetModuleVersionAndType(base::FilePath(kWinHttpDll), &version)) {
user_agent.append(base::StringPrintf("/%u.%u.%u.%u",
version.dwFileVersionMS >> 16,
version.dwFileVersionMS & 0xffff,
version.dwFileVersionLS >> 16,
version.dwFileVersionLS & 0xffff));
}
if (GetModuleVersionAndType(base::FilePath(L"kernel32.dll"), &version) &&
(version.dwFileOS & VOS_NT_WINDOWS32) == VOS_NT_WINDOWS32) {
user_agent.append(base::StringPrintf(" Windows_NT/%u.%u.%u.%u (",
version.dwFileVersionMS >> 16,
version.dwFileVersionMS & 0xffff,
version.dwFileVersionLS >> 16,
version.dwFileVersionLS & 0xffff));
#if defined(ARCH_CPU_X86)
user_agent.append("x86");
#elif defined(ARCH_CPU_X86_64)
user_agent.append("x64");
#else
#error Port
#endif
BOOL is_wow64;
if (!IsWow64Process(GetCurrentProcess(), &is_wow64)) {
PLOG(WARNING) << "IsWow64Process";
} else if (is_wow64) {
user_agent.append("; WoW64");
}
user_agent.append(1, ')');
}
return user_agent;
}
// PLOG doesn't work for messages from WinHTTP, so we need to use
// FORMAT_MESSAGE_FROM_HMODULE + the dll name manually here.
std::string WinHttpMessage(const char* extra) {
@ -45,7 +89,7 @@ std::string WinHttpMessage(const char* extra) {
DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_HMODULE;
DWORD len = FormatMessageA(flags,
GetModuleHandle(L"winhttp.dll"),
GetModuleHandle(kWinHttpDll),
error_code,
0,
msgbuf,
@ -93,12 +137,11 @@ HTTPTransportWin::~HTTPTransportWin() {
}
bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) {
ScopedHINTERNET session(
WinHttpOpen(base::UTF8ToUTF16(PACKAGE_NAME "/" PACKAGE_VERSION).c_str(),
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0));
ScopedHINTERNET session(WinHttpOpen(base::UTF8ToUTF16(UserAgent()).c_str(),
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0));
if (!session.get()) {
LOG(ERROR) << WinHttpMessage("WinHttpOpen");
return false;
@ -136,6 +179,16 @@ bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) {
std::wstring extra_info(url_components.lpszExtraInfo,
url_components.dwExtraInfoLength);
// Use url_path, and get the query parameter from extra_info, up to the first
// #, if any. See RFC 7230 §5.3.1 and RFC 3986 §3.4. Beware that when this is
// used to POST data, the query parameters generally belong in the request
// body and not in the URL request target. Its legal for them to be in both
// places, but the interpretation is subject to whatever the client and server
// agree on. This honors whatever was passed in, matching other platforms, but
// youve been warned!
std::wstring request_target(
url_path.append(extra_info.substr(0, extra_info.find(L'#'))));
ScopedHINTERNET connect(WinHttpConnect(
session.get(), host_name.c_str(), url_components.nPort, 0));
if (!connect.get()) {
@ -146,7 +199,7 @@ bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) {
ScopedHINTERNET request(WinHttpOpenRequest(
connect.get(),
base::UTF8ToUTF16(method()).c_str(),
url_path.c_str(),
request_target.c_str(),
nullptr,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,

View File

@ -211,7 +211,7 @@ void CloseMultipleNowOrOnExec(int fd, int preserve_fd) {
// do_prlimit() and kernel/sysctl.c fs_table. Inability to open this file is
// not considered an error, because /proc may not be available or usable.
{
base::ScopedFILE nr_open_file(fopen("/proc/sys/fs/nr_open", "r"));
base::ScopedFILE nr_open_file(fopen("/proc/sys/fs/nr_open", "re"));
if (nr_open_file.get() != nullptr) {
int nr_open;
if (fscanf(nr_open_file.get(), "%d\n", &nr_open) == 1 &&

View File

@ -27,7 +27,8 @@ namespace crashpad {
namespace {
void CloseStdioStream(int desired_fd, int oflag) {
base::ScopedFD fd(HANDLE_EINTR(open(_PATH_DEVNULL, oflag)));
base::ScopedFD fd(
HANDLE_EINTR(open(_PATH_DEVNULL, oflag | O_NOCTTY | O_CLOEXEC)));
if (fd == desired_fd) {
// Weird, but play along.
ignore_result(fd.release());

View File

@ -103,7 +103,7 @@ void TestSelfProcess(const ProcessInfo& process_info) {
#elif defined(OS_LINUX) || defined(OS_ANDROID)
std::vector<std::string> expect_arg_vector;
{
base::ScopedFILE cmdline(fopen("/proc/self/cmdline", "r"));
base::ScopedFILE cmdline(fopen("/proc/self/cmdline", "re"));
ASSERT_NE(nullptr, cmdline.get()) << ErrnoMessage("fopen");
int expect_arg_char;

281
util/posix/signals.cc Normal file
View File

@ -0,0 +1,281 @@
// Copyright 2017 The Crashpad Authors. All rights reserved.
//
// 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 <vector>
#include "base/logging.h"
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 defined(OS_LINUX)
SIGXCPU,
SIGXFSZ,
#endif // defined(OS_LINUX)
};
// 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 defined(OS_MACOSX)
SIGXCPU,
SIGXFSZ,
#endif // defined(OS_MACOSX)
#if defined(OS_LINUX)
SIGIO,
#endif // defined(OS_LINUX)
};
bool InstallHandlers(const std::vector<int>& signals,
Signals::Handler handler,
int flags,
Signals::OldActions* old_actions) {
bool success = true;
for (int sig : signals) {
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, arraysize(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;
}
return true;
}
// static
bool Signals::InstallCrashHandlers(Handler handler,
int flags,
OldActions* old_actions) {
return InstallHandlers(
std::vector<int>(kCrashSignals, kCrashSignals + arraysize(kCrashSignals)),
handler,
flags,
old_actions);
}
// static
bool Signals::InstallTerminateHandlers(Handler handler,
int flags,
OldActions* old_actions) {
return InstallHandlers(
std::vector<int>(kTerminateSignals,
kTerminateSignals + arraysize(kTerminateSignals)),
handler,
flags,
old_actions);
}
// 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 is received asynchronously via kill(),
// siginfo->si_code makes it appear as though it was actually received via a
// hardware fault. See 10.12.3 xnu-3789.41.3/bsd/dev/i386/unix_signal.c
// sendsig(). Asynchronous SIGBUS will not re-raise itself autonomously, but
// this function (acting on information from the kernel) behaves as though it
// will. This isnt ideal, but asynchronous SIGBUS is an unexpected condition.
// The alternative, to never treat SIGBUS 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 SIGBUS is normally received
// synchronously in response to a hardware fault, dont sweat the unexpected
// asynchronous case.
//
// SIGSEGV on macOS originating from a general protection fault is a more
// difficult case: si_code is cleared, making the signal appear asynchronous.
// See 10.12.3 xnu-3789.41.3/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.
const 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);
}
// 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, arraysize(kCrashSignals));
}
// static
bool Signals::IsTerminateSignal(int sig) {
return IsSignalInSet(sig, kTerminateSignals, arraysize(kTerminateSignals));
}
} // namespace crashpad

228
util/posix/signals.h Normal file
View File

@ -0,0 +1,228 @@
// Copyright 2017 The Crashpad Authors. All rights reserved.
//
// 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.
#ifndef CRASHPAD_UTIL_POSIX_SIGNALS_H_
#define CRASHPAD_UTIL_POSIX_SIGNALS_H_
#include <signal.h>
#include "base/macros.h"
namespace crashpad {
//! \brief Utilities for handling POSIX signals.
class Signals {
public:
//! \brief The type used for `struct sigaction::sa_sigaction`.
using Handler = void (*)(int, siginfo_t*, void*);
//! \brief A group of `struct sigaction` structures corresponding to a set
//! of signals previous actions, addressable by signal number.
//!
//! This type is used to store previous signal actions when new actions are
//! installed in batch by InstallCrashHandlers() or
//! InstallTerminateHandlers().
//!
//! This object is not initialized by any constructor. Its expected initial
//! state is to have its contents filled with zeroes. Because signal handlers
//! are stateless (there is no “context” parameter), any state must be
//! accessed via objects of static storage duration, and it is expected that
//! objects of this class will only ever exist with static storage duration,
//! which in the absence of a constructor will be zero-initialized as
//! expected. In the event that an object of this class must exist with a
//! different storage duration, such as automatic or dynamic storage duration,
//! it must be explicitly initialized. For example: `OldActions old_actions =
//! {};`.
class OldActions {
public:
// DISALLOW_COPY_AND_ASSIGN removes the default constructor, so explicitly
// opt for it. This should not result in any static initialization code even
// when an object of this class is given static storage duration.
OldActions() = default;
//! \brief Returns a `struct sigaction` structure corresponding to the
//! given signal.
//!
//! \note This method is safe to call from a signal handler.
struct sigaction* ActionForSignal(int sig);
private:
// As a small storage optimization, dont waste any space on a slot for
// signal 0, because there is no signal 0.
struct sigaction actions_[NSIG - 1];
DISALLOW_COPY_AND_ASSIGN(OldActions);
};
//! \brief Installs a new signal handler.
//!
//! \param[in] sig The signal number to handle.
//! \param[in] handler A signal-handling function to execute, used as the
//! `struct sigaction::sa_sigaction` field when calling `sigaction()`.
//! \param[in] flags Flags to pass to `sigaction()` in the `struct
//! sigaction::sa_flags` field. `SA_SIGINFO` will be specified implicitly.
//! \param[out] old_action The previous action for the signal, replaced by the
//! new action. May be `nullptr` if not needed.
//!
//! \return `true` on success. `false` on failure with a message logged.
//!
//! \warning This function may not be called from a signal handler because of
//! its use of logging. See RestoreHandlerAndReraiseSignalOnReturn()
//! instead.
static bool InstallHandler(int sig,
Handler handler,
int flags,
struct sigaction* old_action);
//! \brief Installs a new signal handler for all signals associated with
//! crashes.
//!
//! Signals associated with crashes are those whose default dispositions
//! involve creating a core dump. The precise set of signals involved varies
//! between operating systems.
//!
//! A single signal may either be associated with a crash or with termination
//! (see InstallTerminateHandlers()), and perhaps neither, but never both.
//!
//! \param[in] handler A signal-handling function to execute, used as the
//! `struct sigaction::sa_sigaction` field when calling `sigaction()`.
//! \param[in] flags Flags to pass to `sigaction()` in the `struct
//! sigaction::sa_flags` field. `SA_SIGINFO` will be specified implicitly.
//! \param[out] old_actions The previous actions for the signals, replaced by
//! the new action. May be `nullptr` if not needed. The same \a
//! old_actions object may be used for calls to both this function and
//! InstallTerminateHandlers().
//!
//! \return `true` on success. `false` on failure with a message logged.
//!
//! \warning This function may not be called from a signal handler because of
//! its use of logging. See RestoreHandlerAndReraiseSignalOnReturn()
//! instead.
static bool InstallCrashHandlers(Handler handler,
int flags,
OldActions* old_actions);
//! \brief Installs a new signal handler for all signals associated with
//! termination.
//!
//! Signals associated with termination are those whose default dispositions
//! involve terminating the process without creating a core dump. The precise
//! set of signals involved varies between operating systems.
//!
//! A single signal may either be associated with termination or with a
//! crash (see InstalCrashHandlers()), and perhaps neither, but never both.
//!
//! \param[in] handler A signal-handling function to execute, used as the
//! `struct sigaction::sa_sigaction` field when calling `sigaction()`.
//! \param[in] flags Flags to pass to `sigaction()` in the `struct
//! sigaction::sa_flags` field. `SA_SIGINFO` will be specified implicitly.
//! \param[out] old_actions The previous actions for the signals, replaced by
//! the new action. May be `nullptr` if not needed. The same \a
//! old_actions object may be used for calls to both this function and
//! InstallCrashHandlers().
//!
//! \return `true` on success. `false` on failure with a message logged.
//!
//! \warning This function may not be called from a signal handler because of
//! its use of logging. See RestoreHandlerAndReraiseSignalOnReturn()
//! instead.
static bool InstallTerminateHandlers(Handler handler,
int flags,
OldActions* old_actions);
//! \brief Determines whether a signal will be re-raised autonomously upon
//! return from a signal handler.
//!
//! Certain signals, when generated synchronously in response to a hardware
//! fault, are unrecoverable. Upon return from the signal handler, the same
//! action that triggered the signal to be raised initially will be retried,
//! and unless the signal handler took action to mitigate this error, the same
//! signal will be re-raised. As an example, a CPU will not be able to read
//! unmapped memory (causing `SIGSEGV`), thus the signal will be re-raised
//! upon return from the signal handler unless the signal handler established
//! a memory mapping where required.
//!
//! It is important to distinguish between these synchronous signals generated
//! in response to a hardware fault and signals generated asynchronously or in
//! software. As an example, `SIGSEGV` will not re-raise autonomously if sent
//! by `kill()`.
//!
//! This function distinguishes between signals that can re-raise
//! autonomously, and for those that can, between instances of the signal that
//! were generated synchronously in response to a hardware fault and instances
//! that were generated by other means.
//!
//! \param[in] siginfo A pointer to a `siginfo_t` object received by a signal
//! handler.
//!
//! \return `true` if the signal being handled will re-raise itself
//! autonomously upon return from a signal handler. `false` if it will
//! not. When this function returns `false`, a signal can still be
//! re-raised upon return from a signal handler by calling `raise()` from
//! within the signal handler.
//!
//! \note This function is safe to call from a signal handler.
static bool WillSignalReraiseAutonomously(const siginfo_t* siginfo);
//! \brief Restores a previous signal action and arranges to re-raise a signal
//! on return from a signal handler.
//!
//! \param[in] siginfo A pointer to a `siginfo_t` object received by a signal
//! handler.
//! \param[in] old_action The previous action for the signal, which will be
//! re-established as the signals action. May be `nullptr`, which directs
//! the default action for the signal to be used.
//!
//! If this function fails, it will immediately call `_exit()` and set an exit
//! status of `191`.
//!
//! \note This function may only be called from a signal handler.
static void RestoreHandlerAndReraiseSignalOnReturn(
const siginfo_t* siginfo,
const struct sigaction* old_action);
//! \brief Determines whether a signal is associated with a crash.
//!
//! Signals associated with crashes are those whose default dispositions
//! involve creating a core dump. The precise set of signals involved varies
//! between operating systems.
//!
//! \param[in] sig The signal to test.
//!
//! \return `true` if \a sig is associated with a crash. `false` otherwise.
//!
//! \note This function is safe to call from a signal handler.
static bool IsCrashSignal(int sig);
//! \brief Determines whether a signal is associated with termination.
//!
//! Signals associated with termination are those whose default dispositions
//! involve terminating the process without creating a core dump. The precise
//! set of signals involved varies between operating systems.
//!
//! \param[in] sig The signal to test.
//!
//! \return `true` if \a sig is associated with termination. `false`
//! otherwise.
//!
//! \note This function is safe to call from a signal handler.
static bool IsTerminateSignal(int sig);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Signals);
};
} // namespace crashpad
#endif // CRASHPAD_UTIL_POSIX_SIGNALS_H_

579
util/posix/signals_test.cc Normal file
View File

@ -0,0 +1,579 @@
// Copyright 2017 The Crashpad Authors. All rights reserved.
//
// 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/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <limits>
#include "base/compiler_specific.h"
#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"
namespace crashpad {
namespace test {
namespace {
constexpr int kUnexpectedExitStatus = 3;
// Keep synchronized with CauseSignal().
bool CanCauseSignal(int sig) {
return sig == SIGABRT ||
sig == SIGALRM ||
sig == SIGBUS ||
#if !defined(ARCH_CPU_ARM64)
sig == SIGFPE ||
#endif // !defined(ARCH_CPU_ARM64)
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
sig == SIGILL ||
#endif // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL
sig == SIGPIPE ||
sig == SIGSEGV ||
#if defined(OS_MACOSX)
sig == SIGSYS ||
#endif // OS_MACOSX
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
sig == SIGTRAP ||
#endif // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
false;
}
// Keep synchronized with CanCauseSignal().
void CauseSignal(int sig) {
switch (sig) {
case SIGABRT: {
abort();
break;
}
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: {
char* mapped;
{
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);
}
mapped = reinterpret_cast<char*>(mmap(nullptr,
getpagesize(),
PROT_READ | PROT_WRITE,
MAP_PRIVATE,
fd.get(),
0));
if (mapped == MAP_FAILED) {
PLOG(ERROR) << "mmap";
}
}
if (mapped == MAP_FAILED) {
_exit(kUnexpectedExitStatus);
}
*mapped = 0;
_exit(kUnexpectedExitStatus);
break;
}
#if !defined(ARCH_CPU_ARM64)
// ARM64 has hardware integer division instructions that dont generate a
// trap for divide-by-zero, so this doesnt produce SIGFPE.
case SIGFPE: {
// Optimization makes this tricky, so get zero from a system call likely
// to succeed, and try to do something with the result.
struct stat stat_buf;
int zero = stat("/", &stat_buf);
if (zero == -1) {
// Its important to check |== -1| and not |!= 0|. An optimizer is free
// to discard an |== 0| branch entirely, because division by zero is
// undefined behavior.
PLOG(ERROR) << "stat";
_exit(kUnexpectedExitStatus);
}
int quotient = 2 / zero;
fstat(quotient, &stat_buf);
break;
}
#endif // ARCH_CPU_ARM64
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
case SIGILL: {
// __builtin_trap() causes SIGTRAP on arm64 on Android.
__builtin_trap();
break;
}
#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: {
volatile int* i = nullptr;
*i = 0;
break;
}
#if defined(OS_MACOSX)
case SIGSYS: {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
int rv = syscall(0);
#pragma clang diagnostic pop
if (rv != 0) {
PLOG(ERROR) << "syscall";
_exit(kUnexpectedExitStatus);
}
break;
}
#endif // OS_MACOSX
#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);
break;
}
}
}
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)
: Multiprocess(),
sig_(sig),
test_type_(test_type),
signal_source_(signal_source) {}
~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::InstallCrashHandlers;
} else if (Signals::IsTerminateSignal(sig_)) {
install_handlers = Signals::InstallTerminateHandlers;
} else {
_exit(kUnexpectedExitStatus);
}
switch (test_type_) {
case TestType::kDefaultHandler: {
// Dont 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(0, sigaction(sig_, &action, nullptr))
<< 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_);
break;
case SignalSource::kRaise:
raise(sig_);
break;
}
_exit(kUnexpectedExitStatus);
}
int sig_;
TestType test_type_;
SignalSource signal_source_;
static Signals::OldActions old_actions_;
DISALLOW_COPY_AND_ASSIGN(SignalsTest);
};
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},
};
for (size_t index = 0; index < arraysize(kTestData); ++index) {
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(test_data.result,
Signals::WillSignalReraiseAutonomously(&siginfo));
}
}
TEST(Signals, Cause_DefaultHandler) {
for (int sig = 1; sig < NSIG; ++sig) {
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
if (!CanCauseSignal(sig)) {
continue;
}
SignalsTest test(SignalsTest::TestType::kDefaultHandler,
SignalsTest::SignalSource::kCause,
sig);
test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);
test.Run();
}
}
TEST(Signals, Cause_HandlerExits) {
for (int sig = 1; sig < NSIG; ++sig) {
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
if (!CanCauseSignal(sig)) {
continue;
}
SignalsTest test(SignalsTest::TestType::kHandlerExits,
SignalsTest::SignalSource::kCause,
sig);
test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,
SignalsTest::kExitingHandlerExitStatus);
test.Run();
}
}
TEST(Signals, Cause_HandlerReraisesToDefault) {
for (int sig = 1; sig < NSIG; ++sig) {
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
if (!CanCauseSignal(sig)) {
continue;
}
SignalsTest test(SignalsTest::TestType::kHandlerReraisesToDefault,
SignalsTest::SignalSource::kCause,
sig);
test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);
test.Run();
}
}
TEST(Signals, Cause_HandlerReraisesToPrevious) {
for (int sig = 1; sig < NSIG; ++sig) {
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
if (!CanCauseSignal(sig)) {
continue;
}
SignalsTest test(SignalsTest::TestType::kHandlerReraisesToPrevious,
SignalsTest::SignalSource::kCause,
sig);
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);
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);
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;
}
#if defined(OS_MACOSX)
if (sig == SIGBUS) {
// Signal handlers cant distinguish between SIGBUS arising out of a
// hardware fault and SIGBUS raised asynchronously.
// Signals::RestoreHandlerAndReraiseSignalOnReturn() assumes that SIGBUS
// comes from a hardware fault, but this test uses raise(), so the
// re-raise test must be skipped.
continue;
}
#endif // defined(OS_MACOSX)
SignalsTest test(SignalsTest::TestType::kHandlerReraisesToDefault,
SignalsTest::SignalSource::kRaise,
sig);
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;
}
#if defined(OS_MACOSX)
if (sig == SIGBUS) {
// Signal handlers cant distinguish between SIGBUS arising out of a
// hardware fault and SIGBUS raised asynchronously.
// Signals::RestoreHandlerAndReraiseSignalOnReturn() assumes that SIGBUS
// comes from a hardware fault, but this test uses raise(), so the
// re-raise test must be skipped.
continue;
}
#endif // defined(OS_MACOSX)
SignalsTest test(SignalsTest::TestType::kHandlerReraisesToPrevious,
SignalsTest::SignalSource::kRaise,
sig);
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

View File

@ -135,6 +135,8 @@
'posix/drop_privileges.h',
'posix/process_info.h',
'posix/process_info_mac.cc',
'posix/signals.cc',
'posix/signals.h',
'posix/symbolic_constants_posix.cc',
'posix/symbolic_constants_posix.h',
'stdlib/aligned_allocator.cc',
@ -271,6 +273,7 @@
'link_settings': {
'libraries': [
'-luser32.lib',
'-lversion.lib',
'-lwinhttp.lib',
],
},

View File

@ -74,6 +74,7 @@
'numeric/in_range_cast_test.cc',
'numeric/int128_test.cc',
'posix/process_info_test.cc',
'posix/signals_test.cc',
'posix/symbolic_constants_posix_test.cc',
'stdlib/aligned_allocator_test.cc',
'stdlib/map_insert_test.cc',