xref: /aosp_15_r20/external/google-breakpad/src/client/solaris/handler/exception_handler.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1 // Copyright 2007 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 // Author: Alfred Peng
30 
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>  // Must come first
33 #endif
34 
35 #include <signal.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <unistd.h>
39 
40 #include <cassert>
41 #include <cstdlib>
42 #include <ctime>
43 
44 #include "client/solaris/handler/exception_handler.h"
45 #include "common/solaris/guid_creator.h"
46 #include "common/solaris/message_output.h"
47 #include "google_breakpad/common/minidump_format.h"
48 
49 namespace google_breakpad {
50 
51 // Signals that we are interested.
52 static const int kSigTable[] = {
53   SIGSEGV,
54   SIGABRT,
55   SIGFPE,
56   SIGILL,
57   SIGBUS
58 };
59 
60 std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
61 int ExceptionHandler::handler_stack_index_ = 0;
62 pthread_mutex_t ExceptionHandler::handler_stack_mutex_ =
63   PTHREAD_MUTEX_INITIALIZER;
64 
ExceptionHandler(const string & dump_path,FilterCallback filter,MinidumpCallback callback,void * callback_context,bool install_handler)65 ExceptionHandler::ExceptionHandler(const string& dump_path,
66                                    FilterCallback filter,
67                                    MinidumpCallback callback,
68                                    void* callback_context,
69                                    bool install_handler)
70     : filter_(filter),
71       callback_(callback),
72       callback_context_(callback_context),
73       dump_path_(),
74       installed_handler_(install_handler) {
75   set_dump_path(dump_path);
76 
77   if (install_handler) {
78     SetupHandler();
79   }
80 
81   if (install_handler) {
82     pthread_mutex_lock(&handler_stack_mutex_);
83 
84     if (handler_stack_ == NULL)
85       handler_stack_ = new std::vector<ExceptionHandler*>;
86     handler_stack_->push_back(this);
87     pthread_mutex_unlock(&handler_stack_mutex_);
88   }
89 }
90 
~ExceptionHandler()91 ExceptionHandler::~ExceptionHandler() {
92   TeardownAllHandlers();
93   pthread_mutex_lock(&handler_stack_mutex_);
94   if (handler_stack_->back() == this) {
95     handler_stack_->pop_back();
96   } else {
97     print_message1(2, "warning: removing Breakpad handler out of order\n");
98     for (std::vector<ExceptionHandler*>::iterator iterator =
99          handler_stack_->begin();
100          iterator != handler_stack_->end();
101          ++iterator) {
102       if (*iterator == this) {
103         handler_stack_->erase(iterator);
104       }
105     }
106   }
107 
108   if (handler_stack_->empty()) {
109     // When destroying the last ExceptionHandler that installed a handler,
110     // clean up the handler stack.
111     delete handler_stack_;
112     handler_stack_ = NULL;
113   }
114   pthread_mutex_unlock(&handler_stack_mutex_);
115 }
116 
WriteMinidump()117 bool ExceptionHandler::WriteMinidump() {
118   return InternalWriteMinidump(0, 0, NULL);
119 }
120 
121 // static
WriteMinidump(const string & dump_path,MinidumpCallback callback,void * callback_context)122 bool ExceptionHandler::WriteMinidump(const string& dump_path,
123                                      MinidumpCallback callback,
124                                      void* callback_context) {
125   ExceptionHandler handler(dump_path, NULL, callback,
126                            callback_context, false);
127   return handler.InternalWriteMinidump(0, 0, NULL);
128 }
129 
SetupHandler()130 void ExceptionHandler::SetupHandler() {
131   // Signal on a different stack to avoid using the stack
132   // of the crashing lwp.
133   struct sigaltstack sig_stack;
134   sig_stack.ss_sp = malloc(MINSIGSTKSZ);
135   if (sig_stack.ss_sp == NULL)
136     return;
137   sig_stack.ss_size = MINSIGSTKSZ;
138   sig_stack.ss_flags = 0;
139 
140   if (sigaltstack(&sig_stack, NULL) < 0)
141     return;
142   for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i)
143     SetupHandler(kSigTable[i]);
144 }
145 
SetupHandler(int signo)146 void ExceptionHandler::SetupHandler(int signo) {
147   struct sigaction act, old_act;
148   act.sa_handler = HandleException;
149   act.sa_flags = SA_ONSTACK;
150   if (sigaction(signo, &act, &old_act) < 0)
151     return;
152   old_handlers_[signo] = old_act.sa_handler;
153 }
154 
TeardownHandler(int signo)155 void ExceptionHandler::TeardownHandler(int signo) {
156   if (old_handlers_.find(signo) != old_handlers_.end()) {
157     struct sigaction act;
158     act.sa_handler = old_handlers_[signo];
159     act.sa_flags = 0;
160     sigaction(signo, &act, 0);
161   }
162 }
163 
TeardownAllHandlers()164 void ExceptionHandler::TeardownAllHandlers() {
165   for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i) {
166     TeardownHandler(kSigTable[i]);
167   }
168 }
169 
170 // static
HandleException(int signo)171 void ExceptionHandler::HandleException(int signo) {
172 //void ExceptionHandler::HandleException(int signo, siginfo_t* sip, ucontext_t* sig_ctx) {
173   // The context information about the signal is put on the stack of
174   // the signal handler frame as value parameter. For some reasons, the
175   // prototype of the handler doesn't declare this information as parameter, we
176   // will do it by hand. The stack layout for a signal handler frame is here:
177   // http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libproc/common/Pstack.c#81
178   //
179   // However, if we are being called by another signal handler passing the
180   // signal up the chain, then we may not have this random extra parameter,
181   // so we may have to walk the stack to find it.  We do the actual work
182   // on another thread, where it's a little safer, but we want the ebp
183   // from this frame to find it.
184   uintptr_t current_ebp = (uintptr_t)_getfp();
185 
186   pthread_mutex_lock(&handler_stack_mutex_);
187   ExceptionHandler* current_handler =
188     handler_stack_->at(handler_stack_->size() - ++handler_stack_index_);
189   pthread_mutex_unlock(&handler_stack_mutex_);
190 
191   // Restore original handler.
192   current_handler->TeardownHandler(signo);
193 
194   ucontext_t* sig_ctx = NULL;
195   if (current_handler->InternalWriteMinidump(signo, current_ebp, &sig_ctx)) {
196 //  if (current_handler->InternalWriteMinidump(signo, &sig_ctx)) {
197     // Fully handled this exception, safe to exit.
198     exit(EXIT_FAILURE);
199   } else {
200     // Exception not fully handled, will call the next handler in stack to
201     // process it.
202     typedef void (*SignalHandler)(int signo);
203     SignalHandler old_handler =
204       reinterpret_cast<SignalHandler>(current_handler->old_handlers_[signo]);
205     if (old_handler != NULL)
206       old_handler(signo);
207   }
208 
209   pthread_mutex_lock(&handler_stack_mutex_);
210   current_handler->SetupHandler(signo);
211   --handler_stack_index_;
212   // All the handlers in stack have been invoked to handle the exception,
213   // normally the process should be terminated and should not reach here.
214   // In case we got here, ask the OS to handle it to avoid endless loop,
215   // normally the OS will generate a core and termiate the process. This
216   // may be desired to debug the program.
217   if (handler_stack_index_ == 0)
218     signal(signo, SIG_DFL);
219   pthread_mutex_unlock(&handler_stack_mutex_);
220 }
221 
InternalWriteMinidump(int signo,uintptr_t sighandler_ebp,ucontext_t ** sig_ctx)222 bool ExceptionHandler::InternalWriteMinidump(int signo,
223                                              uintptr_t sighandler_ebp,
224                                              ucontext_t** sig_ctx) {
225   if (filter_ && !filter_(callback_context_))
226     return false;
227 
228   bool success = false;
229   GUID guid;
230   char guid_str[kGUIDStringLength + 1];
231   if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) {
232     char minidump_path[PATH_MAX];
233     snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp",
234              dump_path_c_, guid_str);
235 
236     // Block all the signals we want to process when writing minidump.
237     // We don't want it to be interrupted.
238     sigset_t sig_blocked, sig_old;
239     bool blocked = true;
240     sigfillset(&sig_blocked);
241     for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i)
242       sigdelset(&sig_blocked, kSigTable[i]);
243     if (sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old) != 0) {
244       blocked = false;
245       print_message1(2, "HandleException: failed to block signals.\n");
246     }
247 
248     success = minidump_generator_.WriteMinidumpToFile(
249                        minidump_path, signo, sighandler_ebp, sig_ctx);
250 
251     // Unblock the signals.
252     if (blocked)
253       sigprocmask(SIG_SETMASK, &sig_old, &sig_old);
254 
255     if (callback_)
256       success = callback_(dump_path_c_, guid_str, callback_context_, success);
257   }
258   return success;
259 }
260 
261 }  // namespace google_breakpad
262