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 "client/simulate_crash.h"
16
17 #include <mach/mach.h>
18 #include <string.h>
19 #include <sys/types.h>
20
21 #include "base/macros.h"
22 #include "base/stl_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "build/build_config.h"
25 #include "gtest/gtest.h"
26 #include "test/mac/mach_errors.h"
27 #include "test/mac/mach_multiprocess.h"
28 #include "util/mach/exc_server_variants.h"
29 #include "util/mach/exception_behaviors.h"
30 #include "util/mach/exception_ports.h"
31 #include "util/mach/mach_extensions.h"
32 #include "util/mach/mach_message.h"
33 #include "util/mach/mach_message_server.h"
34 #include "util/mach/symbolic_constants_mach.h"
35 #include "util/misc/implicit_cast.h"
36
37 namespace crashpad {
38 namespace test {
39 namespace {
40
41 class TestSimulateCrashMac final : public MachMultiprocess,
42 public UniversalMachExcServer::Interface {
43 public:
44 // Defines which targets the child should set an EXC_CRASH exception handler
45 // for.
46 enum ExceptionPortsTarget {
47 // The child should clear its EXC_CRASH handler for both its task and thread
48 // targets. SimulateCrash() will attempt to deliver the exception to the
49 // host target, which will fail if not running as root. In any case, the
50 // parent should not expect to receive any exception message from the child.
51 kExceptionPortsTargetNone = 0,
52
53 // The child will set an EXC_CRASH handler for its task target, and clear it
54 // for its thread target. The parent runs an exception server to receive
55 // the child’s simulated crash message.
56 kExceptionPortsTargetTask,
57
58 // The child will set an EXC_CRASH handler for its thread target, and clear
59 // it for its task target. The parent runs an exception server to receive
60 // the child’s simulated crash message.
61 kExceptionPortsTargetThread,
62
63 // The child sets an EXC_CRASH handler for both its task and thread targets.
64 // The parent runs an exception server to receive the message expected to be
65 // delivered to the thread target, but returns an error code. The child will
66 // then fall back to trying the server registered for the task target,
67 // sending a second message to the parent. The server in the parent will
68 // handle this one successfully.
69 kExceptionPortsTargetBoth,
70 };
71
TestSimulateCrashMac(ExceptionPortsTarget target,exception_behavior_t behavior,thread_state_flavor_t flavor)72 TestSimulateCrashMac(ExceptionPortsTarget target,
73 exception_behavior_t behavior,
74 thread_state_flavor_t flavor)
75 : MachMultiprocess(),
76 UniversalMachExcServer::Interface(),
77 target_(target),
78 behavior_(behavior),
79 flavor_(flavor),
80 succeed_(true) {
81 }
82
~TestSimulateCrashMac()83 ~TestSimulateCrashMac() {}
84
85 // UniversalMachExcServer::Interface:
CatchMachException(exception_behavior_t behavior,exception_handler_t exception_port,thread_t thread,task_t task,exception_type_t exception,const mach_exception_data_type_t * code,mach_msg_type_number_t code_count,thread_state_flavor_t * flavor,ConstThreadState old_state,mach_msg_type_number_t old_state_count,thread_state_t new_state,mach_msg_type_number_t * new_state_count,const mach_msg_trailer_t * trailer,bool * destroy_complex_request)86 kern_return_t CatchMachException(exception_behavior_t behavior,
87 exception_handler_t exception_port,
88 thread_t thread,
89 task_t task,
90 exception_type_t exception,
91 const mach_exception_data_type_t* code,
92 mach_msg_type_number_t code_count,
93 thread_state_flavor_t* flavor,
94 ConstThreadState old_state,
95 mach_msg_type_number_t old_state_count,
96 thread_state_t new_state,
97 mach_msg_type_number_t* new_state_count,
98 const mach_msg_trailer_t* trailer,
99 bool* destroy_complex_request) override {
100 *destroy_complex_request = true;
101
102 // Check the entire exception message, because most or all of it was
103 // generated by SimulateCrash() instead of the kernel.
104
105 EXPECT_EQ(behavior, behavior_);
106 EXPECT_EQ(exception_port, LocalPort());
107 if (ExceptionBehaviorHasIdentity(behavior)) {
108 EXPECT_NE(thread, THREAD_NULL);
109 EXPECT_EQ(task, ChildTask());
110 } else {
111 EXPECT_EQ(thread, THREAD_NULL);
112 EXPECT_EQ(task, TASK_NULL);
113 }
114 EXPECT_EQ(exception, kMachExceptionSimulated);
115 EXPECT_EQ(code_count, 2u);
116 if (code_count >= 1) {
117 EXPECT_EQ(code[0], 0);
118 }
119 if (code_count >= 2) {
120 EXPECT_EQ(code[1], 0);
121 }
122 if (!ExceptionBehaviorHasState(behavior)) {
123 EXPECT_EQ(*flavor, THREAD_STATE_NONE);
124 } else {
125 EXPECT_EQ(*flavor, flavor_);
126 switch (*flavor) {
127 #if defined(ARCH_CPU_X86_FAMILY)
128 case x86_THREAD_STATE: {
129 EXPECT_EQ(old_state_count, x86_THREAD_STATE_COUNT);
130 const x86_thread_state* state =
131 reinterpret_cast<const x86_thread_state*>(old_state);
132 switch (state->tsh.flavor) {
133 case x86_THREAD_STATE32:
134 EXPECT_EQ(implicit_cast<uint32_t>(state->tsh.count),
135 implicit_cast<uint32_t>(x86_THREAD_STATE32_COUNT));
136 break;
137 case x86_THREAD_STATE64:
138 EXPECT_EQ(implicit_cast<uint32_t>(state->tsh.count),
139 implicit_cast<uint32_t>(x86_THREAD_STATE64_COUNT));
140 break;
141 default:
142 ADD_FAILURE() << "unexpected tsh.flavor " << state->tsh.flavor;
143 break;
144 }
145 break;
146 }
147 case x86_FLOAT_STATE: {
148 EXPECT_EQ(old_state_count, x86_FLOAT_STATE_COUNT);
149 const x86_float_state* state =
150 reinterpret_cast<const x86_float_state*>(old_state);
151 switch (state->fsh.flavor) {
152 case x86_FLOAT_STATE32:
153 EXPECT_EQ(implicit_cast<uint32_t>(state->fsh.count),
154 implicit_cast<uint32_t>(x86_FLOAT_STATE32_COUNT));
155 break;
156 case x86_FLOAT_STATE64:
157 EXPECT_EQ(implicit_cast<uint32_t>(state->fsh.count),
158 implicit_cast<uint32_t>(x86_FLOAT_STATE64_COUNT));
159 break;
160 default:
161 ADD_FAILURE() << "unexpected fsh.flavor " << state->fsh.flavor;
162 break;
163 }
164 break;
165 }
166 case x86_DEBUG_STATE: {
167 EXPECT_EQ(old_state_count, x86_DEBUG_STATE_COUNT);
168 const x86_debug_state* state =
169 reinterpret_cast<const x86_debug_state*>(old_state);
170 switch (state->dsh.flavor) {
171 case x86_DEBUG_STATE32:
172 EXPECT_EQ(implicit_cast<uint32_t>(state->dsh.count),
173 implicit_cast<uint32_t>(x86_DEBUG_STATE32_COUNT));
174 break;
175 case x86_DEBUG_STATE64:
176 EXPECT_EQ(implicit_cast<uint32_t>(state->dsh.count),
177 implicit_cast<uint32_t>(x86_DEBUG_STATE64_COUNT));
178 break;
179 default:
180 ADD_FAILURE() << "unexpected dsh.flavor " << state->dsh.flavor;
181 break;
182 }
183 break;
184 }
185 case x86_THREAD_STATE32:
186 EXPECT_EQ(old_state_count, x86_THREAD_STATE32_COUNT);
187 break;
188 case x86_FLOAT_STATE32:
189 EXPECT_EQ(old_state_count, x86_FLOAT_STATE32_COUNT);
190 break;
191 case x86_DEBUG_STATE32:
192 EXPECT_EQ(old_state_count, x86_DEBUG_STATE32_COUNT);
193 break;
194 case x86_THREAD_STATE64:
195 EXPECT_EQ(old_state_count, x86_THREAD_STATE64_COUNT);
196 break;
197 case x86_FLOAT_STATE64:
198 EXPECT_EQ(old_state_count, x86_FLOAT_STATE64_COUNT);
199 break;
200 case x86_DEBUG_STATE64:
201 EXPECT_EQ(old_state_count, x86_DEBUG_STATE64_COUNT);
202 break;
203 #elif defined(ARCH_CPU_ARM64)
204 case ARM_UNIFIED_THREAD_STATE: {
205 EXPECT_EQ(old_state_count, ARM_UNIFIED_THREAD_STATE_COUNT);
206 const arm_unified_thread_state* state =
207 reinterpret_cast<const arm_unified_thread_state*>(old_state);
208 EXPECT_EQ(state->ash.flavor,
209 implicit_cast<uint32_t>(ARM_THREAD_STATE64));
210 if (state->ash.flavor == ARM_THREAD_STATE64) {
211 EXPECT_EQ(state->ash.count,
212 implicit_cast<uint32_t>(ARM_THREAD_STATE64_COUNT));
213 }
214 break;
215 }
216 case ARM_THREAD_STATE64:
217 EXPECT_EQ(old_state_count, ARM_THREAD_STATE64_COUNT);
218 break;
219 case ARM_NEON_STATE64:
220 EXPECT_EQ(old_state_count, ARM_NEON_STATE64_COUNT);
221 break;
222 case ARM_DEBUG_STATE64:
223 EXPECT_EQ(old_state_count, ARM_DEBUG_STATE64_COUNT);
224 break;
225 #else
226 #error Port to your CPU architecture
227 #endif
228 default:
229 ADD_FAILURE() << "unexpected flavor " << *flavor;
230 break;
231 }
232
233 // Attempt to set a garbage thread state, which would cause the child to
234 // crash inside SimulateCrash() if it actually succeeded. This tests that
235 // SimulateCrash() ignores new_state instead of attempting to set the
236 // state as the kernel would do. This operates in conjunction with the
237 // |true| argument to ExcServerSuccessfulReturnValue() below.
238 *new_state_count = old_state_count;
239 size_t new_state_size = sizeof(natural_t) * old_state_count;
240 memset(new_state, 0xa5, new_state_size);
241 }
242
243 if (!succeed_) {
244 // The client has registered EXC_CRASH handlers for both its thread and
245 // task targets, and sent a simulated exception message to its
246 // thread-level EXC_CRASH handler. To test that it will fall back to
247 // trying the task-level EXC_CRASH handler, return a failure code, which
248 // should cause SimulateCrash() to try the next target.
249 EXPECT_EQ(target_, kExceptionPortsTargetBoth);
250 return KERN_ABORTED;
251 }
252
253 ExcServerCopyState(
254 behavior, old_state, old_state_count, new_state, new_state_count);
255
256 return ExcServerSuccessfulReturnValue(exception, behavior, true);
257 }
258
259 private:
260 // MachMultiprocess:
261
MachMultiprocessParent()262 void MachMultiprocessParent() override {
263 if (target_ == kExceptionPortsTargetNone) {
264 // The child does not have any EXC_CRASH handlers registered for its
265 // thread or task targets, so no exception message is expected to be
266 // generated. Don’t run the server at all.
267 return;
268 }
269
270 UniversalMachExcServer universal_mach_exc_server(this);
271
272 mach_msg_return_t mr;
273 if (target_ == kExceptionPortsTargetBoth) {
274 // The client has registered EXC_CRASH handlers for both its thread and
275 // task targets. Run a server that will return a failure code when the
276 // exception message is sent to the thread target, which will cause the
277 // client to fall back to the task target and send another message.
278 succeed_ = false;
279 mr = MachMessageServer::Run(&universal_mach_exc_server,
280 LocalPort(),
281 MACH_MSG_OPTION_NONE,
282 MachMessageServer::kOneShot,
283 MachMessageServer::kReceiveLargeError,
284 kMachMessageTimeoutWaitIndefinitely);
285 EXPECT_EQ(mr, MACH_MSG_SUCCESS)
286 << MachErrorMessage(mr, "MachMessageServer::Run");
287 }
288
289 succeed_ = true;
290 mr = MachMessageServer::Run(&universal_mach_exc_server,
291 LocalPort(),
292 MACH_MSG_OPTION_NONE,
293 MachMessageServer::kOneShot,
294 MachMessageServer::kReceiveLargeError,
295 kMachMessageTimeoutWaitIndefinitely);
296 EXPECT_EQ(mr, MACH_MSG_SUCCESS)
297 << MachErrorMessage(mr, "MachMessageServer::Run");
298 }
299
MachMultiprocessChild()300 void MachMultiprocessChild() override {
301 bool task_valid = target_ == kExceptionPortsTargetTask ||
302 target_ == kExceptionPortsTargetBoth;
303 ExceptionPorts task_exception_ports(ExceptionPorts::kTargetTypeTask,
304 TASK_NULL);
305 ASSERT_TRUE(task_exception_ports.SetExceptionPort(
306 EXC_MASK_CRASH,
307 task_valid ? RemotePort() : MACH_PORT_NULL,
308 behavior_,
309 flavor_));
310
311 bool thread_valid = target_ == kExceptionPortsTargetThread ||
312 target_ == kExceptionPortsTargetBoth;
313 ExceptionPorts thread_exception_ports(ExceptionPorts::kTargetTypeThread,
314 THREAD_NULL);
315 ASSERT_TRUE(thread_exception_ports.SetExceptionPort(
316 EXC_MASK_CRASH,
317 thread_valid ? RemotePort() : MACH_PORT_NULL,
318 behavior_,
319 flavor_));
320
321 CRASHPAD_SIMULATE_CRASH();
322 }
323
324 ExceptionPortsTarget target_;
325 exception_behavior_t behavior_;
326 thread_state_flavor_t flavor_;
327 bool succeed_;
328
329 DISALLOW_COPY_AND_ASSIGN(TestSimulateCrashMac);
330 };
331
TEST(SimulateCrash,SimulateCrash)332 TEST(SimulateCrash, SimulateCrash) {
333 static constexpr TestSimulateCrashMac::ExceptionPortsTarget kTargets[] = {
334 TestSimulateCrashMac::kExceptionPortsTargetNone,
335 TestSimulateCrashMac::kExceptionPortsTargetTask,
336 TestSimulateCrashMac::kExceptionPortsTargetThread,
337 TestSimulateCrashMac::kExceptionPortsTargetBoth,
338 };
339
340 static constexpr exception_behavior_t kBehaviors[] = {
341 EXCEPTION_DEFAULT,
342 EXCEPTION_STATE,
343 EXCEPTION_STATE_IDENTITY,
344 EXCEPTION_DEFAULT | kMachExceptionCodes,
345 EXCEPTION_STATE | kMachExceptionCodes,
346 EXCEPTION_STATE_IDENTITY | kMachExceptionCodes,
347 };
348
349 static constexpr thread_state_flavor_t kFlavors[] = {
350 #if defined(ARCH_CPU_X86_FAMILY)
351 x86_THREAD_STATE,
352 x86_FLOAT_STATE,
353 x86_DEBUG_STATE,
354 #if defined(ARCH_CPU_X86)
355 x86_THREAD_STATE32,
356 x86_FLOAT_STATE32,
357 x86_DEBUG_STATE32,
358 #elif defined(ARCH_CPU_X86_64)
359 x86_THREAD_STATE64,
360 x86_FLOAT_STATE64,
361 x86_DEBUG_STATE64,
362 #endif
363 #elif defined(ARCH_CPU_ARM64)
364 ARM_UNIFIED_THREAD_STATE,
365 ARM_THREAD_STATE64,
366 ARM_NEON_STATE64,
367 ARM_DEBUG_STATE64,
368 #else
369 #error Port to your CPU architecture
370 #endif
371 };
372
373 for (size_t target_index = 0; target_index < base::size(kTargets);
374 ++target_index) {
375 TestSimulateCrashMac::ExceptionPortsTarget target = kTargets[target_index];
376 SCOPED_TRACE(base::StringPrintf(
377 "target_index %zu, target %d", target_index, target));
378
379 for (size_t behavior_index = 0; behavior_index < base::size(kBehaviors);
380 ++behavior_index) {
381 exception_behavior_t behavior = kBehaviors[behavior_index];
382 SCOPED_TRACE(base::StringPrintf(
383 "behavior_index %zu, behavior %s",
384 behavior_index,
385 ExceptionBehaviorToString(behavior, kUseFullName | kUnknownIsNumeric)
386 .c_str()));
387
388 if (!ExceptionBehaviorHasState(behavior)) {
389 TestSimulateCrashMac test_simulate_crash_mac(
390 target, behavior, THREAD_STATE_NONE);
391 test_simulate_crash_mac.Run();
392 } else {
393 for (size_t flavor_index = 0; flavor_index < base::size(kFlavors);
394 ++flavor_index) {
395 thread_state_flavor_t flavor = kFlavors[flavor_index];
396 SCOPED_TRACE(base::StringPrintf(
397 "flavor_index %zu, flavor %s",
398 flavor_index,
399 ThreadStateFlavorToString(
400 flavor, kUseFullName | kUnknownIsNumeric).c_str()));
401
402 TestSimulateCrashMac test_simulate_crash_mac(
403 target, behavior, flavor);
404 test_simulate_crash_mac.Run();
405 }
406 }
407 }
408 }
409 }
410
411 } // namespace
412 } // namespace test
413 } // namespace crashpad
414