OLD | NEW |
1 // Copyright 2014 The Crashpad Authors. All rights reserved. | 1 // Copyright 2014 The Crashpad Authors. All rights reserved. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
13 // limitations under the License. | 13 // limitations under the License. |
14 | 14 |
15 #include "snapshot/mac/exception_snapshot_mac.h" | 15 #include "snapshot/mac/exception_snapshot_mac.h" |
16 | 16 |
17 #include "base/logging.h" | 17 #include "base/logging.h" |
18 #include "base/strings/stringprintf.h" | 18 #include "base/strings/stringprintf.h" |
19 #include "snapshot/mac/cpu_context_mac.h" | 19 #include "snapshot/mac/cpu_context_mac.h" |
20 #include "snapshot/mac/process_reader.h" | 20 #include "snapshot/mac/process_reader.h" |
21 #include "util/mach/exc_server_variants.h" | 21 #include "util/mach/exc_server_variants.h" |
| 22 #include "util/mach/exception_behaviors.h" |
| 23 #include "util/mach/symbolic_constants_mach.h" |
22 #include "util/numeric/safe_assignment.h" | 24 #include "util/numeric/safe_assignment.h" |
23 | 25 |
24 namespace crashpad { | 26 namespace crashpad { |
25 namespace internal { | 27 namespace internal { |
26 | 28 |
27 ExceptionSnapshotMac::ExceptionSnapshotMac() | 29 ExceptionSnapshotMac::ExceptionSnapshotMac() |
28 : ExceptionSnapshot(), | 30 : ExceptionSnapshot(), |
29 context_union_(), | 31 context_union_(), |
30 context_(), | 32 context_(), |
31 codes_(), | 33 codes_(), |
32 thread_id_(0), | 34 thread_id_(0), |
33 exception_address_(0), | 35 exception_address_(0), |
34 exception_(0), | 36 exception_(0), |
35 exception_code_0_(0), | 37 exception_code_0_(0), |
36 initialized_() { | 38 initialized_() { |
37 } | 39 } |
38 | 40 |
39 ExceptionSnapshotMac::~ExceptionSnapshotMac() { | 41 ExceptionSnapshotMac::~ExceptionSnapshotMac() { |
40 } | 42 } |
41 | 43 |
42 bool ExceptionSnapshotMac::Initialize(ProcessReader* process_reader, | 44 bool ExceptionSnapshotMac::Initialize(ProcessReader* process_reader, |
| 45 exception_behavior_t behavior, |
43 thread_t exception_thread, | 46 thread_t exception_thread, |
44 exception_type_t exception, | 47 exception_type_t exception, |
45 const mach_exception_data_type_t* code, | 48 const mach_exception_data_type_t* code, |
46 mach_msg_type_number_t code_count, | 49 mach_msg_type_number_t code_count, |
47 thread_state_flavor_t flavor, | 50 thread_state_flavor_t flavor, |
48 const natural_t* state, | 51 const natural_t* state, |
49 mach_msg_type_number_t state_count) { | 52 mach_msg_type_number_t state_count) { |
50 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); | 53 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
51 | 54 |
52 codes_.push_back(exception); | 55 codes_.push_back(exception); |
53 for (mach_msg_type_number_t code_index = 0; | 56 for (mach_msg_type_number_t code_index = 0; |
54 code_index < code_count; | 57 code_index < code_count; |
55 ++code_index) { | 58 ++code_index) { |
56 codes_.push_back(code[code_index]); | 59 codes_.push_back(code[code_index]); |
57 } | 60 } |
58 | 61 |
59 exception_ = exception; | 62 exception_ = exception; |
60 mach_exception_code_t exception_code_0 = code[0]; | 63 mach_exception_code_t exception_code_0 = code[0]; |
61 | 64 |
62 if (exception_ == EXC_CRASH) { | 65 if (exception_ == EXC_CRASH) { |
63 exception_ = ExcCrashRecoverOriginalException( | 66 exception_ = ExcCrashRecoverOriginalException( |
64 exception_code_0, &exception_code_0, nullptr); | 67 exception_code_0, &exception_code_0, nullptr); |
| 68 |
| 69 if (exception_ == EXC_RESOURCE || exception_ == EXC_GUARD) { |
| 70 // These are software exceptions that are never mapped to EXC_CRASH. The |
| 71 // only time EXC_CRASH is generated is for processes exiting due to an |
| 72 // unhandled core-generating signal or being killed by SIGKILL for |
| 73 // code-signing reasons. Neither of these applies to EXC_RESOURCE or |
| 74 // EXC_GUARD. See 10.10 xnu-2782.1.97/bsd/kern/kern_exit.c |
| 75 // proc_prepareexit(). |
| 76 // |
| 77 // Receiving these exception types wrapped in EXC_CRASH would lose |
| 78 // information because their code[0] uses all 64 bits (see below) and the |
| 79 // code[0] recovered from EXC_CRASH only contains 20 significant bits. |
| 80 LOG(WARNING) << base::StringPrintf( |
| 81 "exception %s invalid in EXC_CRASH", |
| 82 ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric) |
| 83 .c_str()); |
| 84 } |
65 } | 85 } |
66 | 86 |
67 // ExceptionInfo() returns code[0] in a 32-bit field. This shouldn’t be a | 87 // The operations that follow put exception_code_0 (a mach_exception_code_t, |
68 // problem because code[0]’s values never exceed 32 bits. Only code[1] is ever | 88 // a typedef for int64_t) into exception_code_0_ (a uint32_t). The range |
69 // expected to be that wide. | 89 // checks and bit shifts involved need the same signedness on both sides to |
70 if (!AssignIfInRange(&exception_code_0_, exception_code_0)) { | 90 // work properly. |
71 LOG(WARNING) | 91 const uint64_t unsigned_exception_code_0 = exception_code_0; |
72 << base::StringPrintf("exception_code_0 0x%llx out of range", | 92 |
73 exception_code_0); | 93 // ExceptionInfo() returns code[0] as a 32-bit value, but exception_code_0 is |
74 return false; | 94 // a 64-bit value. The best treatment for this inconsistency depends on the |
| 95 // exception type. |
| 96 if (exception_ == EXC_RESOURCE || exception_ == EXC_GUARD) { |
| 97 // All 64 bits of code[0] are significant for these exceptions. See |
| 98 // <mach/exc_resource.h> for EXC_RESOURCE and 10.10 |
| 99 // xnu-2782.1.97/bsd/kern/kern_guarded.c fd_guard_ast() for EXC_GUARD. |
| 100 // code[0] is structured similarly for these two exceptions. |
| 101 // |
| 102 // EXC_RESOURCE: see <kern/exc_resource.h>. The resource type and “flavor” |
| 103 // together define the resource and are in the highest bits. The resource |
| 104 // limit is in the lowest bits. |
| 105 // |
| 106 // EXC_GUARD: see 10.10 xnu-2782.1.97/osfmk/ipc/mach_port.c |
| 107 // mach_port_guard_exception() and xnu-2782.1.97/bsd/kern/kern_guarded.c |
| 108 // fd_guard_ast(). The guard type (GUARD_TYPE_MACH_PORT or GUARD_TYPE_FD) |
| 109 // and “flavor” (from the mach_port_guard_exception_codes or |
| 110 // guard_exception_codes enums) are in the highest bits. The violating Mach |
| 111 // port name or file descriptor number is in the lowest bits. |
| 112 |
| 113 // If MACH_EXCEPTION_CODES is not set in |behavior|, code[0] will only carry |
| 114 // 32 significant bits, and the interesting high bits will have been |
| 115 // truncated. |
| 116 if (!ExceptionBehaviorHasMachExceptionCodes(behavior)) { |
| 117 LOG(WARNING) << base::StringPrintf( |
| 118 "behavior %s invalid for exception %s", |
| 119 ExceptionBehaviorToString( |
| 120 behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(), |
| 121 ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric) |
| 122 .c_str()); |
| 123 } |
| 124 |
| 125 // Include the more-significant information from the high bits of code[0] in |
| 126 // the value to be returned by ExceptionInfo(). The full value of codes[0] |
| 127 // including the less-significant lower bits is still available via Codes(). |
| 128 exception_code_0_ = unsigned_exception_code_0 >> 32; |
| 129 } else { |
| 130 // For other exceptions, code[0]’s values never exceed 32 bits. |
| 131 if (!base::IsValueInRangeForNumericType<decltype(exception_code_0_)>( |
| 132 unsigned_exception_code_0)) { |
| 133 LOG(WARNING) << base::StringPrintf("exception_code_0 0x%llx out of range", |
| 134 unsigned_exception_code_0); |
| 135 } |
| 136 exception_code_0_ = unsigned_exception_code_0; |
75 } | 137 } |
76 | 138 |
77 const ProcessReader::Thread* thread = nullptr; | 139 const ProcessReader::Thread* thread = nullptr; |
78 for (const ProcessReader::Thread& loop_thread : process_reader->Threads()) { | 140 for (const ProcessReader::Thread& loop_thread : process_reader->Threads()) { |
79 if (exception_thread == loop_thread.port) { | 141 if (exception_thread == loop_thread.port) { |
80 thread = &loop_thread; | 142 thread = &loop_thread; |
81 break; | 143 break; |
82 } | 144 } |
83 } | 145 } |
84 | 146 |
85 if (!thread) { | 147 if (!thread) { |
86 LOG(WARNING) << "exception_thread not found in task"; | 148 LOG(ERROR) << "exception_thread not found in task"; |
87 return false; | 149 return false; |
88 } | 150 } |
89 | 151 |
90 thread_id_ = thread->id; | 152 thread_id_ = thread->id; |
91 | 153 |
92 // Normally, the exception address is present in code[1] for EXC_BAD_ACCESS | 154 // Normally, the exception address is present in code[1] for EXC_BAD_ACCESS |
93 // exceptions, but not for other types of exceptions. | 155 // exceptions, but not for other types of exceptions. |
94 bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS; | 156 bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS; |
95 | 157 |
96 #if defined(ARCH_CPU_X86_FAMILY) | 158 #if defined(ARCH_CPU_X86_FAMILY) |
(...skipping 28 matching lines...) Expand all Loading... |
125 // exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c | 187 // exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c |
126 // user_page_fault_continue() and do contain the exception address in code[1]. | 188 // user_page_fault_continue() and do contain the exception address in code[1]. |
127 if (exception_ == EXC_BAD_ACCESS && | 189 if (exception_ == EXC_BAD_ACCESS && |
128 (exception_code_0_ == EXC_I386_GPFLT || | 190 (exception_code_0_ == EXC_I386_GPFLT || |
129 exception_code_0_ == (VM_PROT_READ | VM_PROT_EXECUTE))) { | 191 exception_code_0_ == (VM_PROT_READ | VM_PROT_EXECUTE))) { |
130 code_1_is_exception_address = false; | 192 code_1_is_exception_address = false; |
131 } | 193 } |
132 #endif | 194 #endif |
133 | 195 |
134 if (code_1_is_exception_address) { | 196 if (code_1_is_exception_address) { |
| 197 if (process_reader->Is64Bit() && |
| 198 !ExceptionBehaviorHasMachExceptionCodes(behavior)) { |
| 199 // If code[1] is an address from a 64-bit process, the exception must have |
| 200 // been received with MACH_EXCEPTION_CODES or the address will have been |
| 201 // truncated. |
| 202 LOG(WARNING) << base::StringPrintf( |
| 203 "behavior %s invalid for exception %s code %d in 64-bit process", |
| 204 ExceptionBehaviorToString( |
| 205 behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(), |
| 206 ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric) |
| 207 .c_str(), |
| 208 exception_code_0_); |
| 209 } |
135 exception_address_ = code[1]; | 210 exception_address_ = code[1]; |
136 } else { | 211 } else { |
137 exception_address_ = context_.InstructionPointer(); | 212 exception_address_ = context_.InstructionPointer(); |
138 } | 213 } |
139 | 214 |
140 INITIALIZATION_STATE_SET_VALID(initialized_); | 215 INITIALIZATION_STATE_SET_VALID(initialized_); |
141 return true; | 216 return true; |
142 } | 217 } |
143 | 218 |
144 const CPUContext* ExceptionSnapshotMac::Context() const { | 219 const CPUContext* ExceptionSnapshotMac::Context() const { |
(...skipping 21 matching lines...) Expand all Loading... |
166 return exception_address_; | 241 return exception_address_; |
167 } | 242 } |
168 | 243 |
169 const std::vector<uint64_t>& ExceptionSnapshotMac::Codes() const { | 244 const std::vector<uint64_t>& ExceptionSnapshotMac::Codes() const { |
170 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 245 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
171 return codes_; | 246 return codes_; |
172 } | 247 } |
173 | 248 |
174 } // namespace internal | 249 } // namespace internal |
175 } // namespace crashpad | 250 } // namespace crashpad |
OLD | NEW |