1// Copyright (c) 2006, Google Inc. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above 11// copyright notice, this list of conditions and the following disclaimer 12// in the documentation and/or other materials provided with the 13// distribution. 14// * Neither the name of Google Inc. nor the names of its 15// contributors may be used to endorse or promote products derived from 16// this software without specific prior written permission. 17// 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30#include <mach/exc.h> 31#include <mach/mig.h> 32#include <pthread.h> 33#include <signal.h> 34#include <TargetConditionals.h> 35 36#include <map> 37 38#include "client/mac/handler/exception_handler.h" 39#include "client/mac/handler/minidump_generator.h" 40#include "common/mac/macho_utilities.h" 41#include "common/mac/scoped_task_suspend-inl.h" 42#include "google_breakpad/common/minidump_exception_mac.h" 43 44#ifndef __EXCEPTIONS 45// This file uses C++ try/catch (but shouldn't). Duplicate the macros from 46// <c++/4.2.1/exception_defines.h> allowing this file to work properly with 47// exceptions disabled even when other C++ libraries are used. #undef the try 48// and catch macros first in case libstdc++ is in use and has already provided 49// its own definitions. 50#undef try 51#define try if (true) 52#undef catch 53#define catch(X) if (false) 54#endif // __EXCEPTIONS 55 56#ifndef USE_PROTECTED_ALLOCATIONS 57#if TARGET_OS_IPHONE 58#define USE_PROTECTED_ALLOCATIONS 1 59#else 60#define USE_PROTECTED_ALLOCATIONS 0 61#endif 62#endif 63 64// If USE_PROTECTED_ALLOCATIONS is activated then the 65// gBreakpadAllocator needs to be setup in other code 66// ahead of time. Please see ProtectedMemoryAllocator.h 67// for more details. 68#if USE_PROTECTED_ALLOCATIONS 69 #include "protected_memory_allocator.h" 70 extern ProtectedMemoryAllocator *gBreakpadAllocator; 71#endif 72 73namespace google_breakpad { 74 75static union { 76#if USE_PROTECTED_ALLOCATIONS 77#if defined PAGE_MAX_SIZE 78 char protected_buffer[PAGE_MAX_SIZE] __attribute__((aligned(PAGE_MAX_SIZE))); 79#else 80 char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); 81#endif // defined PAGE_MAX_SIZE 82#endif // USE_PROTECTED_ALLOCATIONS 83 google_breakpad::ExceptionHandler *handler; 84} gProtectedData; 85 86using std::map; 87 88// These structures and techniques are illustrated in 89// Mac OS X Internals, Amit Singh, ch 9.7 90struct ExceptionMessage { 91 mach_msg_header_t header; 92 mach_msg_body_t body; 93 mach_msg_port_descriptor_t thread; 94 mach_msg_port_descriptor_t task; 95 NDR_record_t ndr; 96 exception_type_t exception; 97 mach_msg_type_number_t code_count; 98 integer_t code[EXCEPTION_CODE_MAX]; 99 char padding[512]; 100}; 101 102struct ExceptionParameters { 103 ExceptionParameters() : count(0) {} 104 mach_msg_type_number_t count; 105 exception_mask_t masks[EXC_TYPES_COUNT]; 106 mach_port_t ports[EXC_TYPES_COUNT]; 107 exception_behavior_t behaviors[EXC_TYPES_COUNT]; 108 thread_state_flavor_t flavors[EXC_TYPES_COUNT]; 109}; 110 111struct ExceptionReplyMessage { 112 mach_msg_header_t header; 113 NDR_record_t ndr; 114 kern_return_t return_code; 115}; 116 117// Only catch these three exceptions. The other ones are nebulously defined 118// and may result in treating a non-fatal exception as fatal. 119exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS | 120EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT; 121 122#if !TARGET_OS_IPHONE 123extern "C" { 124 // Forward declarations for functions that need "C" style compilation 125 boolean_t exc_server(mach_msg_header_t* request, 126 mach_msg_header_t* reply); 127 128 // This symbol must be visible to dlsym() - see 129 // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details. 130 kern_return_t catch_exception_raise(mach_port_t target_port, 131 mach_port_t failed_thread, 132 mach_port_t task, 133 exception_type_t exception, 134 exception_data_t code, 135 mach_msg_type_number_t code_count) 136 __attribute__((visibility("default"))); 137} 138#endif 139 140kern_return_t ForwardException(mach_port_t task, 141 mach_port_t failed_thread, 142 exception_type_t exception, 143 exception_data_t code, 144 mach_msg_type_number_t code_count); 145 146#if TARGET_OS_IPHONE 147// Implementation is based on the implementation generated by mig. 148boolean_t breakpad_exc_server(mach_msg_header_t* InHeadP, 149 mach_msg_header_t* OutHeadP) { 150 OutHeadP->msgh_bits = 151 MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0); 152 OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port; 153 /* Minimal size: routine() will update it if different */ 154 OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t); 155 OutHeadP->msgh_local_port = MACH_PORT_NULL; 156 OutHeadP->msgh_id = InHeadP->msgh_id + 100; 157 158 if (InHeadP->msgh_id != 2401) { 159 ((mig_reply_error_t*)OutHeadP)->NDR = NDR_record; 160 ((mig_reply_error_t*)OutHeadP)->RetCode = MIG_BAD_ID; 161 return FALSE; 162 } 163 164#ifdef __MigPackStructs 165#pragma pack(4) 166#endif 167 typedef struct { 168 mach_msg_header_t Head; 169 /* start of the kernel processed data */ 170 mach_msg_body_t msgh_body; 171 mach_msg_port_descriptor_t thread; 172 mach_msg_port_descriptor_t task; 173 /* end of the kernel processed data */ 174 NDR_record_t NDR; 175 exception_type_t exception; 176 mach_msg_type_number_t codeCnt; 177 integer_t code[2]; 178 mach_msg_trailer_t trailer; 179 } Request; 180 181 typedef struct { 182 mach_msg_header_t Head; 183 NDR_record_t NDR; 184 kern_return_t RetCode; 185 } Reply; 186#ifdef __MigPackStructs 187#pragma pack() 188#endif 189 190 Request* In0P = (Request*)InHeadP; 191 Reply* OutP = (Reply*)OutHeadP; 192 193 if (In0P->task.name != mach_task_self()) { 194 return FALSE; 195 } 196 OutP->RetCode = ForwardException(In0P->task.name, 197 In0P->thread.name, 198 In0P->exception, 199 In0P->code, 200 In0P->codeCnt); 201 OutP->NDR = NDR_record; 202 return TRUE; 203} 204#else 205boolean_t breakpad_exc_server(mach_msg_header_t* request, 206 mach_msg_header_t* reply) { 207 return exc_server(request, reply); 208} 209 210// Callback from exc_server() 211kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread, 212 mach_port_t task, 213 exception_type_t exception, 214 exception_data_t code, 215 mach_msg_type_number_t code_count) { 216 if (task != mach_task_self()) { 217 return KERN_FAILURE; 218 } 219 return ForwardException(task, failed_thread, exception, code, code_count); 220} 221#endif 222 223ExceptionHandler::ExceptionHandler(const string &dump_path, 224 FilterCallback filter, 225 MinidumpCallback callback, 226 void* callback_context, 227 bool install_handler, 228 const char* port_name) 229 : dump_path_(), 230 filter_(filter), 231 callback_(callback), 232 callback_context_(callback_context), 233 directCallback_(NULL), 234 handler_thread_(NULL), 235 handler_port_(MACH_PORT_NULL), 236 previous_(NULL), 237 installed_exception_handler_(false), 238 is_in_teardown_(false), 239 last_minidump_write_result_(false), 240 use_minidump_write_mutex_(false) { 241 // This will update to the ID and C-string pointers 242 set_dump_path(dump_path); 243 MinidumpGenerator::GatherSystemInformation(); 244#if !TARGET_OS_IPHONE 245 if (port_name) 246 crash_generation_client_.reset(new CrashGenerationClient(port_name)); 247#endif 248 Setup(install_handler); 249} 250 251// special constructor if we want to bypass minidump writing and 252// simply get a callback with the exception information 253ExceptionHandler::ExceptionHandler(DirectCallback callback, 254 void* callback_context, 255 bool install_handler) 256 : dump_path_(), 257 filter_(NULL), 258 callback_(NULL), 259 callback_context_(callback_context), 260 directCallback_(callback), 261 handler_thread_(NULL), 262 handler_port_(MACH_PORT_NULL), 263 previous_(NULL), 264 installed_exception_handler_(false), 265 is_in_teardown_(false), 266 last_minidump_write_result_(false), 267 use_minidump_write_mutex_(false) { 268 MinidumpGenerator::GatherSystemInformation(); 269 Setup(install_handler); 270} 271 272ExceptionHandler::~ExceptionHandler() { 273 Teardown(); 274} 275 276bool ExceptionHandler::WriteMinidump(bool write_exception_stream) { 277 // If we're currently writing, just return 278 if (use_minidump_write_mutex_) 279 return false; 280 281 use_minidump_write_mutex_ = true; 282 last_minidump_write_result_ = false; 283 284 // Lock the mutex. Since we just created it, this will return immediately. 285 if (pthread_mutex_lock(&minidump_write_mutex_) == 0) { 286 // Send an empty message to the handle port so that a minidump will 287 // be written 288 bool result = SendMessageToHandlerThread(write_exception_stream ? 289 kWriteDumpWithExceptionMessage : 290 kWriteDumpMessage); 291 if (!result) { 292 pthread_mutex_unlock(&minidump_write_mutex_); 293 return false; 294 } 295 296 // Wait for the minidump writer to complete its writing. It will unlock 297 // the mutex when completed 298 pthread_mutex_lock(&minidump_write_mutex_); 299 } 300 301 use_minidump_write_mutex_ = false; 302 UpdateNextID(); 303 return last_minidump_write_result_; 304} 305 306// static 307bool ExceptionHandler::WriteMinidump(const string &dump_path, 308 bool write_exception_stream, 309 MinidumpCallback callback, 310 void* callback_context) { 311 ExceptionHandler handler(dump_path, NULL, callback, callback_context, false, 312 NULL); 313 return handler.WriteMinidump(write_exception_stream); 314} 315 316// static 317bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child, 318 mach_port_t child_blamed_thread, 319 const string &dump_path, 320 MinidumpCallback callback, 321 void* callback_context) { 322 ScopedTaskSuspend suspend(child); 323 324 MinidumpGenerator generator(child, MACH_PORT_NULL); 325 string dump_id; 326 string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id); 327 328 generator.SetExceptionInformation(EXC_BREAKPOINT, 329#if defined(__i386__) || defined(__x86_64__) 330 EXC_I386_BPT, 331#elif defined(__ppc__) || defined(__ppc64__) 332 EXC_PPC_BREAKPOINT, 333#elif defined(__arm__) || defined(__aarch64__) 334 EXC_ARM_BREAKPOINT, 335#else 336#error architecture not supported 337#endif 338 0, 339 child_blamed_thread); 340 bool result = generator.Write(dump_filename.c_str()); 341 342 if (callback) { 343 return callback(dump_path.c_str(), dump_id.c_str(), 344 callback_context, result); 345 } 346 return result; 347} 348 349bool ExceptionHandler::WriteMinidumpWithException( 350 int exception_type, 351 int exception_code, 352 int exception_subcode, 353 breakpad_ucontext_t* task_context, 354 mach_port_t thread_name, 355 bool exit_after_write, 356 bool report_current_thread) { 357 bool result = false; 358 359 if (directCallback_) { 360 if (directCallback_(callback_context_, 361 exception_type, 362 exception_code, 363 exception_subcode, 364 thread_name) ) { 365 if (exit_after_write) 366 _exit(exception_type); 367 } 368#if !TARGET_OS_IPHONE 369 } else if (IsOutOfProcess()) { 370 if (exception_type && exception_code) { 371 // If this is a real exception, give the filter (if any) a chance to 372 // decide if this should be sent. 373 if (filter_ && !filter_(callback_context_)) 374 return false; 375 result = crash_generation_client_->RequestDumpForException( 376 exception_type, 377 exception_code, 378 exception_subcode, 379 thread_name); 380 if (result && exit_after_write) { 381 _exit(exception_type); 382 } 383 } 384#endif 385 } else { 386 string minidump_id; 387 388 // Putting the MinidumpGenerator in its own context will ensure that the 389 // destructor is executed, closing the newly created minidump file. 390 if (!dump_path_.empty()) { 391 MinidumpGenerator md(mach_task_self(), 392 report_current_thread ? MACH_PORT_NULL : 393 mach_thread_self()); 394 md.SetTaskContext(task_context); 395 if (exception_type && exception_code) { 396 // If this is a real exception, give the filter (if any) a chance to 397 // decide if this should be sent. 398 if (filter_ && !filter_(callback_context_)) 399 return false; 400 401 md.SetExceptionInformation(exception_type, exception_code, 402 exception_subcode, thread_name); 403 } 404 405 result = md.Write(next_minidump_path_c_); 406 } 407 408 // Call user specified callback (if any) 409 if (callback_) { 410 // If the user callback returned true and we're handling an exception 411 // (rather than just writing out the file), then we should exit without 412 // forwarding the exception to the next handler. 413 if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_, 414 result)) { 415 if (exit_after_write) 416 _exit(exception_type); 417 } 418 } 419 } 420 421 return result; 422} 423 424kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread, 425 exception_type_t exception, 426 exception_data_t code, 427 mach_msg_type_number_t code_count) { 428 // At this time, we should have called Uninstall() on the exception handler 429 // so that the current exception ports are the ones that we should be 430 // forwarding to. 431 ExceptionParameters current; 432 433 current.count = EXC_TYPES_COUNT; 434 mach_port_t current_task = mach_task_self(); 435 task_get_exception_ports(current_task, 436 s_exception_mask, 437 current.masks, 438 ¤t.count, 439 current.ports, 440 current.behaviors, 441 current.flavors); 442 443 // Find the first exception handler that matches the exception 444 unsigned int found; 445 for (found = 0; found < current.count; ++found) { 446 if (current.masks[found] & (1 << exception)) { 447 break; 448 } 449 } 450 451 // Nothing to forward 452 if (found == current.count) { 453 fprintf(stderr, "** No previous ports for forwarding!! \n"); 454 exit(KERN_FAILURE); 455 } 456 457 mach_port_t target_port = current.ports[found]; 458 exception_behavior_t target_behavior = current.behaviors[found]; 459 460 kern_return_t result; 461 // TODO: Handle the case where |target_behavior| has MACH_EXCEPTION_CODES 462 // set. https://code.google.com/p/google-breakpad/issues/detail?id=551 463 switch (target_behavior) { 464 case EXCEPTION_DEFAULT: 465 result = exception_raise(target_port, failed_thread, task, exception, 466 code, code_count); 467 break; 468 default: 469 fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior); 470 result = KERN_FAILURE; 471 break; 472 } 473 474 return result; 475} 476 477// static 478void* ExceptionHandler::WaitForMessage(void* exception_handler_class) { 479 ExceptionHandler* self = 480 reinterpret_cast<ExceptionHandler*>(exception_handler_class); 481 ExceptionMessage receive; 482 483 // Wait for the exception info 484 while (1) { 485 receive.header.msgh_local_port = self->handler_port_; 486 receive.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(receive)); 487 kern_return_t result = mach_msg(&(receive.header), 488 MACH_RCV_MSG | MACH_RCV_LARGE, 0, 489 receive.header.msgh_size, 490 self->handler_port_, 491 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 492 493 494 if (result == KERN_SUCCESS) { 495 // Uninstall our handler so that we don't get in a loop if the process of 496 // writing out a minidump causes an exception. However, if the exception 497 // was caused by a fork'd process, don't uninstall things 498 499 // If the actual exception code is zero, then we're calling this handler 500 // in a way that indicates that we want to either exit this thread or 501 // generate a minidump 502 // 503 // While reporting, all threads (except this one) must be suspended 504 // to avoid misleading stacks. If appropriate they will be resumed 505 // afterwards. 506 if (!receive.exception) { 507 // Don't touch self, since this message could have been sent 508 // from its destructor. 509 if (receive.header.msgh_id == kShutdownMessage) 510 return NULL; 511 512 self->SuspendThreads(); 513 514#if USE_PROTECTED_ALLOCATIONS 515 if (gBreakpadAllocator) 516 gBreakpadAllocator->Unprotect(); 517#endif 518 519 mach_port_t thread = MACH_PORT_NULL; 520 int exception_type = 0; 521 int exception_code = 0; 522 if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) { 523 thread = receive.thread.name; 524 exception_type = EXC_BREAKPOINT; 525#if defined(__i386__) || defined(__x86_64__) 526 exception_code = EXC_I386_BPT; 527#elif defined(__ppc__) || defined(__ppc64__) 528 exception_code = EXC_PPC_BREAKPOINT; 529#elif defined(__arm__) || defined(__aarch64__) 530 exception_code = EXC_ARM_BREAKPOINT; 531#else 532#error architecture not supported 533#endif 534 } 535 536 // Write out the dump and save the result for later retrieval 537 self->last_minidump_write_result_ = 538 self->WriteMinidumpWithException(exception_type, exception_code, 539 0, NULL, thread, 540 false, false); 541 542#if USE_PROTECTED_ALLOCATIONS 543 if (gBreakpadAllocator) 544 gBreakpadAllocator->Protect(); 545#endif 546 547 self->ResumeThreads(); 548 549 if (self->use_minidump_write_mutex_) 550 pthread_mutex_unlock(&self->minidump_write_mutex_); 551 } else { 552 // When forking a child process with the exception handler installed, 553 // if the child crashes, it will send the exception back to the parent 554 // process. The check for task == self_task() ensures that only 555 // exceptions that occur in the parent process are caught and 556 // processed. If the exception was not caused by this task, we 557 // still need to call into the exception server and have it return 558 // KERN_FAILURE (see catch_exception_raise) in order for the kernel 559 // to move onto the host exception handler for the child task 560 if (receive.task.name == mach_task_self()) { 561 self->SuspendThreads(); 562 563#if USE_PROTECTED_ALLOCATIONS 564 if (gBreakpadAllocator) 565 gBreakpadAllocator->Unprotect(); 566#endif 567 568 int subcode = 0; 569 if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1) 570 subcode = receive.code[1]; 571 572 // Generate the minidump with the exception data. 573 self->WriteMinidumpWithException(receive.exception, receive.code[0], 574 subcode, NULL, receive.thread.name, 575 true, false); 576 577#if USE_PROTECTED_ALLOCATIONS 578 // This may have become protected again within 579 // WriteMinidumpWithException, but it needs to be unprotected for 580 // UninstallHandler. 581 if (gBreakpadAllocator) 582 gBreakpadAllocator->Unprotect(); 583#endif 584 585 self->UninstallHandler(true); 586 587#if USE_PROTECTED_ALLOCATIONS 588 if (gBreakpadAllocator) 589 gBreakpadAllocator->Protect(); 590#endif 591 } 592 // Pass along the exception to the server, which will setup the 593 // message and call catch_exception_raise() and put the return 594 // code into the reply. 595 ExceptionReplyMessage reply; 596 if (!breakpad_exc_server(&receive.header, &reply.header)) 597 exit(1); 598 599 // Send a reply and exit 600 mach_msg(&(reply.header), MACH_SEND_MSG, 601 reply.header.msgh_size, 0, MACH_PORT_NULL, 602 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 603 } 604 } 605 } 606 607 return NULL; 608} 609 610// static 611void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { 612#if USE_PROTECTED_ALLOCATIONS 613 if (gBreakpadAllocator) 614 gBreakpadAllocator->Unprotect(); 615#endif 616 gProtectedData.handler->WriteMinidumpWithException( 617 EXC_SOFTWARE, 618 MD_EXCEPTION_CODE_MAC_ABORT, 619 0, 620 static_cast<breakpad_ucontext_t*>(uc), 621 mach_thread_self(), 622 true, 623 true); 624#if USE_PROTECTED_ALLOCATIONS 625 if (gBreakpadAllocator) 626 gBreakpadAllocator->Protect(); 627#endif 628} 629 630bool ExceptionHandler::InstallHandler() { 631 // If a handler is already installed, something is really wrong. 632 if (gProtectedData.handler != NULL) { 633 return false; 634 } 635 if (!IsOutOfProcess()) { 636 struct sigaction sa; 637 memset(&sa, 0, sizeof(sa)); 638 sigemptyset(&sa.sa_mask); 639 sigaddset(&sa.sa_mask, SIGABRT); 640 sa.sa_sigaction = ExceptionHandler::SignalHandler; 641 sa.sa_flags = SA_SIGINFO; 642 643 scoped_ptr<struct sigaction> old(new struct sigaction); 644 if (sigaction(SIGABRT, &sa, old.get()) == -1) { 645 return false; 646 } 647 old_handler_.swap(old); 648 gProtectedData.handler = this; 649#if USE_PROTECTED_ALLOCATIONS 650 assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0); 651 mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ); 652#endif 653 } 654 655 try { 656#if USE_PROTECTED_ALLOCATIONS 657 previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) ) 658 ExceptionParameters(); 659#else 660 previous_ = new ExceptionParameters(); 661#endif 662 } 663 catch (std::bad_alloc) { 664 return false; 665 } 666 667 // Save the current exception ports so that we can forward to them 668 previous_->count = EXC_TYPES_COUNT; 669 mach_port_t current_task = mach_task_self(); 670 kern_return_t result = task_get_exception_ports(current_task, 671 s_exception_mask, 672 previous_->masks, 673 &previous_->count, 674 previous_->ports, 675 previous_->behaviors, 676 previous_->flavors); 677 678 // Setup the exception ports on this task 679 if (result == KERN_SUCCESS) 680 result = task_set_exception_ports(current_task, s_exception_mask, 681 handler_port_, EXCEPTION_DEFAULT, 682 THREAD_STATE_NONE); 683 684 installed_exception_handler_ = (result == KERN_SUCCESS); 685 686 return installed_exception_handler_; 687} 688 689bool ExceptionHandler::UninstallHandler(bool in_exception) { 690 kern_return_t result = KERN_SUCCESS; 691 692 if (old_handler_.get()) { 693 sigaction(SIGABRT, old_handler_.get(), NULL); 694#if USE_PROTECTED_ALLOCATIONS 695 mprotect(gProtectedData.protected_buffer, PAGE_SIZE, 696 PROT_READ | PROT_WRITE); 697#endif 698 old_handler_.reset(); 699 gProtectedData.handler = NULL; 700 } 701 702 if (installed_exception_handler_) { 703 mach_port_t current_task = mach_task_self(); 704 705 // Restore the previous ports 706 for (unsigned int i = 0; i < previous_->count; ++i) { 707 result = task_set_exception_ports(current_task, previous_->masks[i], 708 previous_->ports[i], 709 previous_->behaviors[i], 710 previous_->flavors[i]); 711 if (result != KERN_SUCCESS) 712 return false; 713 } 714 715 // this delete should NOT happen if an exception just occurred! 716 if (!in_exception) { 717#if USE_PROTECTED_ALLOCATIONS 718 previous_->~ExceptionParameters(); 719#else 720 delete previous_; 721#endif 722 } 723 724 previous_ = NULL; 725 installed_exception_handler_ = false; 726 } 727 728 return result == KERN_SUCCESS; 729} 730 731bool ExceptionHandler::Setup(bool install_handler) { 732 if (pthread_mutex_init(&minidump_write_mutex_, NULL)) 733 return false; 734 735 // Create a receive right 736 mach_port_t current_task = mach_task_self(); 737 kern_return_t result = mach_port_allocate(current_task, 738 MACH_PORT_RIGHT_RECEIVE, 739 &handler_port_); 740 // Add send right 741 if (result == KERN_SUCCESS) 742 result = mach_port_insert_right(current_task, handler_port_, handler_port_, 743 MACH_MSG_TYPE_MAKE_SEND); 744 745 if (install_handler && result == KERN_SUCCESS) 746 if (!InstallHandler()) 747 return false; 748 749 if (result == KERN_SUCCESS) { 750 // Install the handler in its own thread, detached as we won't be joining. 751 pthread_attr_t attr; 752 pthread_attr_init(&attr); 753 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 754 int thread_create_result = pthread_create(&handler_thread_, &attr, 755 &WaitForMessage, this); 756 pthread_attr_destroy(&attr); 757 result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS; 758 } 759 760 return result == KERN_SUCCESS; 761} 762 763bool ExceptionHandler::Teardown() { 764 kern_return_t result = KERN_SUCCESS; 765 is_in_teardown_ = true; 766 767 if (!UninstallHandler(false)) 768 return false; 769 770 // Send an empty message so that the handler_thread exits 771 if (SendMessageToHandlerThread(kShutdownMessage)) { 772 mach_port_t current_task = mach_task_self(); 773 result = mach_port_deallocate(current_task, handler_port_); 774 if (result != KERN_SUCCESS) 775 return false; 776 } else { 777 return false; 778 } 779 780 handler_thread_ = NULL; 781 handler_port_ = MACH_PORT_NULL; 782 pthread_mutex_destroy(&minidump_write_mutex_); 783 784 return result == KERN_SUCCESS; 785} 786 787bool ExceptionHandler::SendMessageToHandlerThread( 788 HandlerThreadMessage message_id) { 789 ExceptionMessage msg; 790 memset(&msg, 0, sizeof(msg)); 791 msg.header.msgh_id = message_id; 792 if (message_id == kWriteDumpMessage || 793 message_id == kWriteDumpWithExceptionMessage) { 794 // Include this thread's port. 795 msg.thread.name = mach_thread_self(); 796 msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND; 797 msg.thread.type = MACH_MSG_PORT_DESCRIPTOR; 798 } 799 msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding); 800 msg.header.msgh_remote_port = handler_port_; 801 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 802 MACH_MSG_TYPE_MAKE_SEND_ONCE); 803 kern_return_t result = mach_msg(&(msg.header), 804 MACH_SEND_MSG | MACH_SEND_TIMEOUT, 805 msg.header.msgh_size, 0, 0, 806 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 807 808 return result == KERN_SUCCESS; 809} 810 811void ExceptionHandler::UpdateNextID() { 812 next_minidump_path_ = 813 (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_)); 814 815 next_minidump_path_c_ = next_minidump_path_.c_str(); 816 next_minidump_id_c_ = next_minidump_id_.c_str(); 817} 818 819bool ExceptionHandler::SuspendThreads() { 820 thread_act_port_array_t threads_for_task; 821 mach_msg_type_number_t thread_count; 822 823 if (task_threads(mach_task_self(), &threads_for_task, &thread_count)) 824 return false; 825 826 // suspend all of the threads except for this one 827 for (unsigned int i = 0; i < thread_count; ++i) { 828 if (threads_for_task[i] != mach_thread_self()) { 829 if (thread_suspend(threads_for_task[i])) 830 return false; 831 } 832 } 833 834 return true; 835} 836 837bool ExceptionHandler::ResumeThreads() { 838 thread_act_port_array_t threads_for_task; 839 mach_msg_type_number_t thread_count; 840 841 if (task_threads(mach_task_self(), &threads_for_task, &thread_count)) 842 return false; 843 844 // resume all of the threads except for this one 845 for (unsigned int i = 0; i < thread_count; ++i) { 846 if (threads_for_task[i] != mach_thread_self()) { 847 if (thread_resume(threads_for_task[i])) 848 return false; 849 } 850 } 851 852 return true; 853} 854 855} // namespace google_breakpad 856