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 <signal.h>
31 #include <TargetConditionals.h>
32 
33 #include "client/mac/handler/minidump_generator.h"
34 #include "client/ios/exception_handler_no_mach.h"
35 
36 #ifndef USE_PROTECTED_ALLOCATIONS
37 #if TARGET_OS_TV
38 #define USE_PROTECTED_ALLOCATIONS 1
39 #else
40 #define USE_PROTECTED_ALLOCATIONS 0
41 #endif
42 #endif
43 
44 // If USE_PROTECTED_ALLOCATIONS is activated then the
45 // gBreakpadAllocator needs to be setup in other code
46 // ahead of time.  Please see ProtectedMemoryAllocator.h
47 // for more details.
48 #if USE_PROTECTED_ALLOCATIONS
49   #include "client/mac/handler/protected_memory_allocator.h"
50   extern ProtectedMemoryAllocator* gBreakpadAllocator;
51 #endif
52 
53 namespace google_breakpad {
54 
55 const int kExceptionSignals[] = {
56    // Core-generating signals.
57   SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGQUIT, SIGSEGV, SIGSYS, SIGTRAP, SIGEMT,
58   SIGXCPU, SIGXFSZ,
59   // Non-core-generating but terminating signals.
60   SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGPROF, SIGTERM, SIGUSR1, SIGUSR2,
61   SIGVTALRM, SIGXCPU, SIGXFSZ, SIGIO,
62 };
63 const int kNumHandledSignals =
64     sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
65 struct scoped_ptr<struct sigaction> old_handlers[kNumHandledSignals];
66 
67 static union {
68 #if USE_PROTECTED_ALLOCATIONS
69 #if defined PAGE_MAX_SIZE
70   char protected_buffer[PAGE_MAX_SIZE] __attribute__((aligned(PAGE_MAX_SIZE)));
71 #else
72   char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
73 #endif  // defined PAGE_MAX_SIZE
74 #endif  // USE_PROTECTED_ALLOCATIONS
75   google_breakpad::ExceptionHandler* handler;
76 } gProtectedData;
77 
ExceptionHandler(const string & dump_path,FilterCallback filter,MinidumpCallback callback,void * callback_context,bool install_handler,const char * port_name)78 ExceptionHandler::ExceptionHandler(const string& dump_path,
79                                    FilterCallback filter,
80                                    MinidumpCallback callback,
81                                    void* callback_context,
82                                    bool install_handler,
83                                    const char* port_name)
84     : dump_path_(),
85       filter_(filter),
86       callback_(callback),
87       callback_context_(callback_context),
88       directCallback_(NULL),
89       installed_exception_handler_(false),
90       is_in_teardown_(false) {
91   // This will update to the ID and C-string pointers
92   set_dump_path(dump_path);
93   MinidumpGenerator::GatherSystemInformation();
94   Setup();
95 }
96 
97 // special constructor if we want to bypass minidump writing and
98 // simply get a callback with the exception information
ExceptionHandler(DirectCallback callback,void * callback_context,bool install_handler)99 ExceptionHandler::ExceptionHandler(DirectCallback callback,
100                                    void* callback_context,
101                                    bool install_handler)
102     : dump_path_(),
103       filter_(NULL),
104       callback_(NULL),
105       callback_context_(callback_context),
106       directCallback_(callback),
107       installed_exception_handler_(false),
108       is_in_teardown_(false) {
109   MinidumpGenerator::GatherSystemInformation();
110   Setup();
111 }
112 
~ExceptionHandler()113 ExceptionHandler::~ExceptionHandler() {
114   Teardown();
115 }
116 
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)117 bool ExceptionHandler::WriteMinidumpWithException(
118     int exception_type,
119     int exception_code,
120     int exception_subcode,
121     breakpad_ucontext_t* task_context,
122     mach_port_t thread_name,
123     bool exit_after_write,
124     bool report_current_thread) {
125   bool result = false;
126 
127 #if !TARGET_OS_TV
128   exit_after_write = false;
129 #endif  // !TARGET_OS_TV
130 
131   if (directCallback_) {
132     if (directCallback_(callback_context_,
133                         exception_type,
134                         exception_code,
135                         exception_subcode,
136                         thread_name) ) {
137       if (exit_after_write)
138         _exit(exception_type);
139     }
140   } else {
141     string minidump_id;
142 
143     // Putting the MinidumpGenerator in its own context will ensure that the
144     // destructor is executed, closing the newly created minidump file.
145     if (!dump_path_.empty()) {
146       MinidumpGenerator md(mach_task_self(),
147                            report_current_thread ? MACH_PORT_NULL :
148                                                    mach_thread_self());
149       md.SetTaskContext(task_context);
150       if (exception_type && exception_code) {
151         // If this is a real exception, give the filter (if any) a chance to
152         // decide if this should be sent.
153         if (filter_ && !filter_(callback_context_))
154           return false;
155 
156         md.SetExceptionInformation(exception_type, exception_code,
157                                    exception_subcode, thread_name);
158       }
159 
160       result = md.Write(next_minidump_path_c_);
161     }
162 
163     // Call user specified callback (if any)
164     if (callback_) {
165       // If the user callback returned true and we're handling an exception
166       // (rather than just writing out the file), then we should exit without
167       // forwarding the exception to the next handler.
168       if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
169                     result)) {
170         if (exit_after_write)
171           _exit(exception_type);
172       }
173     }
174   }
175 
176   return result;
177 }
178 
179 // static
SignalHandler(int sig,siginfo_t * info,void * uc)180 void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
181 #if USE_PROTECTED_ALLOCATIONS
182   if (gBreakpadAllocator)
183     gBreakpadAllocator->Unprotect();
184 #endif
185   gProtectedData.handler->WriteMinidumpWithException(
186       EXC_SOFTWARE,
187       MD_EXCEPTION_CODE_MAC_ABORT,
188       0,
189       static_cast<breakpad_ucontext_t*>(uc),
190       mach_thread_self(),
191       true,
192       true);
193 #if USE_PROTECTED_ALLOCATIONS
194   if (gBreakpadAllocator)
195     gBreakpadAllocator->Protect();
196 #endif
197 }
198 
InstallHandlers()199 bool ExceptionHandler::InstallHandlers() {
200   // If a handler is already installed, something is really wrong.
201   if (gProtectedData.handler != NULL)
202     return false;
203   for (int i = 0; i < kNumHandledSignals; ++i) {
204     struct sigaction sa;
205     memset(&sa, 0, sizeof(sa));
206     sigemptyset(&sa.sa_mask);
207     sigaddset(&sa.sa_mask, kExceptionSignals[i]);
208     sa.sa_sigaction = ExceptionHandler::SignalHandler;
209     sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
210 
211     if (sigaction(kExceptionSignals[i], &sa, old_handlers[i].get()) == -1) {
212       return false;
213     }
214   }
215   gProtectedData.handler = this;
216 #if USE_PROTECTED_ALLOCATIONS
217   assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0);
218   mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ);
219 #endif  // USE_PROTECTED_ALLOCATIONS
220   installed_exception_handler_ = true;
221   return true;
222 }
223 
UninstallHandlers()224 bool ExceptionHandler::UninstallHandlers() {
225   for (int i = 0; i < kNumHandledSignals; ++i) {
226     if (old_handlers[i].get()) {
227       sigaction(kExceptionSignals[i], old_handlers[i].get(), NULL);
228       old_handlers[i].reset();
229     }
230   }
231 #if USE_PROTECTED_ALLOCATIONS
232   mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ | PROT_WRITE);
233 #endif  // USE_PROTECTED_ALLOCATIONS
234   gProtectedData.handler = NULL;
235   installed_exception_handler_ = false;
236   return true;
237 }
238 
Setup()239 bool ExceptionHandler::Setup() {
240   if (!InstallHandlers())
241     return false;
242   return true;
243 }
244 
Teardown()245 bool ExceptionHandler::Teardown() {
246   is_in_teardown_ = true;
247 
248   if (!UninstallHandlers())
249     return false;
250 
251   return true;
252 }
253 
UpdateNextID()254 void ExceptionHandler::UpdateNextID() {
255   next_minidump_path_ =
256     (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_));
257 
258   next_minidump_path_c_ = next_minidump_path_.c_str();
259   next_minidump_id_c_ = next_minidump_id_.c_str();
260 }
261 
262 }  // namespace google_breakpad
263