OLD | NEW |
---|---|
(Empty) | |
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 "util/mach/exception_ports.h" | |
16 | |
17 #include <dispatch/dispatch.h> | |
18 #include <mach/mach.h> | |
19 #include <pthread.h> | |
20 #include <signal.h> | |
21 #include <unistd.h> | |
22 | |
23 #include "base/basictypes.h" | |
24 #include "base/logging.h" | |
25 #include "base/mac/mach_logging.h" | |
26 #include "base/mac/scoped_mach_port.h" | |
27 #include "base/strings/stringprintf.h" | |
28 #include "gtest/gtest.h" | |
29 #include "util/file/fd_io.h" | |
30 #include "util/mach/exc_server_variants.h" | |
31 #include "util/mach/mach_extensions.h" | |
32 #include "util/misc/scoped_forbid_return.h" | |
33 #include "util/test/errors.h" | |
34 #include "util/test/mac/mach_errors.h" | |
35 #include "util/test/mac/mach_multiprocess.h" | |
36 | |
37 namespace { | |
38 | |
39 using namespace crashpad; | |
40 using namespace crashpad::test; | |
41 | |
42 // Calls GetExceptionPorts() on its |exception_ports| argument to look up the | |
43 // EXC_MASK_CRASH handler. If |expect_port| is not MACH_PORT_NULL, it expects to | |
44 // find a handler for this mask whose port matches |expect_port| and whose | |
45 // behavior matches |expect_behavior| exactly. In this case, if | |
46 // |expect_behavior| is a state-carrying behavior, the looked-up thread state | |
47 // flavor is expected to be MACHINE_THREAD_STATE, otherwise, it is expected to | |
48 // be THREAD_STATE_NONE. If |expect_port| is MACH_PORT_NULL, no handler for | |
49 // EXC_MASK_CRASH is expected to be found. | |
50 // | |
51 // A second GetExceptionPorts() lookup is also performed on a wider exception | |
52 // mask, EXC_MASK_ALL | EXC_MASK_CRASH. The EXC_MASK_CRASH handler’s existence | |
53 // and properties from this second lookup are validated in the same way. | |
54 // | |
55 // This function uses gtest EXPECT_* and ASSERT_* macros to perform its | |
56 // validation. | |
57 void TestGetExceptionPorts(const ExceptionPorts& exception_ports, | |
58 mach_port_t expect_port, | |
59 exception_behavior_t expect_behavior) { | |
60 const exception_mask_t kExceptionMask = EXC_MASK_CRASH; | |
61 | |
62 thread_state_flavor_t expect_flavor = (expect_behavior == EXCEPTION_DEFAULT) | |
63 ? THREAD_STATE_NONE | |
64 : MACHINE_THREAD_STATE; | |
65 | |
66 std::vector<ExceptionPorts::ExceptionHandler> crash_handler; | |
67 ASSERT_TRUE( | |
68 exception_ports.GetExceptionPorts(kExceptionMask, &crash_handler)); | |
69 | |
70 if (expect_port != MACH_PORT_NULL) { | |
71 ASSERT_EQ(1u, crash_handler.size()); | |
72 base::mac::ScopedMachSendRight port_owner(crash_handler[0].port); | |
73 | |
74 EXPECT_EQ(kExceptionMask, crash_handler[0].mask); | |
75 EXPECT_EQ(expect_port, crash_handler[0].port); | |
76 EXPECT_EQ(expect_behavior, crash_handler[0].behavior); | |
77 EXPECT_EQ(expect_flavor, crash_handler[0].flavor); | |
78 } else { | |
79 EXPECT_TRUE(crash_handler.empty()); | |
80 } | |
81 | |
82 std::vector<ExceptionPorts::ExceptionHandler> handlers; | |
83 ASSERT_TRUE(exception_ports.GetExceptionPorts( | |
84 ExcMaskAll() | EXC_MASK_CRASH, &handlers)); | |
85 | |
86 EXPECT_GE(handlers.size(), crash_handler.size()); | |
87 bool found = false; | |
88 for (const ExceptionPorts::ExceptionHandler& handler : handlers) { | |
89 if ((handler.mask & kExceptionMask) != 0) { | |
90 base::mac::ScopedMachSendRight port_owner(handler.port); | |
91 | |
92 EXPECT_FALSE(found); | |
93 found = true; | |
94 EXPECT_EQ(expect_port, handler.port); | |
95 EXPECT_EQ(expect_behavior, handler.behavior); | |
96 EXPECT_EQ(expect_flavor, handler.flavor); | |
97 } | |
98 } | |
99 | |
100 if (expect_port != MACH_PORT_NULL) { | |
101 EXPECT_TRUE(found); | |
102 } else { | |
103 EXPECT_FALSE(found); | |
104 } | |
105 } | |
106 | |
107 class TestExceptionPorts : public UniversalMachExcServer, | |
108 public MachMultiprocess { | |
109 public: | |
110 // Where to call ExceptionPorts::SetExceptionPort() from. | |
111 enum SetType { | |
112 // Call it from the child process on itself. | |
113 kSetInProcess = 0, | |
114 | |
115 // Call it from the parent process on the child. | |
116 kSetOutOfProcess, | |
117 }; | |
118 | |
119 // Which entities to set exception ports for. | |
120 enum SetOn { | |
121 kSetOnTaskOnly = 0, | |
122 kSetOnTaskAndThreads, | |
123 }; | |
124 | |
125 // Which thread in the child process is expected to crash. | |
126 enum WhoCrashes { | |
127 kNobodyCrashes = 0, | |
128 kMainThreadCrashes, | |
129 kOtherThreadCrashes, | |
130 }; | |
131 | |
132 TestExceptionPorts(SetType set_type, SetOn set_on, WhoCrashes who_crashes) | |
133 : UniversalMachExcServer(), | |
134 MachMultiprocess(), | |
135 set_type_(set_type), | |
136 set_on_(set_on), | |
137 who_crashes_(who_crashes), | |
138 handled_(false) {} | |
139 | |
140 SetType set_type() const { return set_type_; } | |
141 SetOn set_on() const { return set_on_; } | |
142 WhoCrashes who_crashes() const { return who_crashes_; } | |
143 | |
144 // UniversalMachExcServer: | |
145 | |
146 virtual kern_return_t CatchMachException( | |
147 exception_behavior_t behavior, | |
148 exception_handler_t exception_port, | |
149 thread_t thread, | |
150 task_t task, | |
151 exception_type_t exception, | |
152 const mach_exception_data_type_t* code, | |
153 mach_msg_type_number_t code_count, | |
154 thread_state_flavor_t* flavor, | |
155 const natural_t* old_state, | |
156 mach_msg_type_number_t old_state_count, | |
157 thread_state_t new_state, | |
158 mach_msg_type_number_t* new_state_count, | |
159 bool* destroy_complex_request) override { | |
160 *destroy_complex_request = true; | |
161 | |
162 EXPECT_FALSE(handled_); | |
163 handled_ = true; | |
164 | |
165 // To be able to distinguish between which handler was actually triggered, | |
166 // the different handlers are registered with different behavior values. | |
167 exception_behavior_t expect_behavior; | |
168 if (set_on_ == kSetOnTaskOnly) { | |
169 expect_behavior = EXCEPTION_DEFAULT; | |
170 } else if (who_crashes_ == kMainThreadCrashes) { | |
171 expect_behavior = EXCEPTION_STATE; | |
172 } else if (who_crashes_ == kOtherThreadCrashes) { | |
173 expect_behavior = EXCEPTION_STATE_IDENTITY; | |
174 } else { | |
175 NOTREACHED(); | |
176 expect_behavior = 0; | |
177 } | |
178 | |
179 EXPECT_EQ(expect_behavior, behavior); | |
180 | |
181 EXPECT_EQ(LocalPort(), exception_port); | |
182 | |
183 EXPECT_EQ(EXC_CRASH, exception); | |
184 EXPECT_EQ(2u, code_count); | |
185 | |
186 if (code_count > 1) { | |
Robert Sesek
2014/09/16 21:16:42
code_count has to be 2, or the test will fail. Not
Mark Mentovai
2014/09/16 21:29:11
rsesek wrote:
| |
187 // The signal that terminated the process is stored in code[0] along with | |
188 // some other data. See 10.9.4 xnu-2422.110.17/bsd/kern/kern_exit.c | |
189 // proc_prepareexit(). | |
190 int sig = (code[0] >> 24) & 0xff; | |
191 | |
192 // The child crashed with a division by zero, which shows up as SIGFPE. | |
193 // This was chosen because it’s unlikely to be generated by testing or | |
194 // assertion failures. | |
195 EXPECT_EQ(SIGFPE, sig); | |
196 | |
197 SetExpectedChildTermination(kTerminationSignal, sig); | |
198 } | |
199 | |
200 // Even for an EXC_CRASH handler, returning KERN_SUCCESS with a | |
201 // state-carrying reply will cause the kernel to try to set a new thread | |
202 // state, leading to a perceptible waste of time. Returning | |
203 // MACH_RCV_PORT_DIED is the only way to suppress this behavior while also | |
204 // preventing the kernel from looking for another (host-level) EXC_CRASH | |
205 // handler. See 10.9.4 xnu-2422.110.17/osfmk/kern/exception.c | |
206 // exception_triage(). | |
207 exception_behavior_t basic_behavior = behavior & ~MACH_EXCEPTION_CODES; | |
208 bool has_state = basic_behavior == EXCEPTION_STATE || | |
209 basic_behavior == EXCEPTION_STATE_IDENTITY; | |
210 return has_state ? MACH_RCV_PORT_DIED : KERN_SUCCESS; | |
211 } | |
212 | |
213 private: | |
214 class Child { | |
215 public: | |
216 explicit Child(TestExceptionPorts* test_exception_ports) | |
217 : test_exception_ports_(test_exception_ports), | |
218 thread_(), | |
219 init_semaphore_(dispatch_semaphore_create(0)), | |
220 crash_semaphore_(dispatch_semaphore_create(0)) {} | |
221 | |
222 ~Child() { | |
223 dispatch_release(crash_semaphore_); | |
224 dispatch_release(init_semaphore_); | |
225 } | |
226 | |
227 void Run() { | |
228 ExceptionPorts self_task_ports(ExceptionPorts::kTargetTypeTask, | |
229 MACH_PORT_NULL); | |
230 ExceptionPorts self_thread_ports(ExceptionPorts::kTargetTypeThread, | |
231 MACH_PORT_NULL); | |
232 | |
233 mach_port_t remote_port = test_exception_ports_->RemotePort(); | |
234 | |
235 // Set the task’s and this thread’s exception ports, if appropriate. | |
236 if (test_exception_ports_->set_type() == kSetInProcess) { | |
237 ASSERT_TRUE(self_task_ports.SetExceptionPort( | |
238 EXC_MASK_CRASH, remote_port, EXCEPTION_DEFAULT, THREAD_STATE_NONE)); | |
239 | |
240 if (test_exception_ports_->set_on() == kSetOnTaskAndThreads) { | |
241 ASSERT_TRUE(self_thread_ports.SetExceptionPort(EXC_MASK_CRASH, | |
242 remote_port, | |
243 EXCEPTION_STATE, | |
244 MACHINE_THREAD_STATE)); | |
245 } | |
246 } | |
247 | |
248 int rv_int = pthread_create(&thread_, NULL, ThreadMainThunk, this); | |
249 ASSERT_EQ(0, rv_int); | |
250 | |
251 // Wait for the new thread to be ready. | |
252 long rv_long = | |
253 dispatch_semaphore_wait(init_semaphore_, DISPATCH_TIME_FOREVER); | |
254 ASSERT_EQ(0, rv_long); | |
255 | |
256 // Tell the parent process that everything is set up. | |
257 char c = '\0'; | |
258 ssize_t rv_ssize = WriteFD(test_exception_ports_->WritePipeFD(), &c, 1); | |
259 ASSERT_EQ(1, rv_ssize) << ErrnoMessage("write"); | |
260 | |
261 // Wait for the parent process to say that its end is set up. | |
262 rv_ssize = ReadFD(test_exception_ports_->ReadPipeFD(), &c, 1); | |
263 ASSERT_EQ(1, rv_ssize) << ErrnoMessage("read"); | |
264 EXPECT_EQ('\0', c); | |
265 | |
266 // Regardless of where ExceptionPorts::SetExceptionPort() ran, | |
267 // ExceptionPorts::GetExceptionPorts() can always be tested in-process. | |
268 { | |
269 SCOPED_TRACE("task"); | |
270 TestGetExceptionPorts(self_task_ports, remote_port, EXCEPTION_DEFAULT); | |
271 } | |
272 | |
273 { | |
274 SCOPED_TRACE("main_thread"); | |
275 mach_port_t thread_handler = | |
276 (test_exception_ports_->set_on() == kSetOnTaskAndThreads) | |
277 ? remote_port | |
278 : MACH_PORT_NULL; | |
279 TestGetExceptionPorts( | |
280 self_thread_ports, thread_handler, EXCEPTION_STATE); | |
281 } | |
282 | |
283 // Let the other thread know it’s safe to proceed. | |
284 dispatch_semaphore_signal(crash_semaphore_); | |
285 | |
286 // If this thread is the one that crashes, do it. | |
287 if (test_exception_ports_->who_crashes() == kMainThreadCrashes) { | |
288 Crash(); | |
289 } | |
290 | |
291 // Reap the other thread. | |
292 rv_int = pthread_join(thread_, NULL); | |
293 ASSERT_EQ(0, rv_int); | |
294 } | |
295 | |
296 private: | |
297 // Calls ThreadMain(). | |
298 static void* ThreadMainThunk(void* argument) { | |
299 Child* self = reinterpret_cast<Child*>(argument); | |
300 return self->ThreadMain(); | |
301 } | |
302 | |
303 // Runs the “other” thread. | |
304 void* ThreadMain() { | |
305 ExceptionPorts self_thread_ports(ExceptionPorts::kTargetTypeThread, | |
306 MACH_PORT_NULL); | |
307 mach_port_t remote_port = test_exception_ports_->RemotePort(); | |
308 | |
309 // Set this thread’s exception handler, if appropriate. | |
310 if (test_exception_ports_->set_type() == kSetInProcess && | |
311 test_exception_ports_->set_on() == kSetOnTaskAndThreads) { | |
312 CHECK(self_thread_ports.SetExceptionPort(EXC_MASK_CRASH, | |
313 remote_port, | |
314 EXCEPTION_STATE_IDENTITY, | |
315 MACHINE_THREAD_STATE)); | |
316 } | |
317 | |
318 // Let the main thread know that this thread is ready. | |
319 dispatch_semaphore_signal(init_semaphore_); | |
320 | |
321 // Wait for the main thread to signal that it’s safe to proceed. | |
322 long rv = | |
323 dispatch_semaphore_wait(crash_semaphore_, DISPATCH_TIME_FOREVER); | |
324 CHECK_EQ(0, rv) << "dispatch_semaphore_wait"; | |
325 | |
326 // Regardless of where ExceptionPorts::SetExceptionPort() ran, | |
327 // ExceptionPorts::GetExceptionPorts() can always be tested in-process. | |
328 { | |
329 SCOPED_TRACE("other_thread"); | |
330 mach_port_t thread_handler = | |
331 (test_exception_ports_->set_on() == kSetOnTaskAndThreads) | |
332 ? remote_port | |
333 : MACH_PORT_NULL; | |
334 TestGetExceptionPorts( | |
335 self_thread_ports, thread_handler, EXCEPTION_STATE_IDENTITY); | |
336 } | |
337 | |
338 // If this thread is the one that crashes, do it. | |
339 if (test_exception_ports_->who_crashes() == kOtherThreadCrashes) { | |
340 Crash(); | |
341 } | |
342 | |
343 return NULL; | |
344 } | |
345 | |
346 // Crashes by performing a division by zero. The assignment is present to | |
347 // avoid optimizing zero_ out entirely by making it appear that its value | |
348 // might change. | |
349 static void Crash() { zero_ = 1 / zero_; } | |
350 | |
351 // The parent object. | |
352 TestExceptionPorts* test_exception_ports_; // weak | |
353 | |
354 // The “other” thread. | |
355 pthread_t thread_; | |
356 | |
357 // The main thread waits on this for the other thread to start up and | |
358 // perform its own initialization. | |
359 dispatch_semaphore_t init_semaphore_; | |
360 | |
361 // The child thread waits on this for the parent thread to indicate that the | |
362 // child can test its exception ports and possibly crash, as appropriate. | |
363 dispatch_semaphore_t crash_semaphore_; | |
364 | |
365 // Always zero. Crash() divides by this in order to trigger a crash. This is | |
366 // structured as a static volatile int to ward off aggressive compiler | |
367 // optimizations. | |
368 static volatile int zero_; | |
369 | |
370 DISALLOW_COPY_AND_ASSIGN(Child); | |
371 }; | |
372 | |
373 // MachMultiprocess: | |
374 | |
375 virtual void MachMultiprocessParent() override { | |
376 // Wait for the child process to be ready. It needs to have all of its | |
377 // threads set up before proceeding if in kSetOutOfProcess mode. | |
378 char c; | |
379 ssize_t rv = ReadFD(ReadPipeFD(), &c, 1); | |
380 ASSERT_EQ(1, rv) << ErrnoMessage("read"); | |
381 EXPECT_EQ('\0', c); | |
382 | |
383 mach_port_t local_port = LocalPort(); | |
384 | |
385 // Get an ExceptionPorts object for the task and each of its threads. | |
386 ExceptionPorts task_ports(ExceptionPorts::kTargetTypeTask, ChildTask()); | |
387 EXPECT_EQ("task", task_ports.TargetTypeName()); | |
388 | |
389 // Hopefully the threads returned by task_threads() are in order, with the | |
390 // main thread first and the other thread second. This is currently always | |
391 // the case, although nothing guarantees that it will remain so. | |
392 thread_act_array_t threads; | |
393 mach_msg_type_number_t thread_count = 0; | |
394 kern_return_t kr = task_threads(ChildTask(), &threads, &thread_count); | |
395 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "task_threads"); | |
396 | |
397 ScopedForbidReturn threads_need_owners; | |
398 ASSERT_EQ(2u, thread_count); | |
399 base::mac::ScopedMachSendRight main_thread(threads[0]); | |
400 base::mac::ScopedMachSendRight other_thread(threads[1]); | |
401 threads_need_owners.Disarm(); | |
402 | |
403 ExceptionPorts main_thread_ports(ExceptionPorts::kTargetTypeThread, | |
404 main_thread); | |
405 ExceptionPorts other_thread_ports(ExceptionPorts::kTargetTypeThread, | |
406 other_thread); | |
407 EXPECT_EQ("thread", main_thread_ports.TargetTypeName()); | |
408 EXPECT_EQ("thread", other_thread_ports.TargetTypeName()); | |
409 | |
410 if (set_type_ == kSetOutOfProcess) { | |
411 // Test ExceptionPorts::SetExceptionPorts() being called from | |
412 // out-of-process. | |
413 // | |
414 // local_port is only a receive right, but a send right is needed for | |
415 // ExceptionPorts::SetExceptionPort(). Make a send right, which can be | |
416 // deallocated once the calls to ExceptionPorts::SetExceptionPort() are | |
417 // done. | |
418 kr = mach_port_insert_right( | |
419 mach_task_self(), local_port, local_port, MACH_MSG_TYPE_MAKE_SEND); | |
420 ASSERT_EQ(KERN_SUCCESS, kr) | |
421 << MachErrorMessage(kr, "mach_port_insert_right"); | |
422 base::mac::ScopedMachSendRight send_owner(local_port); | |
423 | |
424 ASSERT_TRUE(task_ports.SetExceptionPort( | |
425 EXC_MASK_CRASH, local_port, EXCEPTION_DEFAULT, THREAD_STATE_NONE)); | |
426 | |
427 if (set_on_ == kSetOnTaskAndThreads) { | |
428 ASSERT_TRUE(main_thread_ports.SetExceptionPort( | |
429 EXC_MASK_CRASH, local_port, EXCEPTION_STATE, MACHINE_THREAD_STATE)); | |
430 | |
431 ASSERT_TRUE( | |
432 other_thread_ports.SetExceptionPort(EXC_MASK_CRASH, | |
433 local_port, | |
434 EXCEPTION_STATE_IDENTITY, | |
435 MACHINE_THREAD_STATE)); | |
436 } | |
437 } | |
438 | |
439 // Regardless of where ExceptionPorts::SetExceptionPort() ran, | |
440 // ExceptionPorts::GetExceptionPorts() can always be tested out-of-process. | |
441 { | |
442 SCOPED_TRACE("task"); | |
443 TestGetExceptionPorts(task_ports, local_port, EXCEPTION_DEFAULT); | |
444 } | |
445 | |
446 mach_port_t thread_handler = | |
447 (set_on_ == kSetOnTaskAndThreads) ? local_port : MACH_PORT_NULL; | |
448 | |
449 { | |
450 SCOPED_TRACE("main_thread"); | |
451 TestGetExceptionPorts(main_thread_ports, thread_handler, EXCEPTION_STATE); | |
452 } | |
453 | |
454 { | |
455 SCOPED_TRACE("other_thread"); | |
456 TestGetExceptionPorts( | |
457 other_thread_ports, thread_handler, EXCEPTION_STATE_IDENTITY); | |
458 } | |
459 | |
460 // Let the child process know that everything in the parent process is set | |
461 // up. | |
462 c = '\0'; | |
463 rv = WriteFD(WritePipeFD(), &c, 1); | |
464 ASSERT_EQ(1, rv) << ErrnoMessage("write"); | |
465 | |
466 if (who_crashes_ != kNobodyCrashes) { | |
467 kern_return_t kr = MachMessageServer::Run(this, | |
468 local_port, | |
469 MACH_MSG_OPTION_NONE, | |
470 MachMessageServer::kOneShot, | |
471 MachMessageServer::kBlocking, | |
472 0); | |
473 EXPECT_EQ(KERN_SUCCESS, kr) | |
474 << MachErrorMessage(kr, "MachMessageServer::Run"); | |
475 | |
476 EXPECT_TRUE(handled_); | |
477 } | |
478 | |
479 // Wait for the child process to exit or terminate, as indicated by it | |
480 // closing its pipe. This keeps LocalPort() alive in the child as | |
481 // RemotePort(), for the child’s use in its TestGetExceptionPorts(). | |
482 rv = ReadFD(ReadPipeFD(), &c, 1); | |
483 ASSERT_EQ(0, rv); | |
484 } | |
485 | |
486 virtual void MachMultiprocessChild() override { | |
487 Child child(this); | |
488 child.Run(); | |
489 } | |
490 | |
491 SetType set_type_; | |
492 SetOn set_on_; | |
493 WhoCrashes who_crashes_; | |
494 | |
495 // true if an exception message was handled. | |
496 bool handled_; | |
497 | |
498 DISALLOW_COPY_AND_ASSIGN(TestExceptionPorts); | |
499 }; | |
500 | |
501 volatile int TestExceptionPorts::Child::zero_ = 0; | |
502 | |
503 TEST(ExceptionPorts, TaskAndThreadExceptionPorts) { | |
504 struct Testcase { | |
505 TestExceptionPorts::SetType set_type; | |
506 TestExceptionPorts::SetOn set_on; | |
507 TestExceptionPorts::WhoCrashes who_crashes; | |
508 }; | |
509 const Testcase kTestcases[] = { | |
510 {TestExceptionPorts::kSetInProcess, | |
511 TestExceptionPorts::kSetOnTaskOnly, | |
512 TestExceptionPorts::kNobodyCrashes}, | |
513 {TestExceptionPorts::kSetInProcess, | |
514 TestExceptionPorts::kSetOnTaskOnly, | |
515 TestExceptionPorts::kMainThreadCrashes}, | |
516 {TestExceptionPorts::kSetInProcess, | |
517 TestExceptionPorts::kSetOnTaskOnly, | |
518 TestExceptionPorts::kOtherThreadCrashes}, | |
519 {TestExceptionPorts::kSetInProcess, | |
520 TestExceptionPorts::kSetOnTaskAndThreads, | |
521 TestExceptionPorts::kNobodyCrashes}, | |
522 {TestExceptionPorts::kSetInProcess, | |
523 TestExceptionPorts::kSetOnTaskAndThreads, | |
524 TestExceptionPorts::kMainThreadCrashes}, | |
525 {TestExceptionPorts::kSetInProcess, | |
526 TestExceptionPorts::kSetOnTaskAndThreads, | |
527 TestExceptionPorts::kOtherThreadCrashes}, | |
528 {TestExceptionPorts::kSetOutOfProcess, | |
529 TestExceptionPorts::kSetOnTaskOnly, | |
530 TestExceptionPorts::kNobodyCrashes}, | |
531 {TestExceptionPorts::kSetOutOfProcess, | |
532 TestExceptionPorts::kSetOnTaskOnly, | |
533 TestExceptionPorts::kMainThreadCrashes}, | |
534 {TestExceptionPorts::kSetOutOfProcess, | |
535 TestExceptionPorts::kSetOnTaskOnly, | |
536 TestExceptionPorts::kOtherThreadCrashes}, | |
537 {TestExceptionPorts::kSetOutOfProcess, | |
538 TestExceptionPorts::kSetOnTaskAndThreads, | |
539 TestExceptionPorts::kNobodyCrashes}, | |
540 {TestExceptionPorts::kSetOutOfProcess, | |
541 TestExceptionPorts::kSetOnTaskAndThreads, | |
542 TestExceptionPorts::kMainThreadCrashes}, | |
543 {TestExceptionPorts::kSetOutOfProcess, | |
544 TestExceptionPorts::kSetOnTaskAndThreads, | |
545 TestExceptionPorts::kOtherThreadCrashes}, | |
546 }; | |
547 | |
548 for (size_t index = 0; index < arraysize(kTestcases); ++index) { | |
549 const Testcase& testcase = kTestcases[index]; | |
550 SCOPED_TRACE( | |
551 base::StringPrintf("index %zu, set_type %d, set_on %d, who_crashes %d", | |
552 index, | |
553 testcase.set_type, | |
554 testcase.set_on, | |
555 testcase.who_crashes)); | |
556 | |
557 TestExceptionPorts test_exception_ports( | |
558 testcase.set_type, testcase.set_on, testcase.who_crashes); | |
559 test_exception_ports.Run(); | |
560 } | |
561 } | |
562 | |
563 TEST(ExceptionPorts, HostExceptionPorts) { | |
564 // ExceptionPorts isn’t expected to work as non-root. Just do a quick test to | |
565 // make sure that TargetTypeName() returns the right string, and that the | |
566 // underlying host_get_exception_ports() function appears to be called by | |
567 // looking for a KERN_INVALID_ARGUMENT return value. Or, on the off chance | |
568 // that the test is being run as root, just look for KERN_SUCCESS. | |
569 // host_set_exception_ports() is not tested, because if the test were running | |
570 // as root and the call succeeded, it would have global effects. | |
571 | |
572 base::mac::ScopedMachSendRight host(mach_host_self()); | |
573 ExceptionPorts explicit_host_ports(ExceptionPorts::kTargetTypeHost, host); | |
574 EXPECT_EQ("host", explicit_host_ports.TargetTypeName()); | |
575 | |
576 std::vector<ExceptionPorts::ExceptionHandler> handlers; | |
577 bool rv = explicit_host_ports.GetExceptionPorts( | |
578 ExcMaskAll() | EXC_MASK_CRASH, &handlers); | |
579 if (geteuid() == 0) { | |
580 EXPECT_TRUE(rv); | |
581 } else { | |
582 EXPECT_FALSE(rv); | |
583 } | |
584 | |
585 ExceptionPorts implicit_host_ports(ExceptionPorts::kTargetTypeHost, | |
586 MACH_PORT_NULL); | |
587 EXPECT_EQ("host", implicit_host_ports.TargetTypeName()); | |
588 | |
589 rv = implicit_host_ports.GetExceptionPorts( | |
590 ExcMaskAll() | EXC_MASK_CRASH, &handlers); | |
591 if (geteuid() == 0) { | |
592 EXPECT_TRUE(rv); | |
593 } else { | |
594 EXPECT_FALSE(rv); | |
595 } | |
596 } | |
597 | |
598 } // namespace | |
OLD | NEW |