Chromium Code Reviews| 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 |