2023-01-28 04:54:20 +00:00
|
|
|
|
// Copyright 2014 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 "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_mac.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(ProcessReaderMac* 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 (!ExcCrashCouldContainException(exception_)) {
|
|
|
|
|
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 ProcessReaderMac::Thread* thread = nullptr;
|
|
|
|
|
for (const ProcessReaderMac::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, for EXC_BAD_ACCESS exceptions, the exception address is present
|
|
|
|
|
// in code[1]. It may or may not be the instruction pointer address (usually
|
|
|
|
|
// it’s not). code[1] may carry the exception address for other exception
|
|
|
|
|
// types too, but it’s not guaranteed. But for all other exception types, the
|
|
|
|
|
// instruction pointer will be the exception address, and in fact will be
|
|
|
|
|
// equal to codes[1] when it’s carrying the exception address. In those cases,
|
|
|
|
|
// just use the instruction pointer directly.
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
#elif defined(ARCH_CPU_ARM64)
|
|
|
|
|
context_.architecture = kCPUArchitectureARM64;
|
|
|
|
|
context_.arm64 = &context_union_.arm64;
|
|
|
|
|
InitializeCPUContextARM64(context_.arm64,
|
|
|
|
|
flavor,
|
|
|
|
|
state,
|
|
|
|
|
state_count,
|
|
|
|
|
&thread->thread_context,
|
|
|
|
|
&thread->float_context,
|
|
|
|
|
&thread->debug_context);
|
|
|
|
|
#else
|
|
|
|
|
#error Port to your architecture
|
|
|
|
|
#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_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<const MemorySnapshot*> ExceptionSnapshotMac::ExtraMemory() const {
|
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
return std::vector<const MemorySnapshot*>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace internal
|
|
|
|
|
} // namespace crashpad
|