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