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