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 <map>
31 #include <pthread.h>
32 
33 #include "client/mac/handler/exception_handler.h"
34 #include "client/mac/handler/minidump_generator.h"
35 #include "common/mac/macho_utilities.h"
36 #include "common/mac/scoped_task_suspend-inl.h"
37 
38 #ifndef USE_PROTECTED_ALLOCATIONS
39 #define USE_PROTECTED_ALLOCATIONS 0
40 #endif
41 
42 // If USE_PROTECTED_ALLOCATIONS is activated then the
43 // gBreakpadAllocator needs to be setup in other code
44 // ahead of time.  Please see ProtectedMemoryAllocator.h
45 // for more details.
46 #if USE_PROTECTED_ALLOCATIONS
47   #include "protected_memory_allocator.h"
48   extern ProtectedMemoryAllocator *gBreakpadAllocator;
49 #endif
50 
51 
52 namespace google_breakpad {
53 
54 using std::map;
55 
56 // These structures and techniques are illustrated in
57 // Mac OS X Internals, Amit Singh, ch 9.7
58 struct ExceptionMessage {
59   mach_msg_header_t           header;
60   mach_msg_body_t             body;
61   mach_msg_port_descriptor_t  thread;
62   mach_msg_port_descriptor_t  task;
63   NDR_record_t                ndr;
64   exception_type_t            exception;
65   mach_msg_type_number_t      code_count;
66   integer_t                   code[EXCEPTION_CODE_MAX];
67   char                        padding[512];
68 };
69 
70 struct ExceptionParameters {
ExceptionParametersgoogle_breakpad::ExceptionParameters71   ExceptionParameters() : count(0) {}
72   mach_msg_type_number_t count;
73   exception_mask_t masks[EXC_TYPES_COUNT];
74   mach_port_t ports[EXC_TYPES_COUNT];
75   exception_behavior_t behaviors[EXC_TYPES_COUNT];
76   thread_state_flavor_t flavors[EXC_TYPES_COUNT];
77 };
78 
79 struct ExceptionReplyMessage {
80   mach_msg_header_t  header;
81   NDR_record_t       ndr;
82   kern_return_t      return_code;
83 };
84 
85 // Only catch these three exceptions.  The other ones are nebulously defined
86 // and may result in treating a non-fatal exception as fatal.
87 exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
88 EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
89 
90 extern "C"
91 {
92   // Forward declarations for functions that need "C" style compilation
93   boolean_t exc_server(mach_msg_header_t *request,
94                        mach_msg_header_t *reply);
95 
96   // This symbol must be visible to dlsym() - see
97   // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
98   kern_return_t catch_exception_raise(mach_port_t target_port,
99                                       mach_port_t failed_thread,
100                                       mach_port_t task,
101                                       exception_type_t exception,
102                                       exception_data_t code,
103                                       mach_msg_type_number_t code_count)
104       __attribute__((visibility("default")));
105 
106   kern_return_t ForwardException(mach_port_t task,
107                                  mach_port_t failed_thread,
108                                  exception_type_t exception,
109                                  exception_data_t code,
110                                  mach_msg_type_number_t code_count);
111 
112   kern_return_t exception_raise(mach_port_t target_port,
113                                 mach_port_t failed_thread,
114                                 mach_port_t task,
115                                 exception_type_t exception,
116                                 exception_data_t exception_code,
117                                 mach_msg_type_number_t exception_code_count);
118 
119   kern_return_t
120     exception_raise_state(mach_port_t target_port,
121                           mach_port_t failed_thread,
122                           mach_port_t task,
123                           exception_type_t exception,
124                           exception_data_t exception_code,
125                           mach_msg_type_number_t code_count,
126                           thread_state_flavor_t *target_flavor,
127                           thread_state_t in_thread_state,
128                           mach_msg_type_number_t in_thread_state_count,
129                           thread_state_t out_thread_state,
130                           mach_msg_type_number_t *out_thread_state_count);
131 
132   kern_return_t
133     exception_raise_state_identity(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 exception_code,
138                                    mach_msg_type_number_t exception_code_count,
139                                    thread_state_flavor_t *target_flavor,
140                                    thread_state_t in_thread_state,
141                                    mach_msg_type_number_t in_thread_state_count,
142                                    thread_state_t out_thread_state,
143                                    mach_msg_type_number_t *out_thread_state_count);
144 
145   kern_return_t breakpad_exception_raise_state(mach_port_t exception_port,
146                                                exception_type_t exception,
147                                                const exception_data_t code,
148                                                mach_msg_type_number_t codeCnt,
149                                                int *flavor,
150                                                const thread_state_t old_state,
151                                                mach_msg_type_number_t old_stateCnt,
152                                                thread_state_t new_state,
153                                                mach_msg_type_number_t *new_stateCnt
154                                                );
155 
156   kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port,
157                                                         mach_port_t thread,
158                                                         mach_port_t task,
159                                                         exception_type_t exception,
160                                                         exception_data_t code,
161                                                         mach_msg_type_number_t codeCnt,
162                                                         int *flavor,
163                                                         thread_state_t old_state,
164                                                         mach_msg_type_number_t old_stateCnt,
165                                                         thread_state_t new_state,
166                                                         mach_msg_type_number_t *new_stateCnt
167                                                         );
168 
169   kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread,
170                                          mach_port_t task,
171                                          exception_type_t exception,
172                                          exception_data_t code,
173                                          mach_msg_type_number_t code_count);
174 }
175 
176 
177 
breakpad_exception_raise_state(mach_port_t exception_port,exception_type_t exception,const exception_data_t code,mach_msg_type_number_t codeCnt,int * flavor,const thread_state_t old_state,mach_msg_type_number_t old_stateCnt,thread_state_t new_state,mach_msg_type_number_t * new_stateCnt)178 kern_return_t breakpad_exception_raise_state(mach_port_t exception_port,
179 					     exception_type_t exception,
180 					     const exception_data_t code,
181 					     mach_msg_type_number_t codeCnt,
182 					     int *flavor,
183 					     const thread_state_t old_state,
184 					     mach_msg_type_number_t old_stateCnt,
185 					     thread_state_t new_state,
186 					     mach_msg_type_number_t *new_stateCnt
187                                              )
188 {
189   return KERN_SUCCESS;
190 }
191 
breakpad_exception_raise_state_identity(mach_port_t exception_port,mach_port_t thread,mach_port_t task,exception_type_t exception,exception_data_t code,mach_msg_type_number_t codeCnt,int * flavor,thread_state_t old_state,mach_msg_type_number_t old_stateCnt,thread_state_t new_state,mach_msg_type_number_t * new_stateCnt)192 kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port,
193 						      mach_port_t thread,
194 						      mach_port_t task,
195 						      exception_type_t exception,
196 						      exception_data_t code,
197 						      mach_msg_type_number_t codeCnt,
198 						      int *flavor,
199 						      thread_state_t old_state,
200 						      mach_msg_type_number_t old_stateCnt,
201 						      thread_state_t new_state,
202 						      mach_msg_type_number_t *new_stateCnt
203                                                       )
204 {
205   return KERN_SUCCESS;
206 }
207 
breakpad_exception_raise(mach_port_t port,mach_port_t failed_thread,mach_port_t task,exception_type_t exception,exception_data_t code,mach_msg_type_number_t code_count)208 kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread,
209                                        mach_port_t task,
210                                        exception_type_t exception,
211                                        exception_data_t code,
212                                        mach_msg_type_number_t code_count) {
213 
214   if (task != mach_task_self()) {
215     return KERN_FAILURE;
216   }
217   return ForwardException(task, failed_thread, exception, code, code_count);
218 }
219 
220 
ExceptionHandler(const string & dump_path,FilterCallback filter,MinidumpCallback callback,void * callback_context,bool install_handler,const char * port_name)221 ExceptionHandler::ExceptionHandler(const string &dump_path,
222                                    FilterCallback filter,
223                                    MinidumpCallback callback,
224                                    void *callback_context,
225                                    bool install_handler,
226 				   const char *port_name)
227     : dump_path_(),
228       filter_(filter),
229       callback_(callback),
230       callback_context_(callback_context),
231       directCallback_(NULL),
232       handler_thread_(NULL),
233       handler_port_(MACH_PORT_NULL),
234       previous_(NULL),
235       installed_exception_handler_(false),
236       is_in_teardown_(false),
237       last_minidump_write_result_(false),
238       use_minidump_write_mutex_(false) {
239   // This will update to the ID and C-string pointers
240   set_dump_path(dump_path);
241   MinidumpGenerator::GatherSystemInformation();
242   if (port_name)
243     crash_generation_client_.reset(new CrashGenerationClient(port_name));
244   Setup(install_handler);
245 }
246 
247 // special constructor if we want to bypass minidump writing and
248 // simply get a callback with the exception information
ExceptionHandler(DirectCallback callback,void * callback_context,bool install_handler)249 ExceptionHandler::ExceptionHandler(DirectCallback callback,
250                                    void *callback_context,
251                                    bool install_handler)
252     : dump_path_(),
253       filter_(NULL),
254       callback_(NULL),
255       callback_context_(callback_context),
256       directCallback_(callback),
257       handler_thread_(NULL),
258       handler_port_(MACH_PORT_NULL),
259       previous_(NULL),
260       installed_exception_handler_(false),
261       is_in_teardown_(false),
262       last_minidump_write_result_(false),
263       use_minidump_write_mutex_(false) {
264   MinidumpGenerator::GatherSystemInformation();
265   Setup(install_handler);
266 }
267 
~ExceptionHandler()268 ExceptionHandler::~ExceptionHandler() {
269   Teardown();
270 }
271 
WriteMinidump(bool write_exception_stream)272 bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
273   // If we're currently writing, just return
274   if (use_minidump_write_mutex_)
275     return false;
276 
277   use_minidump_write_mutex_ = true;
278   last_minidump_write_result_ = false;
279 
280   // Lock the mutex.  Since we just created it, this will return immediately.
281   if (pthread_mutex_lock(&minidump_write_mutex_) == 0) {
282     // Send an empty message to the handle port so that a minidump will
283     // be written
284     SendMessageToHandlerThread(write_exception_stream ?
285                                    kWriteDumpWithExceptionMessage :
286                                    kWriteDumpMessage);
287 
288     // Wait for the minidump writer to complete its writing.  It will unlock
289     // the mutex when completed
290     pthread_mutex_lock(&minidump_write_mutex_);
291   }
292 
293   use_minidump_write_mutex_ = false;
294   UpdateNextID();
295   return last_minidump_write_result_;
296 }
297 
298 // static
WriteMinidump(const string & dump_path,bool write_exception_stream,MinidumpCallback callback,void * callback_context)299 bool ExceptionHandler::WriteMinidump(const string &dump_path,
300                                      bool write_exception_stream,
301                                      MinidumpCallback callback,
302                                      void *callback_context) {
303   ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
304 			   NULL);
305   return handler.WriteMinidump(write_exception_stream);
306 }
307 
308 // static
WriteMinidumpForChild(mach_port_t child,mach_port_t child_blamed_thread,const string & dump_path,MinidumpCallback callback,void * callback_context)309 bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
310 					     mach_port_t child_blamed_thread,
311 					     const string &dump_path,
312 					     MinidumpCallback callback,
313 					     void *callback_context) {
314   ScopedTaskSuspend suspend(child);
315 
316   MinidumpGenerator generator(child, MACH_PORT_NULL);
317   string dump_id;
318   string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id);
319 
320   generator.SetExceptionInformation(EXC_BREAKPOINT,
321 #if defined (__i386__) || defined(__x86_64__)
322 				    EXC_I386_BPT,
323 #elif defined (__ppc__) || defined (__ppc64__)
324 				    EXC_PPC_BREAKPOINT,
325 #else
326 #error architecture not supported
327 #endif
328 				    0,
329 				    child_blamed_thread);
330   bool result = generator.Write(dump_filename.c_str());
331 
332   if (callback) {
333     return callback(dump_path.c_str(), dump_id.c_str(),
334 		    callback_context, result);
335   }
336   return result;
337 }
338 
WriteMinidumpWithException(int exception_type,int exception_code,int exception_subcode,mach_port_t thread_name,bool exit_after_write)339 bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
340                                                   int exception_code,
341                                                   int exception_subcode,
342                                                   mach_port_t thread_name,
343                                                   bool exit_after_write) {
344   bool result = false;
345 
346   if (directCallback_) {
347     if (directCallback_(callback_context_,
348                         exception_type,
349                         exception_code,
350                         exception_subcode,
351                         thread_name) ) {
352       if (exit_after_write)
353         _exit(exception_type);
354     }
355   } else if (IsOutOfProcess()) {
356     if (exception_type && exception_code) {
357       // If this is a real exception, give the filter (if any) a chance to
358       // decide if this should be sent.
359       if (filter_ && !filter_(callback_context_))
360 	return false;
361       return crash_generation_client_->RequestDumpForException(
362 	         exception_type,
363 		 exception_code,
364 		 exception_subcode,
365 		 thread_name);
366     }
367   } else {
368     string minidump_id;
369 
370     // Putting the MinidumpGenerator in its own context will ensure that the
371     // destructor is executed, closing the newly created minidump file.
372     if (!dump_path_.empty()) {
373       MinidumpGenerator md;
374       if (exception_type && exception_code) {
375         // If this is a real exception, give the filter (if any) a chance to
376         // decide if this should be sent.
377         if (filter_ && !filter_(callback_context_))
378           return false;
379 
380         md.SetExceptionInformation(exception_type, exception_code,
381                                    exception_subcode, thread_name);
382       }
383 
384       result = md.Write(next_minidump_path_c_);
385     }
386 
387     // Call user specified callback (if any)
388     if (callback_) {
389       // If the user callback returned true and we're handling an exception
390       // (rather than just writing out the file), then we should exit without
391       // forwarding the exception to the next handler.
392       if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
393                     result)) {
394         if (exit_after_write)
395           _exit(exception_type);
396       }
397     }
398   }
399 
400   return result;
401 }
402 
ForwardException(mach_port_t task,mach_port_t failed_thread,exception_type_t exception,exception_data_t code,mach_msg_type_number_t code_count)403 kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
404                                exception_type_t exception,
405                                exception_data_t code,
406                                mach_msg_type_number_t code_count) {
407   // At this time, we should have called Uninstall() on the exception handler
408   // so that the current exception ports are the ones that we should be
409   // forwarding to.
410   ExceptionParameters current;
411 
412   current.count = EXC_TYPES_COUNT;
413   mach_port_t current_task = mach_task_self();
414   kern_return_t result = task_get_exception_ports(current_task,
415                                                   s_exception_mask,
416                                                   current.masks,
417                                                   &current.count,
418                                                   current.ports,
419                                                   current.behaviors,
420                                                   current.flavors);
421 
422   // Find the first exception handler that matches the exception
423   unsigned int found;
424   for (found = 0; found < current.count; ++found) {
425     if (current.masks[found] & (1 << exception)) {
426       break;
427     }
428   }
429 
430   // Nothing to forward
431   if (found == current.count) {
432     fprintf(stderr, "** No previous ports for forwarding!! \n");
433     exit(KERN_FAILURE);
434   }
435 
436   mach_port_t target_port = current.ports[found];
437   exception_behavior_t target_behavior = current.behaviors[found];
438   thread_state_flavor_t target_flavor = current.flavors[found];
439 
440   mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX;
441   breakpad_thread_state_data_t thread_state;
442   switch (target_behavior) {
443     case EXCEPTION_DEFAULT:
444       result = exception_raise(target_port, failed_thread, task, exception,
445                                code, code_count);
446       break;
447 
448     case EXCEPTION_STATE:
449       result = thread_get_state(failed_thread, target_flavor, thread_state,
450                                 &thread_state_count);
451       if (result == KERN_SUCCESS)
452         result = exception_raise_state(target_port, failed_thread, task,
453                                        exception, code,
454                                        code_count, &target_flavor,
455                                        thread_state, thread_state_count,
456                                        thread_state, &thread_state_count);
457       if (result == KERN_SUCCESS)
458         result = thread_set_state(failed_thread, target_flavor, thread_state,
459                                   thread_state_count);
460       break;
461 
462     case EXCEPTION_STATE_IDENTITY:
463       result = thread_get_state(failed_thread, target_flavor, thread_state,
464                                 &thread_state_count);
465       if (result == KERN_SUCCESS)
466         result = exception_raise_state_identity(target_port, failed_thread,
467                                                 task, exception, code,
468                                                 code_count, &target_flavor,
469                                                 thread_state,
470                                                 thread_state_count,
471                                                 thread_state,
472                                                 &thread_state_count);
473       if (result == KERN_SUCCESS)
474         result = thread_set_state(failed_thread, target_flavor, thread_state,
475                                   thread_state_count);
476       break;
477 
478     default:
479       fprintf(stderr, "** Unknown exception behavior\n");
480       result = KERN_FAILURE;
481       break;
482   }
483 
484   return result;
485 }
486 
487 // Callback from exc_server()
catch_exception_raise(mach_port_t port,mach_port_t failed_thread,mach_port_t task,exception_type_t exception,exception_data_t code,mach_msg_type_number_t code_count)488 kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
489                                     mach_port_t task,
490                                     exception_type_t exception,
491                                     exception_data_t code,
492                                     mach_msg_type_number_t code_count) {
493   if (task != mach_task_self()) {
494     return KERN_FAILURE;
495   }
496   return ForwardException(task, failed_thread, exception, code, code_count);
497 }
498 
499 // static
WaitForMessage(void * exception_handler_class)500 void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
501   ExceptionHandler *self =
502     reinterpret_cast<ExceptionHandler *>(exception_handler_class);
503   ExceptionMessage receive;
504 
505   // Wait for the exception info
506   while (1) {
507     receive.header.msgh_local_port = self->handler_port_;
508     receive.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(receive));
509     kern_return_t result = mach_msg(&(receive.header),
510                                     MACH_RCV_MSG | MACH_RCV_LARGE, 0,
511                                     receive.header.msgh_size,
512                                     self->handler_port_,
513                                     MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
514 
515 
516     if (result == KERN_SUCCESS) {
517       // Uninstall our handler so that we don't get in a loop if the process of
518       // writing out a minidump causes an exception.  However, if the exception
519       // was caused by a fork'd process, don't uninstall things
520 
521       // If the actual exception code is zero, then we're calling this handler
522       // in a way that indicates that we want to either exit this thread or
523       // generate a minidump
524       //
525       // While reporting, all threads (except this one) must be suspended
526       // to avoid misleading stacks.  If appropriate they will be resumed
527       // afterwards.
528       if (!receive.exception) {
529         // Don't touch self, since this message could have been sent
530         // from its destructor.
531         if (receive.header.msgh_id == kShutdownMessage)
532           return NULL;
533 
534         self->SuspendThreads();
535 
536 #if USE_PROTECTED_ALLOCATIONS
537         if(gBreakpadAllocator)
538           gBreakpadAllocator->Unprotect();
539 #endif
540 
541         mach_port_t thread = MACH_PORT_NULL;
542         int exception_type = 0;
543         int exception_code = 0;
544         if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
545           thread = receive.thread.name;
546           exception_type = EXC_BREAKPOINT;
547 #if defined (__i386__) || defined(__x86_64__)
548           exception_code = EXC_I386_BPT;
549 #elif defined (__ppc__) || defined (__ppc64__)
550           exception_code = EXC_PPC_BREAKPOINT;
551 #else
552 #error architecture not supported
553 #endif
554         }
555 
556         // Write out the dump and save the result for later retrieval
557         self->last_minidump_write_result_ =
558           self->WriteMinidumpWithException(exception_type, exception_code,
559                                            0, thread,
560                                            false);
561 
562 #if USE_PROTECTED_ALLOCATIONS
563         if(gBreakpadAllocator)
564           gBreakpadAllocator->Protect();
565 #endif
566 
567         self->ResumeThreads();
568 
569         if (self->use_minidump_write_mutex_)
570           pthread_mutex_unlock(&self->minidump_write_mutex_);
571       } else {
572         // When forking a child process with the exception handler installed,
573         // if the child crashes, it will send the exception back to the parent
574         // process.  The check for task == self_task() ensures that only
575         // exceptions that occur in the parent process are caught and
576         // processed.  If the exception was not caused by this task, we
577         // still need to call into the exception server and have it return
578         // KERN_FAILURE (see breakpad_exception_raise) in order for the kernel
579         // to move onto the host exception handler for the child task
580         if (receive.task.name == mach_task_self()) {
581           self->SuspendThreads();
582 
583 #if USE_PROTECTED_ALLOCATIONS
584         if(gBreakpadAllocator)
585           gBreakpadAllocator->Unprotect();
586 #endif
587 
588         int subcode = 0;
589         if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1)
590           subcode = receive.code[1];
591 
592         // Generate the minidump with the exception data.
593         self->WriteMinidumpWithException(receive.exception, receive.code[0],
594                                          subcode, receive.thread.name, true);
595 
596         self->UninstallHandler(true);
597 
598 #if USE_PROTECTED_ALLOCATIONS
599         if(gBreakpadAllocator)
600           gBreakpadAllocator->Protect();
601 #endif
602         }
603         // Pass along the exception to the server, which will setup the
604         // message and call breakpad_exception_raise() and put the return
605         // code into the reply.
606         ExceptionReplyMessage reply;
607         if (!exc_server(&receive.header, &reply.header))
608           exit(1);
609 
610         // Send a reply and exit
611         result = mach_msg(&(reply.header), MACH_SEND_MSG,
612                           reply.header.msgh_size, 0, MACH_PORT_NULL,
613                           MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
614       }
615     }
616   }
617 
618   return NULL;
619 }
620 
InstallHandler()621 bool ExceptionHandler::InstallHandler() {
622   try {
623 #if USE_PROTECTED_ALLOCATIONS
624     previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
625       ExceptionParameters();
626 #else
627     previous_ = new ExceptionParameters();
628 #endif
629 
630   }
631   catch (std::bad_alloc) {
632     return false;
633   }
634 
635   // Save the current exception ports so that we can forward to them
636   previous_->count = EXC_TYPES_COUNT;
637   mach_port_t current_task = mach_task_self();
638   kern_return_t result = task_get_exception_ports(current_task,
639                                                   s_exception_mask,
640                                                   previous_->masks,
641                                                   &previous_->count,
642                                                   previous_->ports,
643                                                   previous_->behaviors,
644                                                   previous_->flavors);
645 
646   // Setup the exception ports on this task
647   if (result == KERN_SUCCESS)
648     result = task_set_exception_ports(current_task, s_exception_mask,
649                                       handler_port_, EXCEPTION_DEFAULT,
650                                       THREAD_STATE_NONE);
651 
652   installed_exception_handler_ = (result == KERN_SUCCESS);
653 
654   return installed_exception_handler_;
655 }
656 
UninstallHandler(bool in_exception)657 bool ExceptionHandler::UninstallHandler(bool in_exception) {
658   kern_return_t result = KERN_SUCCESS;
659 
660   if (installed_exception_handler_) {
661     mach_port_t current_task = mach_task_self();
662 
663     // Restore the previous ports
664     for (unsigned int i = 0; i < previous_->count; ++i) {
665        result = task_set_exception_ports(current_task, previous_->masks[i],
666                                         previous_->ports[i],
667                                         previous_->behaviors[i],
668                                         previous_->flavors[i]);
669       if (result != KERN_SUCCESS)
670         return false;
671     }
672 
673     // this delete should NOT happen if an exception just occurred!
674     if (!in_exception) {
675 #if USE_PROTECTED_ALLOCATIONS
676       previous_->~ExceptionParameters();
677 #else
678       delete previous_;
679 #endif
680     }
681 
682     previous_ = NULL;
683     installed_exception_handler_ = false;
684   }
685 
686   return result == KERN_SUCCESS;
687 }
688 
Setup(bool install_handler)689 bool ExceptionHandler::Setup(bool install_handler) {
690   if (pthread_mutex_init(&minidump_write_mutex_, NULL))
691     return false;
692 
693   // Create a receive right
694   mach_port_t current_task = mach_task_self();
695   kern_return_t result = mach_port_allocate(current_task,
696                                             MACH_PORT_RIGHT_RECEIVE,
697                                             &handler_port_);
698   // Add send right
699   if (result == KERN_SUCCESS)
700     result = mach_port_insert_right(current_task, handler_port_, handler_port_,
701                                     MACH_MSG_TYPE_MAKE_SEND);
702 
703   if (install_handler && result == KERN_SUCCESS)
704     if (!InstallHandler())
705       return false;
706 
707   if (result == KERN_SUCCESS) {
708     // Install the handler in its own thread, detached as we won't be joining.
709     pthread_attr_t attr;
710     pthread_attr_init(&attr);
711     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
712     int thread_create_result = pthread_create(&handler_thread_, &attr,
713                                               &WaitForMessage, this);
714     pthread_attr_destroy(&attr);
715     result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS;
716   }
717 
718   return result == KERN_SUCCESS ? true : false;
719 }
720 
Teardown()721 bool ExceptionHandler::Teardown() {
722   kern_return_t result = KERN_SUCCESS;
723   is_in_teardown_ = true;
724 
725   if (!UninstallHandler(false))
726     return false;
727 
728   // Send an empty message so that the handler_thread exits
729   if (SendMessageToHandlerThread(kShutdownMessage)) {
730     mach_port_t current_task = mach_task_self();
731     result = mach_port_deallocate(current_task, handler_port_);
732     if (result != KERN_SUCCESS)
733       return false;
734   } else {
735     return false;
736   }
737 
738   handler_thread_ = NULL;
739   handler_port_ = NULL;
740   pthread_mutex_destroy(&minidump_write_mutex_);
741 
742   return result == KERN_SUCCESS;
743 }
744 
SendMessageToHandlerThread(HandlerThreadMessage message_id)745 bool ExceptionHandler::SendMessageToHandlerThread(
746     HandlerThreadMessage message_id) {
747   ExceptionMessage msg;
748   memset(&msg, 0, sizeof(msg));
749   msg.header.msgh_id = message_id;
750   if (message_id == kWriteDumpMessage ||
751       message_id == kWriteDumpWithExceptionMessage) {
752     // Include this thread's port.
753     msg.thread.name = mach_thread_self();
754     msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND;
755     msg.thread.type = MACH_MSG_PORT_DESCRIPTOR;
756   }
757   msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding);
758   msg.header.msgh_remote_port = handler_port_;
759   msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
760                                           MACH_MSG_TYPE_MAKE_SEND_ONCE);
761   kern_return_t result = mach_msg(&(msg.header),
762                                   MACH_SEND_MSG | MACH_SEND_TIMEOUT,
763                                   msg.header.msgh_size, 0, 0,
764                                   MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
765 
766   return result == KERN_SUCCESS;
767 }
768 
UpdateNextID()769 void ExceptionHandler::UpdateNextID() {
770   next_minidump_path_ =
771     (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_));
772 
773   next_minidump_path_c_ = next_minidump_path_.c_str();
774   next_minidump_id_c_ = next_minidump_id_.c_str();
775 }
776 
SuspendThreads()777 bool ExceptionHandler::SuspendThreads() {
778   thread_act_port_array_t   threads_for_task;
779   mach_msg_type_number_t    thread_count;
780 
781   if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
782     return false;
783 
784   // suspend all of the threads except for this one
785   for (unsigned int i = 0; i < thread_count; ++i) {
786     if (threads_for_task[i] != mach_thread_self()) {
787       if (thread_suspend(threads_for_task[i]))
788         return false;
789     }
790   }
791 
792   return true;
793 }
794 
ResumeThreads()795 bool ExceptionHandler::ResumeThreads() {
796   thread_act_port_array_t   threads_for_task;
797   mach_msg_type_number_t    thread_count;
798 
799   if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
800     return false;
801 
802   // resume all of the threads except for this one
803   for (unsigned int i = 0; i < thread_count; ++i) {
804     if (threads_for_task[i] != mach_thread_self()) {
805       if (thread_resume(threads_for_task[i]))
806         return false;
807     }
808   }
809 
810   return true;
811 }
812 
813 }  // namespace google_breakpad
814