|
1 // Copyright (c) 2010 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 // The ExceptionHandler object installs signal handlers for a number of |
|
31 // signals. We rely on the signal handler running on the thread which crashed |
|
32 // in order to identify it. This is true of the synchronous signals (SEGV etc), |
|
33 // but not true of ABRT. Thus, if you send ABRT to yourself in a program which |
|
34 // uses ExceptionHandler, you need to use tgkill to direct it to the current |
|
35 // thread. |
|
36 // |
|
37 // The signal flow looks like this: |
|
38 // |
|
39 // SignalHandler (uses a global stack of ExceptionHandler objects to find |
|
40 // | one to handle the signal. If the first rejects it, try |
|
41 // | the second etc...) |
|
42 // V |
|
43 // HandleSignal ----------------------------| (clones a new process which |
|
44 // | | shares an address space with |
|
45 // (wait for cloned | the crashed process. This |
|
46 // process) | allows us to ptrace the crashed |
|
47 // | | process) |
|
48 // V V |
|
49 // (set signal handler to ThreadEntry (static function to bounce |
|
50 // SIG_DFL and rethrow, | back into the object) |
|
51 // killing the crashed | |
|
52 // process) V |
|
53 // DoDump (writes minidump) |
|
54 // | |
|
55 // V |
|
56 // sys_exit |
|
57 // |
|
58 |
|
59 // This code is a little fragmented. Different functions of the ExceptionHandler |
|
60 // class run in a number of different contexts. Some of them run in a normal |
|
61 // context and are easy to code, others run in a compromised context and the |
|
62 // restrictions at the top of minidump_writer.cc apply: no libc and use the |
|
63 // alternative malloc. Each function should have comment above it detailing the |
|
64 // context which it runs in. |
|
65 |
|
66 #include "client/linux/handler/exception_handler.h" |
|
67 |
|
68 #include <errno.h> |
|
69 #include <fcntl.h> |
|
70 #include <linux/limits.h> |
|
71 #include <sched.h> |
|
72 #include <signal.h> |
|
73 #include <stdio.h> |
|
74 #include <sys/mman.h> |
|
75 #include <sys/prctl.h> |
|
76 #include <sys/syscall.h> |
|
77 #include <sys/wait.h> |
|
78 #include <unistd.h> |
|
79 |
|
80 #include <sys/signal.h> |
|
81 #include <sys/ucontext.h> |
|
82 #include <sys/user.h> |
|
83 #include <ucontext.h> |
|
84 |
|
85 #include <algorithm> |
|
86 #include <utility> |
|
87 #include <vector> |
|
88 |
|
89 #include "common/linux/linux_libc_support.h" |
|
90 #include "common/memory.h" |
|
91 #include "client/linux/log/log.h" |
|
92 #include "client/linux/minidump_writer/linux_dumper.h" |
|
93 #include "client/linux/minidump_writer/minidump_writer.h" |
|
94 #include "common/linux/eintr_wrapper.h" |
|
95 #include "third_party/lss/linux_syscall_support.h" |
|
96 |
|
97 #include "linux/sched.h" |
|
98 |
|
99 #ifndef PR_SET_PTRACER |
|
100 #define PR_SET_PTRACER 0x59616d61 |
|
101 #endif |
|
102 |
|
103 // A wrapper for the tgkill syscall: send a signal to a specific thread. |
|
104 static int tgkill(pid_t tgid, pid_t tid, int sig) { |
|
105 return syscall(__NR_tgkill, tgid, tid, sig); |
|
106 return 0; |
|
107 } |
|
108 |
|
109 namespace google_breakpad { |
|
110 |
|
111 namespace { |
|
112 // The list of signals which we consider to be crashes. The default action for |
|
113 // all these signals must be Core (see man 7 signal) because we rethrow the |
|
114 // signal after handling it and expect that it'll be fatal. |
|
115 const int kExceptionSignals[] = { |
|
116 SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS |
|
117 }; |
|
118 const int kNumHandledSignals = |
|
119 sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]); |
|
120 struct sigaction old_handlers[kNumHandledSignals]; |
|
121 bool handlers_installed = false; |
|
122 |
|
123 // InstallAlternateStackLocked will store the newly installed stack in new_stack |
|
124 // and (if it exists) the previously installed stack in old_stack. |
|
125 stack_t old_stack; |
|
126 stack_t new_stack; |
|
127 bool stack_installed = false; |
|
128 |
|
129 // Create an alternative stack to run the signal handlers on. This is done since |
|
130 // the signal might have been caused by a stack overflow. |
|
131 // Runs before crashing: normal context. |
|
132 void InstallAlternateStackLocked() { |
|
133 if (stack_installed) |
|
134 return; |
|
135 |
|
136 memset(&old_stack, 0, sizeof(old_stack)); |
|
137 memset(&new_stack, 0, sizeof(new_stack)); |
|
138 |
|
139 // SIGSTKSZ may be too small to prevent the signal handlers from overrunning |
|
140 // the alternative stack. Ensure that the size of the alternative stack is |
|
141 // large enough. |
|
142 static const unsigned kSigStackSize = std::max(8192, SIGSTKSZ); |
|
143 |
|
144 // Only set an alternative stack if there isn't already one, or if the current |
|
145 // one is too small. |
|
146 if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp || |
|
147 old_stack.ss_size < kSigStackSize) { |
|
148 new_stack.ss_sp = malloc(kSigStackSize); |
|
149 new_stack.ss_size = kSigStackSize; |
|
150 |
|
151 if (sys_sigaltstack(&new_stack, NULL) == -1) { |
|
152 free(new_stack.ss_sp); |
|
153 return; |
|
154 } |
|
155 stack_installed = true; |
|
156 } |
|
157 } |
|
158 |
|
159 // Runs before crashing: normal context. |
|
160 void RestoreAlternateStackLocked() { |
|
161 if (!stack_installed) |
|
162 return; |
|
163 |
|
164 stack_t current_stack; |
|
165 if (sys_sigaltstack(NULL, ¤t_stack) == -1) |
|
166 return; |
|
167 |
|
168 // Only restore the old_stack if the current alternative stack is the one |
|
169 // installed by the call to InstallAlternateStackLocked. |
|
170 if (current_stack.ss_sp == new_stack.ss_sp) { |
|
171 if (old_stack.ss_sp) { |
|
172 if (sys_sigaltstack(&old_stack, NULL) == -1) |
|
173 return; |
|
174 } else { |
|
175 stack_t disable_stack; |
|
176 disable_stack.ss_flags = SS_DISABLE; |
|
177 if (sys_sigaltstack(&disable_stack, NULL) == -1) |
|
178 return; |
|
179 } |
|
180 } |
|
181 |
|
182 free(new_stack.ss_sp); |
|
183 stack_installed = false; |
|
184 } |
|
185 |
|
186 } // namespace |
|
187 |
|
188 // We can stack multiple exception handlers. In that case, this is the global |
|
189 // which holds the stack. |
|
190 std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL; |
|
191 pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = |
|
192 PTHREAD_MUTEX_INITIALIZER; |
|
193 |
|
194 // Runs before crashing: normal context. |
|
195 ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor, |
|
196 FilterCallback filter, |
|
197 MinidumpCallback callback, |
|
198 void* callback_context, |
|
199 bool install_handler, |
|
200 const int server_fd) |
|
201 : filter_(filter), |
|
202 callback_(callback), |
|
203 callback_context_(callback_context), |
|
204 minidump_descriptor_(descriptor), |
|
205 crash_handler_(NULL) { |
|
206 if (server_fd >= 0) |
|
207 crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd)); |
|
208 |
|
209 if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) |
|
210 minidump_descriptor_.UpdatePath(); |
|
211 |
|
212 pthread_mutex_lock(&handler_stack_mutex_); |
|
213 if (!handler_stack_) |
|
214 handler_stack_ = new std::vector<ExceptionHandler*>; |
|
215 if (install_handler) { |
|
216 InstallAlternateStackLocked(); |
|
217 InstallHandlersLocked(); |
|
218 } |
|
219 handler_stack_->push_back(this); |
|
220 pthread_mutex_unlock(&handler_stack_mutex_); |
|
221 } |
|
222 |
|
223 // Runs before crashing: normal context. |
|
224 ExceptionHandler::~ExceptionHandler() { |
|
225 pthread_mutex_lock(&handler_stack_mutex_); |
|
226 std::vector<ExceptionHandler*>::iterator handler = |
|
227 std::find(handler_stack_->begin(), handler_stack_->end(), this); |
|
228 handler_stack_->erase(handler); |
|
229 if (handler_stack_->empty()) { |
|
230 RestoreAlternateStackLocked(); |
|
231 RestoreHandlersLocked(); |
|
232 } |
|
233 pthread_mutex_unlock(&handler_stack_mutex_); |
|
234 } |
|
235 |
|
236 // Runs before crashing: normal context. |
|
237 // static |
|
238 bool ExceptionHandler::InstallHandlersLocked() { |
|
239 if (handlers_installed) |
|
240 return false; |
|
241 |
|
242 // Fail if unable to store all the old handlers. |
|
243 for (int i = 0; i < kNumHandledSignals; ++i) { |
|
244 if (sigaction(kExceptionSignals[i], NULL, &old_handlers[i]) == -1) |
|
245 return false; |
|
246 } |
|
247 |
|
248 struct sigaction sa; |
|
249 memset(&sa, 0, sizeof(sa)); |
|
250 sigemptyset(&sa.sa_mask); |
|
251 |
|
252 // Mask all exception signals when we're handling one of them. |
|
253 for (int i = 0; i < kNumHandledSignals; ++i) |
|
254 sigaddset(&sa.sa_mask, kExceptionSignals[i]); |
|
255 |
|
256 sa.sa_sigaction = SignalHandler; |
|
257 sa.sa_flags = SA_ONSTACK | SA_SIGINFO; |
|
258 |
|
259 for (int i = 0; i < kNumHandledSignals; ++i) { |
|
260 if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) { |
|
261 // At this point it is impractical to back out changes, and so failure to |
|
262 // install a signal is intentionally ignored. |
|
263 } |
|
264 } |
|
265 handlers_installed = true; |
|
266 return true; |
|
267 } |
|
268 |
|
269 // This function runs in a compromised context: see the top of the file. |
|
270 // Runs on the crashing thread. |
|
271 // static |
|
272 void ExceptionHandler::RestoreHandlersLocked() { |
|
273 if (!handlers_installed) |
|
274 return; |
|
275 |
|
276 for (int i = 0; i < kNumHandledSignals; ++i) { |
|
277 if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) { |
|
278 signal(kExceptionSignals[i], SIG_DFL); |
|
279 } |
|
280 } |
|
281 handlers_installed = false; |
|
282 } |
|
283 |
|
284 // void ExceptionHandler::set_crash_handler(HandlerCallback callback) { |
|
285 // crash_handler_ = callback; |
|
286 // } |
|
287 |
|
288 // This function runs in a compromised context: see the top of the file. |
|
289 // Runs on the crashing thread. |
|
290 // static |
|
291 void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { |
|
292 // All the exception signals are blocked at this point. |
|
293 pthread_mutex_lock(&handler_stack_mutex_); |
|
294 |
|
295 // Sometimes, Breakpad runs inside a process where some other buggy code |
|
296 // saves and restores signal handlers temporarily with 'signal' |
|
297 // instead of 'sigaction'. This loses the SA_SIGINFO flag associated |
|
298 // with this function. As a consequence, the values of 'info' and 'uc' |
|
299 // become totally bogus, generally inducing a crash. |
|
300 // |
|
301 // The following code tries to detect this case. When it does, it |
|
302 // resets the signal handlers with sigaction + SA_SIGINFO and returns. |
|
303 // This forces the signal to be thrown again, but this time the kernel |
|
304 // will call the function with the right arguments. |
|
305 struct sigaction cur_handler; |
|
306 if (sigaction(sig, NULL, &cur_handler) == 0 && |
|
307 (cur_handler.sa_flags & SA_SIGINFO) == 0) { |
|
308 // Reset signal handler with the right flags. |
|
309 sigemptyset(&cur_handler.sa_mask); |
|
310 sigaddset(&cur_handler.sa_mask, sig); |
|
311 |
|
312 cur_handler.sa_sigaction = SignalHandler; |
|
313 cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO; |
|
314 |
|
315 if (sigaction(sig, &cur_handler, NULL) == -1) { |
|
316 // When resetting the handler fails, try to reset the |
|
317 // default one to avoid an infinite loop here. |
|
318 signal(sig, SIG_DFL); |
|
319 } |
|
320 pthread_mutex_unlock(&handler_stack_mutex_); |
|
321 return; |
|
322 } |
|
323 |
|
324 bool handled = false; |
|
325 for (int i = handler_stack_->size() - 1; !handled && i >= 0; --i) { |
|
326 handled = (*handler_stack_)[i]->HandleSignal(sig, info, uc); |
|
327 } |
|
328 |
|
329 // Upon returning from this signal handler, sig will become unmasked and then |
|
330 // it will be retriggered. If one of the ExceptionHandlers handled it |
|
331 // successfully, restore the default handler. Otherwise, restore the |
|
332 // previously installed handler. Then, when the signal is retriggered, it will |
|
333 // be delivered to the appropriate handler. |
|
334 if (handled) { |
|
335 signal(sig, SIG_DFL); |
|
336 } else { |
|
337 RestoreHandlersLocked(); |
|
338 } |
|
339 |
|
340 pthread_mutex_unlock(&handler_stack_mutex_); |
|
341 |
|
342 if (info->si_code <= 0) { |
|
343 // This signal was sent by another process. (Positive values of |
|
344 // si_code are reserved for kernel-originated signals.) In order |
|
345 // to retrigger it, we have to queue a new signal. |
|
346 if (tgkill(getpid(), syscall(__NR_gettid), sig) < 0) { |
|
347 // If we failed to kill ourselves (e.g. because a sandbox disallows us |
|
348 // to do so), we instead resort to terminating our process. This will |
|
349 // result in an incorrect exit code. |
|
350 _exit(1); |
|
351 } |
|
352 } else { |
|
353 // This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV). |
|
354 // No need to reissue the signal. It will automatically trigger again, |
|
355 // when we return from the signal handler. |
|
356 } |
|
357 } |
|
358 |
|
359 struct ThreadArgument { |
|
360 pid_t pid; // the crashing process |
|
361 const MinidumpDescriptor* minidump_descriptor; |
|
362 ExceptionHandler* handler; |
|
363 const void* context; // a CrashContext structure |
|
364 size_t context_size; |
|
365 }; |
|
366 |
|
367 // This is the entry function for the cloned process. We are in a compromised |
|
368 // context here: see the top of the file. |
|
369 // static |
|
370 int ExceptionHandler::ThreadEntry(void *arg) { |
|
371 const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg); |
|
372 |
|
373 // Block here until the crashing process unblocks us when |
|
374 // we're allowed to use ptrace |
|
375 thread_arg->handler->WaitForContinueSignal(); |
|
376 |
|
377 return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context, |
|
378 thread_arg->context_size) == false; |
|
379 } |
|
380 |
|
381 // This function runs in a compromised context: see the top of the file. |
|
382 // Runs on the crashing thread. |
|
383 bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) { |
|
384 if (filter_ && !filter_(callback_context_)) |
|
385 return false; |
|
386 |
|
387 // Allow ourselves to be dumped if the signal is trusted. |
|
388 bool signal_trusted = info->si_code > 0; |
|
389 bool signal_pid_trusted = info->si_code == SI_USER || |
|
390 info->si_code == SI_TKILL; |
|
391 if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) { |
|
392 sys_prctl(PR_SET_DUMPABLE, 1); |
|
393 } |
|
394 CrashContext context; |
|
395 memcpy(&context.siginfo, info, sizeof(siginfo_t)); |
|
396 memcpy(&context.context, uc, sizeof(struct ucontext)); |
|
397 #if !defined(__ARM_EABI__) |
|
398 // FP state is not part of user ABI on ARM Linux. |
|
399 struct ucontext *uc_ptr = (struct ucontext*)uc; |
|
400 if (uc_ptr->uc_mcontext.fpregs) { |
|
401 memcpy(&context.float_state, |
|
402 uc_ptr->uc_mcontext.fpregs, |
|
403 sizeof(context.float_state)); |
|
404 } |
|
405 #endif |
|
406 context.tid = syscall(__NR_gettid); |
|
407 if (crash_handler_ != NULL) { |
|
408 if (crash_handler_(&context, sizeof(context), callback_context_)) { |
|
409 return true; |
|
410 } |
|
411 } |
|
412 return GenerateDump(&context); |
|
413 } |
|
414 |
|
415 // This is a public interface to HandleSignal that allows the client to |
|
416 // generate a crash dump. This function may run in a compromised context. |
|
417 bool ExceptionHandler::SimulateSignalDelivery(int sig) { |
|
418 siginfo_t siginfo = {}; |
|
419 // Mimic a trusted signal to allow tracing the process (see |
|
420 // ExceptionHandler::HandleSignal(). |
|
421 siginfo.si_code = SI_USER; |
|
422 siginfo.si_pid = getpid(); |
|
423 struct ucontext context; |
|
424 getcontext(&context); |
|
425 return HandleSignal(sig, &siginfo, &context); |
|
426 } |
|
427 |
|
428 // This function may run in a compromised context: see the top of the file. |
|
429 bool ExceptionHandler::GenerateDump(CrashContext *context) { |
|
430 if (IsOutOfProcess()) |
|
431 return crash_generation_client_->RequestDump(context, sizeof(*context)); |
|
432 |
|
433 static const unsigned kChildStackSize = 8000; |
|
434 PageAllocator allocator; |
|
435 uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize); |
|
436 if (!stack) |
|
437 return false; |
|
438 // clone() needs the top-most address. (scrub just to be safe) |
|
439 stack += kChildStackSize; |
|
440 my_memset(stack - 16, 0, 16); |
|
441 |
|
442 ThreadArgument thread_arg; |
|
443 thread_arg.handler = this; |
|
444 thread_arg.minidump_descriptor = &minidump_descriptor_; |
|
445 thread_arg.pid = getpid(); |
|
446 thread_arg.context = context; |
|
447 thread_arg.context_size = sizeof(*context); |
|
448 |
|
449 // We need to explicitly enable ptrace of parent processes on some |
|
450 // kernels, but we need to know the PID of the cloned process before we |
|
451 // can do this. Create a pipe here which we can use to block the |
|
452 // cloned process after creating it, until we have explicitly enabled ptrace |
|
453 if(sys_pipe(fdes) == -1) { |
|
454 // Creating the pipe failed. We'll log an error but carry on anyway, |
|
455 // as we'll probably still get a useful crash report. All that will happen |
|
456 // is the write() and read() calls will fail with EBADF |
|
457 static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \ |
|
458 sys_pipe failed:"; |
|
459 logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1); |
|
460 logger::write(strerror(errno), strlen(strerror(errno))); |
|
461 logger::write("\n", 1); |
|
462 } |
|
463 |
|
464 const pid_t child = sys_clone( |
|
465 ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, |
|
466 &thread_arg, NULL, NULL, NULL); |
|
467 |
|
468 int r, status; |
|
469 // Allow the child to ptrace us |
|
470 sys_prctl(PR_SET_PTRACER, child); |
|
471 SendContinueSignalToChild(); |
|
472 do { |
|
473 r = sys_waitpid(child, &status, __WALL); |
|
474 } while (r == -1 && errno == EINTR); |
|
475 |
|
476 sys_close(fdes[0]); |
|
477 sys_close(fdes[1]); |
|
478 |
|
479 if (r == -1) { |
|
480 static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:"; |
|
481 logger::write(msg, sizeof(msg) - 1); |
|
482 logger::write(strerror(errno), strlen(strerror(errno))); |
|
483 logger::write("\n", 1); |
|
484 } |
|
485 |
|
486 bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; |
|
487 if (callback_) |
|
488 success = callback_(minidump_descriptor_, callback_context_, success); |
|
489 return success; |
|
490 } |
|
491 |
|
492 // This function runs in a compromised context: see the top of the file. |
|
493 void ExceptionHandler::SendContinueSignalToChild() { |
|
494 static const char okToContinueMessage = 'a'; |
|
495 int r; |
|
496 r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char))); |
|
497 if(r == -1) { |
|
498 static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \ |
|
499 sys_write failed:"; |
|
500 logger::write(msg, sizeof(msg) - 1); |
|
501 logger::write(strerror(errno), strlen(strerror(errno))); |
|
502 logger::write("\n", 1); |
|
503 } |
|
504 } |
|
505 |
|
506 // This function runs in a compromised context: see the top of the file. |
|
507 // Runs on the cloned process. |
|
508 void ExceptionHandler::WaitForContinueSignal() { |
|
509 int r; |
|
510 char receivedMessage; |
|
511 r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char))); |
|
512 if(r == -1) { |
|
513 static const char msg[] = "ExceptionHandler::WaitForContinueSignal \ |
|
514 sys_read failed:"; |
|
515 logger::write(msg, sizeof(msg) - 1); |
|
516 logger::write(strerror(errno), strlen(strerror(errno))); |
|
517 logger::write("\n", 1); |
|
518 } |
|
519 } |
|
520 |
|
521 // This function runs in a compromised context: see the top of the file. |
|
522 // Runs on the cloned process. |
|
523 bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, |
|
524 size_t context_size) { |
|
525 if (minidump_descriptor_.IsFD()) { |
|
526 return google_breakpad::WriteMinidump(minidump_descriptor_.fd(), |
|
527 minidump_descriptor_.size_limit(), |
|
528 crashing_process, |
|
529 context, |
|
530 context_size, |
|
531 mapping_list_, |
|
532 app_memory_list_); |
|
533 } |
|
534 return google_breakpad::WriteMinidump(minidump_descriptor_.path(), |
|
535 minidump_descriptor_.size_limit(), |
|
536 crashing_process, |
|
537 context, |
|
538 context_size, |
|
539 mapping_list_, |
|
540 app_memory_list_); |
|
541 } |
|
542 |
|
543 // static |
|
544 bool ExceptionHandler::WriteMinidump(const string& dump_path, |
|
545 MinidumpCallback callback, |
|
546 void* callback_context) { |
|
547 MinidumpDescriptor descriptor(dump_path); |
|
548 ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1); |
|
549 return eh.WriteMinidump(); |
|
550 } |
|
551 |
|
552 bool ExceptionHandler::WriteMinidump() { |
|
553 if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) { |
|
554 // Update the path of the minidump so that this can be called multiple times |
|
555 // and new files are created for each minidump. This is done before the |
|
556 // generation happens, as clients may want to access the MinidumpDescriptor |
|
557 // after this call to find the exact path to the minidump file. |
|
558 minidump_descriptor_.UpdatePath(); |
|
559 } else if (minidump_descriptor_.IsFD()) { |
|
560 // Reposition the FD to its beginning and resize it to get rid of the |
|
561 // previous minidump info. |
|
562 lseek(minidump_descriptor_.fd(), 0, SEEK_SET); |
|
563 static_cast<void>(ftruncate(minidump_descriptor_.fd(), 0)); |
|
564 } |
|
565 |
|
566 // Allow this process to be dumped. |
|
567 sys_prctl(PR_SET_DUMPABLE, 1); |
|
568 |
|
569 CrashContext context; |
|
570 int getcontext_result = getcontext(&context.context); |
|
571 if (getcontext_result) |
|
572 return false; |
|
573 #if !defined(__ARM_EABI__) |
|
574 // FPU state is not part of ARM EABI ucontext_t. |
|
575 memcpy(&context.float_state, context.context.uc_mcontext.fpregs, |
|
576 sizeof(context.float_state)); |
|
577 #endif |
|
578 context.tid = sys_gettid(); |
|
579 |
|
580 // Add an exception stream to the minidump for better reporting. |
|
581 memset(&context.siginfo, 0, sizeof(context.siginfo)); |
|
582 context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED; |
|
583 #if defined(__i386__) |
|
584 context.siginfo.si_addr = |
|
585 reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_EIP]); |
|
586 #elif defined(__x86_64__) |
|
587 context.siginfo.si_addr = |
|
588 reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_RIP]); |
|
589 #elif defined(__arm__) |
|
590 context.siginfo.si_addr = |
|
591 reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc); |
|
592 #else |
|
593 #error "This code has not been ported to your platform yet." |
|
594 #endif |
|
595 |
|
596 return GenerateDump(&context); |
|
597 } |
|
598 |
|
599 void ExceptionHandler::AddMappingInfo(const string& name, |
|
600 const uint8_t identifier[sizeof(MDGUID)], |
|
601 uintptr_t start_address, |
|
602 size_t mapping_size, |
|
603 size_t file_offset) { |
|
604 MappingInfo info; |
|
605 info.start_addr = start_address; |
|
606 info.size = mapping_size; |
|
607 info.offset = file_offset; |
|
608 strncpy(info.name, name.c_str(), sizeof(info.name) - 1); |
|
609 info.name[sizeof(info.name) - 1] = '\0'; |
|
610 |
|
611 MappingEntry mapping; |
|
612 mapping.first = info; |
|
613 memcpy(mapping.second, identifier, sizeof(MDGUID)); |
|
614 mapping_list_.push_back(mapping); |
|
615 } |
|
616 |
|
617 void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) { |
|
618 AppMemoryList::iterator iter = |
|
619 std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); |
|
620 if (iter != app_memory_list_.end()) { |
|
621 // Don't allow registering the same pointer twice. |
|
622 return; |
|
623 } |
|
624 |
|
625 AppMemory app_memory; |
|
626 app_memory.ptr = ptr; |
|
627 app_memory.length = length; |
|
628 app_memory_list_.push_back(app_memory); |
|
629 } |
|
630 |
|
631 void ExceptionHandler::UnregisterAppMemory(void* ptr) { |
|
632 AppMemoryList::iterator iter = |
|
633 std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); |
|
634 if (iter != app_memory_list_.end()) { |
|
635 app_memory_list_.erase(iter); |
|
636 } |
|
637 } |
|
638 |
|
639 // static |
|
640 bool ExceptionHandler::WriteMinidumpForChild(pid_t child, |
|
641 pid_t child_blamed_thread, |
|
642 const string& dump_path, |
|
643 MinidumpCallback callback, |
|
644 void* callback_context) { |
|
645 // This function is not run in a compromised context. |
|
646 MinidumpDescriptor descriptor(dump_path); |
|
647 descriptor.UpdatePath(); |
|
648 if (!google_breakpad::WriteMinidump(descriptor.path(), |
|
649 child, |
|
650 child_blamed_thread)) |
|
651 return false; |
|
652 |
|
653 return callback ? callback(descriptor, callback_context, true) : true; |
|
654 } |
|
655 |
|
656 } // namespace google_breakpad |