| 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 |