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