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, |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 if (expect_port != MACH_PORT_NULL) { | 100 if (expect_port != MACH_PORT_NULL) { |
101 EXPECT_TRUE(found); | 101 EXPECT_TRUE(found); |
102 } else { | 102 } else { |
103 EXPECT_FALSE(found); | 103 EXPECT_FALSE(found); |
104 } | 104 } |
105 } | 105 } |
106 | 106 |
107 class TestExceptionPorts : public MachMultiprocess, | 107 class TestExceptionPorts : public MachMultiprocess, |
108 public UniversalMachExcServer::Interface { | 108 public UniversalMachExcServer::Interface { |
109 public: | 109 public: |
| 110 // Which entities to set exception ports for. |
| 111 enum SetOn { |
| 112 kSetOnTaskOnly = 0, |
| 113 kSetOnTaskAndThreads, |
| 114 }; |
| 115 |
110 // Where to call ExceptionPorts::SetExceptionPort() from. | 116 // Where to call ExceptionPorts::SetExceptionPort() from. |
111 enum SetType { | 117 enum SetType { |
112 // Call it from the child process on itself. | 118 // Call it from the child process on itself. |
113 kSetInProcess = 0, | 119 kSetInProcess = 0, |
114 | 120 |
115 // Call it from the parent process on the child. | 121 // Call it from the parent process on the child. |
116 kSetOutOfProcess, | 122 kSetOutOfProcess, |
117 }; | 123 }; |
118 | 124 |
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. | 125 // Which thread in the child process is expected to crash. |
126 enum WhoCrashes { | 126 enum WhoCrashes { |
127 kNobodyCrashes = 0, | 127 kNobodyCrashes = 0, |
128 kMainThreadCrashes, | 128 kMainThreadCrashes, |
129 kOtherThreadCrashes, | 129 kOtherThreadCrashes, |
130 }; | 130 }; |
131 | 131 |
132 TestExceptionPorts(SetType set_type, SetOn set_on, WhoCrashes who_crashes) | 132 TestExceptionPorts(SetOn set_on, SetType set_type, WhoCrashes who_crashes) |
133 : MachMultiprocess(), | 133 : MachMultiprocess(), |
134 UniversalMachExcServer::Interface(), | 134 UniversalMachExcServer::Interface(), |
| 135 set_on_(set_on), |
135 set_type_(set_type), | 136 set_type_(set_type), |
136 set_on_(set_on), | |
137 who_crashes_(who_crashes), | 137 who_crashes_(who_crashes), |
138 handled_(false) {} | 138 handled_(false) {} |
139 | 139 |
| 140 SetOn set_on() const { return set_on_; } |
140 SetType set_type() const { return set_type_; } | 141 SetType set_type() const { return set_type_; } |
141 SetOn set_on() const { return set_on_; } | |
142 WhoCrashes who_crashes() const { return who_crashes_; } | 142 WhoCrashes who_crashes() const { return who_crashes_; } |
143 | 143 |
144 // UniversalMachExcServer::Interface: | 144 // UniversalMachExcServer::Interface: |
145 | 145 |
146 virtual kern_return_t CatchMachException( | 146 virtual kern_return_t CatchMachException( |
147 exception_behavior_t behavior, | 147 exception_behavior_t behavior, |
148 exception_handler_t exception_port, | 148 exception_handler_t exception_port, |
149 thread_t thread, | 149 thread_t thread, |
150 task_t task, | 150 task_t task, |
151 exception_type_t exception, | 151 exception_type_t exception, |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
184 EXPECT_EQ(EXC_CRASH, exception); | 184 EXPECT_EQ(EXC_CRASH, exception); |
185 EXPECT_EQ(2u, code_count); | 185 EXPECT_EQ(2u, code_count); |
186 | 186 |
187 // The exception and code_count checks above would ideally use ASSERT_EQ so | 187 // The exception and code_count checks above would ideally use ASSERT_EQ so |
188 // that the next conditional would not be necessary, but ASSERT_* requires a | 188 // that the next conditional would not be necessary, but ASSERT_* requires a |
189 // function returning type void, and the interface dictates otherwise here. | 189 // function returning type void, and the interface dictates otherwise here. |
190 if (exception == EXC_CRASH && code_count >= 1) { | 190 if (exception == EXC_CRASH && code_count >= 1) { |
191 int signal; | 191 int signal; |
192 ExcCrashRecoverOriginalException(code[0], nullptr, &signal); | 192 ExcCrashRecoverOriginalException(code[0], nullptr, &signal); |
193 | 193 |
194 // The child crashed with a division by zero, which shows up as SIGFPE. | 194 // The child crashed with __builtin_trap(), which shows up as SIGILL. |
195 // This was chosen because it’s unlikely to be generated by testing or | 195 EXPECT_EQ(SIGILL, signal); |
196 // assertion failures. | |
197 EXPECT_EQ(SIGFPE, signal); | |
198 | 196 |
199 SetExpectedChildTermination(kTerminationSignal, signal); | 197 SetExpectedChildTermination(kTerminationSignal, signal); |
200 } | 198 } |
201 | 199 |
202 return ExcServerSuccessfulReturnValue(behavior, false); | 200 return ExcServerSuccessfulReturnValue(behavior, false); |
203 } | 201 } |
204 | 202 |
205 private: | 203 private: |
206 class Child { | 204 class Child { |
207 public: | 205 public: |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
319 } | 317 } |
320 | 318 |
321 // If this thread is the one that crashes, do it. | 319 // If this thread is the one that crashes, do it. |
322 if (test_exception_ports_->who_crashes() == kOtherThreadCrashes) { | 320 if (test_exception_ports_->who_crashes() == kOtherThreadCrashes) { |
323 Crash(); | 321 Crash(); |
324 } | 322 } |
325 | 323 |
326 return nullptr; | 324 return nullptr; |
327 } | 325 } |
328 | 326 |
329 // Crashes by performing a division by zero. The assignment is present to | 327 static void Crash() { |
330 // avoid optimizing zero_ out entirely by making it appear that its value | 328 __builtin_trap(); |
331 // might change. | 329 } |
332 static void Crash() { zero_ = 1 / zero_; } | |
333 | 330 |
334 // The parent object. | 331 // The parent object. |
335 TestExceptionPorts* test_exception_ports_; // weak | 332 TestExceptionPorts* test_exception_ports_; // weak |
336 | 333 |
337 // The “other” thread. | 334 // The “other” thread. |
338 pthread_t thread_; | 335 pthread_t thread_; |
339 | 336 |
340 // The main thread waits on this for the other thread to start up and | 337 // The main thread waits on this for the other thread to start up and |
341 // perform its own initialization. | 338 // perform its own initialization. |
342 Semaphore init_semaphore_; | 339 Semaphore init_semaphore_; |
343 | 340 |
344 // The child thread waits on this for the parent thread to indicate that the | 341 // The child thread waits on this for the parent thread to indicate that the |
345 // child can test its exception ports and possibly crash, as appropriate. | 342 // child can test its exception ports and possibly crash, as appropriate. |
346 Semaphore crash_semaphore_; | 343 Semaphore crash_semaphore_; |
347 | 344 |
348 // Always zero. Crash() divides by this in order to trigger a crash. This is | |
349 // structured as a static volatile int to ward off aggressive compiler | |
350 // optimizations. | |
351 static volatile int zero_; | |
352 | |
353 DISALLOW_COPY_AND_ASSIGN(Child); | 345 DISALLOW_COPY_AND_ASSIGN(Child); |
354 }; | 346 }; |
355 | 347 |
356 // MachMultiprocess: | 348 // MachMultiprocess: |
357 | 349 |
358 void MachMultiprocessParent() override { | 350 void MachMultiprocessParent() override { |
359 // Wait for the child process to be ready. It needs to have all of its | 351 // Wait for the child process to be ready. It needs to have all of its |
360 // threads set up before proceeding if in kSetOutOfProcess mode. | 352 // threads set up before proceeding if in kSetOutOfProcess mode. |
361 char c; | 353 char c; |
362 CheckedReadFile(ReadPipeHandle(), &c, 1); | 354 CheckedReadFile(ReadPipeHandle(), &c, 1); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
440 } | 432 } |
441 | 433 |
442 // Let the child process know that everything in the parent process is set | 434 // Let the child process know that everything in the parent process is set |
443 // up. | 435 // up. |
444 c = '\0'; | 436 c = '\0'; |
445 CheckedWriteFile(WritePipeHandle(), &c, 1); | 437 CheckedWriteFile(WritePipeHandle(), &c, 1); |
446 | 438 |
447 if (who_crashes_ != kNobodyCrashes) { | 439 if (who_crashes_ != kNobodyCrashes) { |
448 UniversalMachExcServer universal_mach_exc_server(this); | 440 UniversalMachExcServer universal_mach_exc_server(this); |
449 | 441 |
| 442 const mach_msg_timeout_t kTimeoutMs = 50; |
450 kern_return_t kr = | 443 kern_return_t kr = |
451 MachMessageServer::Run(&universal_mach_exc_server, | 444 MachMessageServer::Run(&universal_mach_exc_server, |
452 local_port, | 445 local_port, |
453 MACH_MSG_OPTION_NONE, | 446 MACH_MSG_OPTION_NONE, |
454 MachMessageServer::kOneShot, | 447 MachMessageServer::kOneShot, |
455 MachMessageServer::kReceiveLargeError, | 448 MachMessageServer::kReceiveLargeError, |
456 kMachMessageTimeoutWaitIndefinitely); | 449 kTimeoutMs); |
457 EXPECT_EQ(KERN_SUCCESS, kr) | 450 EXPECT_EQ(KERN_SUCCESS, kr) |
458 << MachErrorMessage(kr, "MachMessageServer::Run"); | 451 << MachErrorMessage(kr, "MachMessageServer::Run"); |
459 | 452 |
460 EXPECT_TRUE(handled_); | 453 EXPECT_TRUE(handled_); |
461 } | 454 } |
462 | 455 |
463 // Wait for the child process to exit or terminate, as indicated by it | 456 // Wait for the child process to exit or terminate, as indicated by it |
464 // closing its pipe. This keeps LocalPort() alive in the child as | 457 // closing its pipe. This keeps LocalPort() alive in the child as |
465 // RemotePort(), for the child’s use in its TestGetExceptionPorts(). | 458 // RemotePort(), for the child’s use in its TestGetExceptionPorts(). |
466 CheckedReadFileAtEOF(ReadPipeHandle()); | 459 CheckedReadFileAtEOF(ReadPipeHandle()); |
467 } | 460 } |
468 | 461 |
469 void MachMultiprocessChild() override { | 462 void MachMultiprocessChild() override { |
470 Child child(this); | 463 Child child(this); |
471 child.Run(); | 464 child.Run(); |
472 } | 465 } |
473 | 466 |
| 467 SetOn set_on_; |
474 SetType set_type_; | 468 SetType set_type_; |
475 SetOn set_on_; | |
476 WhoCrashes who_crashes_; | 469 WhoCrashes who_crashes_; |
477 | 470 |
478 // true if an exception message was handled. | 471 // true if an exception message was handled. |
479 bool handled_; | 472 bool handled_; |
480 | 473 |
481 DISALLOW_COPY_AND_ASSIGN(TestExceptionPorts); | 474 DISALLOW_COPY_AND_ASSIGN(TestExceptionPorts); |
482 }; | 475 }; |
483 | 476 |
484 volatile int TestExceptionPorts::Child::zero_ = 0; | 477 TEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_NoCrash) { |
| 478 TestExceptionPorts test_exception_ports( |
| 479 TestExceptionPorts::kSetOnTaskOnly, |
| 480 TestExceptionPorts::kSetInProcess, |
| 481 TestExceptionPorts::kNobodyCrashes); |
| 482 test_exception_ports.Run(); |
| 483 } |
485 | 484 |
486 TEST(ExceptionPorts, TaskAndThreadExceptionPorts) { | 485 TEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_MainThreadCrash) { |
487 struct Testcase { | 486 TestExceptionPorts test_exception_ports( |
488 TestExceptionPorts::SetType set_type; | 487 TestExceptionPorts::kSetOnTaskOnly, |
489 TestExceptionPorts::SetOn set_on; | 488 TestExceptionPorts::kSetInProcess, |
490 TestExceptionPorts::WhoCrashes who_crashes; | 489 TestExceptionPorts::kMainThreadCrashes); |
491 }; | 490 test_exception_ports.Run(); |
492 const Testcase kTestcases[] = { | 491 } |
493 {TestExceptionPorts::kSetInProcess, | |
494 TestExceptionPorts::kSetOnTaskOnly, | |
495 TestExceptionPorts::kNobodyCrashes}, | |
496 {TestExceptionPorts::kSetInProcess, | |
497 TestExceptionPorts::kSetOnTaskOnly, | |
498 TestExceptionPorts::kMainThreadCrashes}, | |
499 {TestExceptionPorts::kSetInProcess, | |
500 TestExceptionPorts::kSetOnTaskOnly, | |
501 TestExceptionPorts::kOtherThreadCrashes}, | |
502 {TestExceptionPorts::kSetInProcess, | |
503 TestExceptionPorts::kSetOnTaskAndThreads, | |
504 TestExceptionPorts::kNobodyCrashes}, | |
505 {TestExceptionPorts::kSetInProcess, | |
506 TestExceptionPorts::kSetOnTaskAndThreads, | |
507 TestExceptionPorts::kMainThreadCrashes}, | |
508 {TestExceptionPorts::kSetInProcess, | |
509 TestExceptionPorts::kSetOnTaskAndThreads, | |
510 TestExceptionPorts::kOtherThreadCrashes}, | |
511 {TestExceptionPorts::kSetOutOfProcess, | |
512 TestExceptionPorts::kSetOnTaskOnly, | |
513 TestExceptionPorts::kNobodyCrashes}, | |
514 {TestExceptionPorts::kSetOutOfProcess, | |
515 TestExceptionPorts::kSetOnTaskOnly, | |
516 TestExceptionPorts::kMainThreadCrashes}, | |
517 {TestExceptionPorts::kSetOutOfProcess, | |
518 TestExceptionPorts::kSetOnTaskOnly, | |
519 TestExceptionPorts::kOtherThreadCrashes}, | |
520 {TestExceptionPorts::kSetOutOfProcess, | |
521 TestExceptionPorts::kSetOnTaskAndThreads, | |
522 TestExceptionPorts::kNobodyCrashes}, | |
523 {TestExceptionPorts::kSetOutOfProcess, | |
524 TestExceptionPorts::kSetOnTaskAndThreads, | |
525 TestExceptionPorts::kMainThreadCrashes}, | |
526 {TestExceptionPorts::kSetOutOfProcess, | |
527 TestExceptionPorts::kSetOnTaskAndThreads, | |
528 TestExceptionPorts::kOtherThreadCrashes}, | |
529 }; | |
530 | 492 |
531 for (size_t index = 0; index < arraysize(kTestcases); ++index) { | 493 TEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_OtherThreadCrash) { |
532 const Testcase& testcase = kTestcases[index]; | 494 TestExceptionPorts test_exception_ports( |
533 SCOPED_TRACE( | 495 TestExceptionPorts::kSetOnTaskOnly, |
534 base::StringPrintf("index %zu, set_type %d, set_on %d, who_crashes %d", | 496 TestExceptionPorts::kSetInProcess, |
535 index, | 497 TestExceptionPorts::kOtherThreadCrashes); |
536 testcase.set_type, | 498 test_exception_ports.Run(); |
537 testcase.set_on, | 499 } |
538 testcase.who_crashes)); | |
539 | 500 |
540 TestExceptionPorts test_exception_ports( | 501 TEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetInProcess_NoCrash) { |
541 testcase.set_type, testcase.set_on, testcase.who_crashes); | 502 TestExceptionPorts test_exception_ports( |
542 test_exception_ports.Run(); | 503 TestExceptionPorts::kSetOnTaskAndThreads, |
543 } | 504 TestExceptionPorts::kSetInProcess, |
| 505 TestExceptionPorts::kNobodyCrashes); |
| 506 test_exception_ports.Run(); |
| 507 } |
| 508 |
| 509 TEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetInProcess_MainThreadCrash) { |
| 510 TestExceptionPorts test_exception_ports( |
| 511 TestExceptionPorts::kSetOnTaskAndThreads, |
| 512 TestExceptionPorts::kSetInProcess, |
| 513 TestExceptionPorts::kMainThreadCrashes); |
| 514 test_exception_ports.Run(); |
| 515 } |
| 516 |
| 517 TEST(ExceptionPorts, |
| 518 TaskAndThreadExceptionPorts_SetInProcess_OtherThreadCrash) { |
| 519 TestExceptionPorts test_exception_ports( |
| 520 TestExceptionPorts::kSetOnTaskAndThreads, |
| 521 TestExceptionPorts::kSetInProcess, |
| 522 TestExceptionPorts::kOtherThreadCrashes); |
| 523 test_exception_ports.Run(); |
| 524 } |
| 525 |
| 526 TEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_NoCrash) { |
| 527 TestExceptionPorts test_exception_ports( |
| 528 TestExceptionPorts::kSetOnTaskOnly, |
| 529 TestExceptionPorts::kSetOutOfProcess, |
| 530 TestExceptionPorts::kNobodyCrashes); |
| 531 test_exception_ports.Run(); |
| 532 } |
| 533 |
| 534 TEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_MainThreadCrash) { |
| 535 TestExceptionPorts test_exception_ports( |
| 536 TestExceptionPorts::kSetOnTaskOnly, |
| 537 TestExceptionPorts::kSetOutOfProcess, |
| 538 TestExceptionPorts::kMainThreadCrashes); |
| 539 test_exception_ports.Run(); |
| 540 } |
| 541 |
| 542 TEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_OtherThreadCrash) { |
| 543 TestExceptionPorts test_exception_ports( |
| 544 TestExceptionPorts::kSetOnTaskOnly, |
| 545 TestExceptionPorts::kSetOutOfProcess, |
| 546 TestExceptionPorts::kOtherThreadCrashes); |
| 547 test_exception_ports.Run(); |
| 548 } |
| 549 |
| 550 TEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetOutOfProcess_NoCrash) { |
| 551 TestExceptionPorts test_exception_ports( |
| 552 TestExceptionPorts::kSetOnTaskAndThreads, |
| 553 TestExceptionPorts::kSetOutOfProcess, |
| 554 TestExceptionPorts::kNobodyCrashes); |
| 555 test_exception_ports.Run(); |
| 556 } |
| 557 |
| 558 TEST(ExceptionPorts, |
| 559 TaskAndThreadExceptionPorts_SetOutOfProcess_MainThreadCrash) { |
| 560 TestExceptionPorts test_exception_ports( |
| 561 TestExceptionPorts::kSetOnTaskAndThreads, |
| 562 TestExceptionPorts::kSetOutOfProcess, |
| 563 TestExceptionPorts::kMainThreadCrashes); |
| 564 test_exception_ports.Run(); |
| 565 } |
| 566 |
| 567 TEST(ExceptionPorts, |
| 568 TaskAndThreadExceptionPorts_SetOutOfProcess_OtherThreadCrash) { |
| 569 TestExceptionPorts test_exception_ports( |
| 570 TestExceptionPorts::kSetOnTaskAndThreads, |
| 571 TestExceptionPorts::kSetOutOfProcess, |
| 572 TestExceptionPorts::kOtherThreadCrashes); |
| 573 test_exception_ports.Run(); |
544 } | 574 } |
545 | 575 |
546 TEST(ExceptionPorts, HostExceptionPorts) { | 576 TEST(ExceptionPorts, HostExceptionPorts) { |
547 // ExceptionPorts isn’t expected to work as non-root. Just do a quick test to | 577 // ExceptionPorts isn’t expected to work as non-root. Just do a quick test to |
548 // make sure that TargetTypeName() returns the right string, and that the | 578 // make sure that TargetTypeName() returns the right string, and that the |
549 // underlying host_get_exception_ports() function appears to be called by | 579 // underlying host_get_exception_ports() function appears to be called by |
550 // looking for a KERN_INVALID_ARGUMENT return value. Or, on the off chance | 580 // looking for a KERN_INVALID_ARGUMENT return value. Or, on the off chance |
551 // that the test is being run as root, just look for KERN_SUCCESS. | 581 // that the test is being run as root, just look for KERN_SUCCESS. |
552 // host_set_exception_ports() is not tested, because if the test were running | 582 // host_set_exception_ports() is not tested, because if the test were running |
553 // as root and the call succeeded, it would have global effects. | 583 // as root and the call succeeded, it would have global effects. |
(...skipping 20 matching lines...) Expand all Loading... |
574 if (geteuid() == 0) { | 604 if (geteuid() == 0) { |
575 EXPECT_TRUE(rv); | 605 EXPECT_TRUE(rv); |
576 } else { | 606 } else { |
577 EXPECT_FALSE(rv); | 607 EXPECT_FALSE(rv); |
578 } | 608 } |
579 } | 609 } |
580 | 610 |
581 } // namespace | 611 } // namespace |
582 } // namespace test | 612 } // namespace test |
583 } // namespace crashpad | 613 } // namespace crashpad |
OLD | NEW |