1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (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
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "snapshot/mac/exception_snapshot_mac.h"
16 
17 #include "base/logging.h"
18 #include "base/strings/stringprintf.h"
19 #include "snapshot/mac/cpu_context_mac.h"
20 #include "snapshot/mac/process_reader_mac.h"
21 #include "util/mach/exception_behaviors.h"
22 #include "util/mach/exception_types.h"
23 #include "util/mach/symbolic_constants_mach.h"
24 #include "util/numeric/safe_assignment.h"
25 
26 namespace crashpad {
27 namespace internal {
28 
ExceptionSnapshotMac()29 ExceptionSnapshotMac::ExceptionSnapshotMac()
30     : ExceptionSnapshot(),
31       context_union_(),
32       context_(),
33       codes_(),
34       thread_id_(0),
35       exception_address_(0),
36       exception_(0),
37       exception_code_0_(0),
38       initialized_() {
39 }
40 
~ExceptionSnapshotMac()41 ExceptionSnapshotMac::~ExceptionSnapshotMac() {
42 }
43 
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)44 bool ExceptionSnapshotMac::Initialize(ProcessReaderMac* process_reader,
45                                       exception_behavior_t behavior,
46                                       thread_t exception_thread,
47                                       exception_type_t exception,
48                                       const mach_exception_data_type_t* code,
49                                       mach_msg_type_number_t code_count,
50                                       thread_state_flavor_t flavor,
51                                       ConstThreadState state,
52                                       mach_msg_type_number_t state_count) {
53   INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
54 
55   codes_.push_back(exception);
56   for (mach_msg_type_number_t code_index = 0;
57        code_index < code_count;
58        ++code_index) {
59     codes_.push_back(code[code_index]);
60   }
61 
62   exception_ = exception;
63   mach_exception_code_t exception_code_0 = code[0];
64 
65   if (exception_ == EXC_CRASH) {
66     exception_ = ExcCrashRecoverOriginalException(
67         exception_code_0, &exception_code_0, nullptr);
68 
69     if (!ExcCrashCouldContainException(exception_)) {
70       LOG(WARNING) << base::StringPrintf(
71           "exception %s invalid in EXC_CRASH",
72           ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)
73               .c_str());
74     }
75   }
76 
77   // The operations that follow put exception_code_0 (a mach_exception_code_t,
78   // a typedef for int64_t) into exception_code_0_ (a uint32_t). The range
79   // checks and bit shifts involved need the same signedness on both sides to
80   // work properly.
81   const uint64_t unsigned_exception_code_0 = exception_code_0;
82 
83   // ExceptionInfo() returns code[0] as a 32-bit value, but exception_code_0 is
84   // a 64-bit value. The best treatment for this inconsistency depends on the
85   // exception type.
86   if (exception_ == EXC_RESOURCE || exception_ == EXC_GUARD) {
87     // All 64 bits of code[0] are significant for these exceptions. See
88     // <mach/exc_resource.h> for EXC_RESOURCE and 10.10
89     // xnu-2782.1.97/bsd/kern/kern_guarded.c fd_guard_ast() for EXC_GUARD.
90     // code[0] is structured similarly for these two exceptions.
91     //
92     // EXC_RESOURCE: see <kern/exc_resource.h>. The resource type and “flavor”
93     // together define the resource and are in the highest bits. The resource
94     // limit is in the lowest bits.
95     //
96     // EXC_GUARD: see 10.10 xnu-2782.1.97/osfmk/ipc/mach_port.c
97     // mach_port_guard_exception() and xnu-2782.1.97/bsd/kern/kern_guarded.c
98     // fd_guard_ast(). The guard type (GUARD_TYPE_MACH_PORT or GUARD_TYPE_FD)
99     // and “flavor” (from the mach_port_guard_exception_codes or
100     // guard_exception_codes enums) are in the highest bits. The violating Mach
101     // port name or file descriptor number is in the lowest bits.
102 
103     // If MACH_EXCEPTION_CODES is not set in |behavior|, code[0] will only carry
104     // 32 significant bits, and the interesting high bits will have been
105     // truncated.
106     if (!ExceptionBehaviorHasMachExceptionCodes(behavior)) {
107       LOG(WARNING) << base::StringPrintf(
108           "behavior %s invalid for exception %s",
109           ExceptionBehaviorToString(
110               behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(),
111           ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)
112               .c_str());
113     }
114 
115     // Include the more-significant information from the high bits of code[0] in
116     // the value to be returned by ExceptionInfo(). The full value of codes[0]
117     // including the less-significant lower bits is still available via Codes().
118     exception_code_0_ = unsigned_exception_code_0 >> 32;
119   } else {
120     // For other exceptions, code[0]’s values never exceed 32 bits.
121     if (!base::IsValueInRangeForNumericType<decltype(exception_code_0_)>(
122             unsigned_exception_code_0)) {
123       LOG(WARNING) << base::StringPrintf("exception_code_0 0x%llx out of range",
124                                          unsigned_exception_code_0);
125     }
126     exception_code_0_ = unsigned_exception_code_0;
127   }
128 
129   const ProcessReaderMac::Thread* thread = nullptr;
130   for (const ProcessReaderMac::Thread& loop_thread :
131        process_reader->Threads()) {
132     if (exception_thread == loop_thread.port) {
133       thread = &loop_thread;
134       break;
135     }
136   }
137 
138   if (!thread) {
139     LOG(ERROR) << "exception_thread not found in task";
140     return false;
141   }
142 
143   thread_id_ = thread->id;
144 
145   // Normally, for EXC_BAD_ACCESS exceptions, the exception address is present
146   // in code[1]. It may or may not be the instruction pointer address (usually
147   // it’s not). code[1] may carry the exception address for other exception
148   // types too, but it’s not guaranteed. But for all other exception types, the
149   // instruction pointer will be the exception address, and in fact will be
150   // equal to codes[1] when it’s carrying the exception address. In those cases,
151   // just use the instruction pointer directly.
152   bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS;
153 
154 #if defined(ARCH_CPU_X86_FAMILY)
155   if (process_reader->Is64Bit()) {
156     context_.architecture = kCPUArchitectureX86_64;
157     context_.x86_64 = &context_union_.x86_64;
158     InitializeCPUContextX86_64(context_.x86_64,
159                                flavor,
160                                state,
161                                state_count,
162                                &thread->thread_context.t64,
163                                &thread->float_context.f64,
164                                &thread->debug_context.d64);
165   } else {
166     context_.architecture = kCPUArchitectureX86;
167     context_.x86 = &context_union_.x86;
168     InitializeCPUContextX86(context_.x86,
169                             flavor,
170                             state,
171                             state_count,
172                             &thread->thread_context.t32,
173                             &thread->float_context.f32,
174                             &thread->debug_context.d32);
175   }
176 
177   // For x86 and x86_64 EXC_BAD_ACCESS exceptions, some code[0] values indicate
178   // that code[1] does not (or may not) carry the exception address:
179   // EXC_I386_GPFLT (10.9.5 xnu-2422.115.4/osfmk/i386/trap.c user_trap() for
180   // T_GENERAL_PROTECTION) and the oddball (VM_PROT_READ | VM_PROT_EXECUTE)
181   // which collides with EXC_I386_BOUNDFLT (10.9.5
182   // xnu-2422.115.4/osfmk/i386/fpu.c fpextovrflt()). Other EXC_BAD_ACCESS
183   // exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c
184   // user_page_fault_continue() and do contain the exception address in code[1].
185   if (exception_ == EXC_BAD_ACCESS &&
186       (exception_code_0_ == EXC_I386_GPFLT ||
187        exception_code_0_ == (VM_PROT_READ | VM_PROT_EXECUTE))) {
188     code_1_is_exception_address = false;
189   }
190 #elif defined(ARCH_CPU_ARM64)
191   context_.architecture = kCPUArchitectureARM64;
192   context_.arm64 = &context_union_.arm64;
193   InitializeCPUContextARM64(context_.arm64,
194                             flavor,
195                             state,
196                             state_count,
197                             &thread->thread_context,
198                             &thread->float_context,
199                             &thread->debug_context);
200 #else
201 #error Port to your architecture
202 #endif
203 
204   if (code_1_is_exception_address) {
205     if (process_reader->Is64Bit() &&
206         !ExceptionBehaviorHasMachExceptionCodes(behavior)) {
207       // If code[1] is an address from a 64-bit process, the exception must have
208       // been received with MACH_EXCEPTION_CODES or the address will have been
209       // truncated.
210       LOG(WARNING) << base::StringPrintf(
211           "behavior %s invalid for exception %s code %d in 64-bit process",
212           ExceptionBehaviorToString(
213               behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(),
214           ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)
215               .c_str(),
216           exception_code_0_);
217     }
218     exception_address_ = code[1];
219   } else {
220     exception_address_ = context_.InstructionPointer();
221   }
222 
223   INITIALIZATION_STATE_SET_VALID(initialized_);
224   return true;
225 }
226 
Context() const227 const CPUContext* ExceptionSnapshotMac::Context() const {
228   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
229   return &context_;
230 }
231 
ThreadID() const232 uint64_t ExceptionSnapshotMac::ThreadID() const {
233   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
234   return thread_id_;
235 }
236 
Exception() const237 uint32_t ExceptionSnapshotMac::Exception() const {
238   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
239   return exception_;
240 }
241 
ExceptionInfo() const242 uint32_t ExceptionSnapshotMac::ExceptionInfo() const {
243   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
244   return exception_code_0_;
245 }
246 
ExceptionAddress() const247 uint64_t ExceptionSnapshotMac::ExceptionAddress() const {
248   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
249   return exception_address_;
250 }
251 
Codes() const252 const std::vector<uint64_t>& ExceptionSnapshotMac::Codes() const {
253   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
254   return codes_;
255 }
256 
ExtraMemory() const257 std::vector<const MemorySnapshot*> ExceptionSnapshotMac::ExtraMemory() const {
258   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
259   return std::vector<const MemorySnapshot*>();
260 }
261 
262 }  // namespace internal
263 }  // namespace crashpad
264