Index: snapshot/mac/exception_snapshot_mac.cc |
diff --git a/snapshot/mac/exception_snapshot_mac.cc b/snapshot/mac/exception_snapshot_mac.cc |
index 95d8c620c73cde36c21ab923b750d9cf6caa95b4..d89757fccbb9bb4923de09873575d1ccd2abe401 100644 |
--- a/snapshot/mac/exception_snapshot_mac.cc |
+++ b/snapshot/mac/exception_snapshot_mac.cc |
@@ -19,6 +19,8 @@ |
#include "snapshot/mac/cpu_context_mac.h" |
#include "snapshot/mac/process_reader.h" |
#include "util/mach/exc_server_variants.h" |
+#include "util/mach/exception_behaviors.h" |
+#include "util/mach/symbolic_constants_mach.h" |
#include "util/numeric/safe_assignment.h" |
namespace crashpad { |
@@ -40,6 +42,7 @@ 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, |
@@ -62,16 +65,75 @@ bool ExceptionSnapshotMac::Initialize(ProcessReader* process_reader, |
if (exception_ == EXC_CRASH) { |
exception_ = ExcCrashRecoverOriginalException( |
exception_code_0, &exception_code_0, nullptr); |
+ |
+ if (exception_ == EXC_RESOURCE || exception_ == EXC_GUARD) { |
+ // These are software exceptions that are never mapped to 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()); |
+ } |
} |
- // ExceptionInfo() returns code[0] in a 32-bit field. This shouldn’t be a |
- // problem because code[0]’s values never exceed 32 bits. Only code[1] is ever |
- // expected to be that wide. |
- if (!AssignIfInRange(&exception_code_0_, exception_code_0)) { |
- LOG(WARNING) |
- << base::StringPrintf("exception_code_0 0x%llx out of range", |
- exception_code_0); |
- return false; |
+ // 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; |
@@ -83,7 +145,7 @@ bool ExceptionSnapshotMac::Initialize(ProcessReader* process_reader, |
} |
if (!thread) { |
- LOG(WARNING) << "exception_thread not found in task"; |
+ LOG(ERROR) << "exception_thread not found in task"; |
return false; |
} |
@@ -132,6 +194,19 @@ bool ExceptionSnapshotMac::Initialize(ProcessReader* process_reader, |
#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(); |