crashpad/snapshot/mac/exception_snapshot_mac.cc
Mark Mentovai 1baff4ff92 Accept non-fatal resource exceptions without generating crash reports.
This adds IsExceptionNonfatalResource() and its test, and uses it in
crashpad_handler. When non-fatal resource exceptions are encountered, no
crash report is generated. crashpad_handler swallows these exceptions.
Alternatively, it could allow them to be sent to the system’s host-level
resource exception handler, normally com.apple.ReportCrash.root, which
would allow them to be processed in the same way as when Crashpad is not
in use. I’m not sure which option is better. I chose to swallow them
because there doesn’t appear to be much value in letting
com.apple.ReportCrash.root and spindump look at them.

This also moves ExcCrashRecoverOriginalException() to the new file as a
sibling of IsExceptionNonfatalResource(). This provides better
organization.

BUG=crashpad:35, chromium:474163, chromium:474326
TEST=crashpad_util_test ExceptionTypes.IsExceptionNonfatalResource
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/1066243002
2015-04-08 17:46:09 -04:00

254 lines
9.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

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

// Copyright 2014 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 "snapshot/mac/exception_snapshot_mac.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "snapshot/mac/cpu_context_mac.h"
#include "snapshot/mac/process_reader.h"
#include "util/mach/exception_behaviors.h"
#include "util/mach/exception_types.h"
#include "util/mach/symbolic_constants_mach.h"
#include "util/numeric/safe_assignment.h"
namespace crashpad {
namespace internal {
ExceptionSnapshotMac::ExceptionSnapshotMac()
: ExceptionSnapshot(),
context_union_(),
context_(),
codes_(),
thread_id_(0),
exception_address_(0),
exception_(0),
exception_code_0_(0),
initialized_() {
}
ExceptionSnapshotMac::~ExceptionSnapshotMac() {
}
bool ExceptionSnapshotMac::Initialize(ProcessReader* process_reader,
exception_behavior_t behavior,
thread_t exception_thread,
exception_type_t exception,
const mach_exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t flavor,
ConstThreadState state,
mach_msg_type_number_t state_count) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
codes_.push_back(exception);
for (mach_msg_type_number_t code_index = 0;
code_index < code_count;
++code_index) {
codes_.push_back(code[code_index]);
}
exception_ = exception;
mach_exception_code_t exception_code_0 = code[0];
if (exception_ == EXC_CRASH) {
exception_ = ExcCrashRecoverOriginalException(
exception_code_0, &exception_code_0, nullptr);
if (exception_ == EXC_CRASH ||
exception_ == EXC_RESOURCE ||
exception_ == EXC_GUARD) {
// EXC_CRASH should never be wrapped in another EXC_CRASH.
//
// EXC_RESOURCE and EXC_GUARD are software exceptions that are never
// wrapped in EXC_CRASH. The only time EXC_CRASH is generated is for
// processes exiting due to an unhandled core-generating signal or being
// killed by SIGKILL for code-signing reasons. Neither of these applies to
// EXC_RESOURCE or EXC_GUARD. See 10.10 xnu-2782.1.97/bsd/kern/kern_exit.c
// proc_prepareexit(). Receiving these exception types wrapped in
// EXC_CRASH would lose information because their code[0] uses all 64 bits
// (see below) and the code[0] recovered from EXC_CRASH only contains 20
// significant bits.
LOG(WARNING) << base::StringPrintf(
"exception %s invalid in EXC_CRASH",
ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)
.c_str());
}
}
// The operations that follow put exception_code_0 (a mach_exception_code_t,
// a typedef for int64_t) into exception_code_0_ (a uint32_t). The range
// checks and bit shifts involved need the same signedness on both sides to
// work properly.
const uint64_t unsigned_exception_code_0 = exception_code_0;
// ExceptionInfo() returns code[0] as a 32-bit value, but exception_code_0 is
// a 64-bit value. The best treatment for this inconsistency depends on the
// exception type.
if (exception_ == EXC_RESOURCE || exception_ == EXC_GUARD) {
// All 64 bits of code[0] are significant for these exceptions. See
// <mach/exc_resource.h> for EXC_RESOURCE and 10.10
// xnu-2782.1.97/bsd/kern/kern_guarded.c fd_guard_ast() for EXC_GUARD.
// code[0] is structured similarly for these two exceptions.
//
// EXC_RESOURCE: see <kern/exc_resource.h>. The resource type and “flavor”
// together define the resource and are in the highest bits. The resource
// limit is in the lowest bits.
//
// EXC_GUARD: see 10.10 xnu-2782.1.97/osfmk/ipc/mach_port.c
// mach_port_guard_exception() and xnu-2782.1.97/bsd/kern/kern_guarded.c
// fd_guard_ast(). The guard type (GUARD_TYPE_MACH_PORT or GUARD_TYPE_FD)
// and “flavor” (from the mach_port_guard_exception_codes or
// guard_exception_codes enums) are in the highest bits. The violating Mach
// port name or file descriptor number is in the lowest bits.
// If MACH_EXCEPTION_CODES is not set in |behavior|, code[0] will only carry
// 32 significant bits, and the interesting high bits will have been
// truncated.
if (!ExceptionBehaviorHasMachExceptionCodes(behavior)) {
LOG(WARNING) << base::StringPrintf(
"behavior %s invalid for exception %s",
ExceptionBehaviorToString(
behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(),
ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)
.c_str());
}
// Include the more-significant information from the high bits of code[0] in
// the value to be returned by ExceptionInfo(). The full value of codes[0]
// including the less-significant lower bits is still available via Codes().
exception_code_0_ = unsigned_exception_code_0 >> 32;
} else {
// For other exceptions, code[0]s values never exceed 32 bits.
if (!base::IsValueInRangeForNumericType<decltype(exception_code_0_)>(
unsigned_exception_code_0)) {
LOG(WARNING) << base::StringPrintf("exception_code_0 0x%llx out of range",
unsigned_exception_code_0);
}
exception_code_0_ = unsigned_exception_code_0;
}
const ProcessReader::Thread* thread = nullptr;
for (const ProcessReader::Thread& loop_thread : process_reader->Threads()) {
if (exception_thread == loop_thread.port) {
thread = &loop_thread;
break;
}
}
if (!thread) {
LOG(ERROR) << "exception_thread not found in task";
return false;
}
thread_id_ = thread->id;
// Normally, the exception address is present in code[1] for EXC_BAD_ACCESS
// exceptions, but not for other types of exceptions.
bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS;
#if defined(ARCH_CPU_X86_FAMILY)
if (process_reader->Is64Bit()) {
context_.architecture = kCPUArchitectureX86_64;
context_.x86_64 = &context_union_.x86_64;
InitializeCPUContextX86_64(context_.x86_64,
flavor,
state,
state_count,
&thread->thread_context.t64,
&thread->float_context.f64,
&thread->debug_context.d64);
} else {
context_.architecture = kCPUArchitectureX86;
context_.x86 = &context_union_.x86;
InitializeCPUContextX86(context_.x86,
flavor,
state,
state_count,
&thread->thread_context.t32,
&thread->float_context.f32,
&thread->debug_context.d32);
}
// For x86 and x86_64 EXC_BAD_ACCESS exceptions, some code[0] values indicate
// that code[1] does not (or may not) carry the exception address:
// EXC_I386_GPFLT (10.9.5 xnu-2422.115.4/osfmk/i386/trap.c user_trap() for
// T_GENERAL_PROTECTION) and the oddball (VM_PROT_READ | VM_PROT_EXECUTE)
// which collides with EXC_I386_BOUNDFLT (10.9.5
// xnu-2422.115.4/osfmk/i386/fpu.c fpextovrflt()). Other EXC_BAD_ACCESS
// exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c
// user_page_fault_continue() and do contain the exception address in code[1].
if (exception_ == EXC_BAD_ACCESS &&
(exception_code_0_ == EXC_I386_GPFLT ||
exception_code_0_ == (VM_PROT_READ | VM_PROT_EXECUTE))) {
code_1_is_exception_address = false;
}
#endif
if (code_1_is_exception_address) {
if (process_reader->Is64Bit() &&
!ExceptionBehaviorHasMachExceptionCodes(behavior)) {
// If code[1] is an address from a 64-bit process, the exception must have
// been received with MACH_EXCEPTION_CODES or the address will have been
// truncated.
LOG(WARNING) << base::StringPrintf(
"behavior %s invalid for exception %s code %d in 64-bit process",
ExceptionBehaviorToString(
behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(),
ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)
.c_str(),
exception_code_0_);
}
exception_address_ = code[1];
} else {
exception_address_ = context_.InstructionPointer();
}
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
const CPUContext* ExceptionSnapshotMac::Context() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return &context_;
}
uint64_t ExceptionSnapshotMac::ThreadID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return thread_id_;
}
uint32_t ExceptionSnapshotMac::Exception() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return exception_;
}
uint32_t ExceptionSnapshotMac::ExceptionInfo() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return exception_code_0_;
}
uint64_t ExceptionSnapshotMac::ExceptionAddress() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return exception_address_;
}
const std::vector<uint64_t>& ExceptionSnapshotMac::Codes() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return codes_;
}
} // namespace internal
} // namespace crashpad