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
73 namespace google_breakpad {
74
75 static 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
86 using std::map;
87
88 // These structures and techniques are illustrated in
89 // Mac OS X Internals, Amit Singh, ch 9.7
90 struct 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
102 struct ExceptionParameters {
ExceptionParametersgoogle_breakpad::ExceptionParameters103 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
111 struct 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.
119 exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
120 EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
121
122 #if !TARGET_OS_IPHONE
123 extern "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
140 kern_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.
breakpad_exc_server(mach_msg_header_t * InHeadP,mach_msg_header_t * OutHeadP)148 boolean_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
breakpad_exc_server(mach_msg_header_t * request,mach_msg_header_t * reply)205 boolean_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()
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)211 kern_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
ExceptionHandler(const string & dump_path,FilterCallback filter,MinidumpCallback callback,void * callback_context,bool install_handler,const char * port_name)223 ExceptionHandler::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
ExceptionHandler(DirectCallback callback,void * callback_context,bool install_handler)253 ExceptionHandler::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
~ExceptionHandler()272 ExceptionHandler::~ExceptionHandler() {
273 Teardown();
274 }
275
WriteMinidump(bool write_exception_stream)276 bool 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
WriteMinidump(const string & dump_path,bool write_exception_stream,MinidumpCallback callback,void * callback_context)307 bool 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
WriteMinidumpForChild(mach_port_t child,mach_port_t child_blamed_thread,const string & dump_path,MinidumpCallback callback,void * callback_context)317 bool 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
WriteMinidumpWithException(int exception_type,int exception_code,int exception_subcode,breakpad_ucontext_t * task_context,mach_port_t thread_name,bool exit_after_write,bool report_current_thread)349 bool 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
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)424 kern_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
WaitForMessage(void * exception_handler_class)478 void* 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
SignalHandler(int sig,siginfo_t * info,void * uc)611 void 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
InstallHandler()630 bool 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
UninstallHandler(bool in_exception)689 bool 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
Setup(bool install_handler)731 bool 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
Teardown()763 bool 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
SendMessageToHandlerThread(HandlerThreadMessage message_id)787 bool 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
UpdateNextID()811 void 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
SuspendThreads()819 bool 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
ResumeThreads()837 bool 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