// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // The ExceptionHandler object installs signal handlers for a number of // signals. We rely on the signal handler running on the thread which crashed // in order to identify it. This is true of the synchronous signals (SEGV etc), // but not true of ABRT. Thus, if you send ABRT to yourself in a program which // uses ExceptionHandler, you need to use tgkill to direct it to the current // thread. // // The signal flow looks like this: // // SignalHandler (uses a global stack of ExceptionHandler objects to find // | one to handle the signal. If the first rejects it, try // | the second etc...) // V // HandleSignal ----------------------------| (clones a new process which // | | shares an address space with // (wait for cloned | the crashed process. This // process) | allows us to ptrace the crashed // | | process) // V V // (set signal handler to ThreadEntry (static function to bounce // SIG_DFL and rethrow, | back into the object) // killing the crashed | // process) V // DoDump (writes minidump) // | // V // sys_exit // // This code is a little fragmented. Different functions of the ExceptionHandler // class run in a number of different contexts. Some of them run in a normal // context and are easy to code, others run in a compromised context and the // restrictions at the top of minidump_writer.cc apply: no libc and use the // alternative malloc. Each function should have comment above it detailing the // context which it runs in. #include "linux/handler/exception_handler.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common/basictypes.h" #include "common/linux/breakpad_getcontext.h" #include "common/linux/linux_libc_support.h" #include "common/memory_allocator.h" #include "linux/log/log.h" #include "linux/microdump_writer/microdump_writer.h" #include "linux/minidump_writer/linux_dumper.h" #include "linux/minidump_writer/minidump_writer.h" #include "common/linux/eintr_wrapper.h" #include "third_party/lss/linux_syscall_support.h" #if defined(MOZ_OXIDIZED_BREAKPAD) #include "nsString.h" #include "mozilla/toolkit/crashreporter/rust_minidump_writer_linux_ffi_generated.h" #endif #ifdef MOZ_PHC #include "replace_malloc_bridge.h" #endif #if defined(__ANDROID__) #include "linux/sched.h" #endif #ifndef PR_SET_PTRACER #define PR_SET_PTRACER 0x59616d61 #endif #define SKIP_SIGILL(sig) if (g_skip_sigill_ && (sig == SIGILL)) continue; namespace google_breakpad { namespace { // The list of signals which we consider to be crashes. The default action for // all these signals must be Core (see man 7 signal) because we rethrow the // signal after handling it and expect that it'll be fatal. const int kExceptionSignals[] = { SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, SIGTRAP }; const int kNumHandledSignals = sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]); struct sigaction old_handlers[kNumHandledSignals]; bool handlers_installed = false; // InstallAlternateStackLocked will store the newly installed stack in new_stack // and (if it exists) the previously installed stack in old_stack. stack_t old_stack; stack_t new_stack; bool stack_installed = false; // Create an alternative stack to run the signal handlers on. This is done since // the signal might have been caused by a stack overflow. // Runs before crashing: normal context. void InstallAlternateStackLocked() { if (stack_installed) return; memset(&old_stack, 0, sizeof(old_stack)); memset(&new_stack, 0, sizeof(new_stack)); // SIGSTKSZ may be too small to prevent the signal handlers from overrunning // the alternative stack. Ensure that the size of the alternative stack is // large enough. static const size_t kSigStackSize = std::max(size_t(16384), size_t(SIGSTKSZ)); // Only set an alternative stack if there isn't already one, or if the current // one is too small. if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp || old_stack.ss_size < kSigStackSize) { new_stack.ss_sp = calloc(1, kSigStackSize); new_stack.ss_size = kSigStackSize; if (sys_sigaltstack(&new_stack, NULL) == -1) { free(new_stack.ss_sp); return; } stack_installed = true; } } // Runs before crashing: normal context. void RestoreAlternateStackLocked() { if (!stack_installed) return; stack_t current_stack; if (sys_sigaltstack(NULL, ¤t_stack) == -1) return; // Only restore the old_stack if the current alternative stack is the one // installed by the call to InstallAlternateStackLocked. if (current_stack.ss_sp == new_stack.ss_sp) { if (old_stack.ss_sp) { if (sys_sigaltstack(&old_stack, NULL) == -1) return; } else { stack_t disable_stack; disable_stack.ss_flags = SS_DISABLE; if (sys_sigaltstack(&disable_stack, NULL) == -1) return; } } free(new_stack.ss_sp); stack_installed = false; } void InstallDefaultHandler(int sig) { #if defined(__ANDROID__) // Android L+ expose signal and sigaction symbols that override the system // ones. There is a bug in these functions where a request to set the handler // to SIG_DFL is ignored. In that case, an infinite loop is entered as the // signal is repeatedly sent to breakpad's signal handler. // To work around this, directly call the system's sigaction. struct kernel_sigaction sa; memset(&sa, 0, sizeof(sa)); sys_sigemptyset(&sa.sa_mask); sa.sa_handler_ = SIG_DFL; sa.sa_flags = SA_RESTART; sys_rt_sigaction(sig, &sa, NULL, sizeof(kernel_sigset_t)); #else signal(sig, SIG_DFL); #endif } // The global exception handler stack. This is needed because there may exist // multiple ExceptionHandler instances in a process. Each will have itself // registered in this stack. std::vector* g_handler_stack_ = NULL; pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER; // sizeof(CrashContext) can be too big w.r.t the size of alternatate stack // for SignalHandler(). Keep the crash context as a .bss field. Exception // handlers are serialized by the |g_handler_stack_mutex_| and at most one at a // time can use |g_crash_context_|. ExceptionHandler::CrashContext g_crash_context_; FirstChanceHandler g_first_chance_handler_ = nullptr; bool g_skip_sigill_ = false; } // namespace // Runs before crashing: normal context. ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor, FilterCallback filter, MinidumpCallback callback, void* callback_context, bool install_handler, const int server_fd) : filter_(filter), callback_(callback), callback_context_(callback_context), minidump_descriptor_(descriptor), crash_handler_(NULL) { g_skip_sigill_ = getenv("MOZ_DISABLE_EXCEPTION_HANDLER_SIGILL") ? true : false; if (server_fd >= 0) crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd)); if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() && !minidump_descriptor_.IsMicrodumpOnConsole()) minidump_descriptor_.UpdatePath(); #if defined(__ANDROID__) if (minidump_descriptor_.IsMicrodumpOnConsole()) logger::initializeCrashLogWriter(); #endif pthread_mutex_lock(&g_handler_stack_mutex_); // Pre-fault the crash context struct. This is to avoid failing due to OOM // if handling an exception when the process ran out of virtual memory. memset(&g_crash_context_, 0, sizeof(g_crash_context_)); if (!g_handler_stack_) g_handler_stack_ = new std::vector; if (install_handler) { InstallAlternateStackLocked(); InstallHandlersLocked(); } g_handler_stack_->push_back(this); pthread_mutex_unlock(&g_handler_stack_mutex_); } // Runs before crashing: normal context. ExceptionHandler::~ExceptionHandler() { pthread_mutex_lock(&g_handler_stack_mutex_); std::vector::iterator handler = std::find(g_handler_stack_->begin(), g_handler_stack_->end(), this); g_handler_stack_->erase(handler); if (g_handler_stack_->empty()) { delete g_handler_stack_; g_handler_stack_ = NULL; RestoreAlternateStackLocked(); RestoreHandlersLocked(); } pthread_mutex_unlock(&g_handler_stack_mutex_); } // Runs before crashing: normal context. // static bool ExceptionHandler::InstallHandlersLocked() { if (handlers_installed) return false; // Fail if unable to store all the old handlers. for (int i = 0; i < kNumHandledSignals; ++i) { SKIP_SIGILL(kExceptionSignals[i]); if (sigaction(kExceptionSignals[i], NULL, &old_handlers[i]) == -1) return false; } struct sigaction sa; memset(&sa, 0, sizeof(sa)); sigemptyset(&sa.sa_mask); // Mask all exception signals when we're handling one of them. for (int i = 0; i < kNumHandledSignals; ++i) { SKIP_SIGILL(kExceptionSignals[i]); sigaddset(&sa.sa_mask, kExceptionSignals[i]); } sa.sa_sigaction = SignalHandler; sa.sa_flags = SA_ONSTACK | SA_SIGINFO; for (int i = 0; i < kNumHandledSignals; ++i) { SKIP_SIGILL(kExceptionSignals[i]); if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) { // At this point it is impractical to back out changes, and so failure to // install a signal is intentionally ignored. } } handlers_installed = true; return true; } // This function runs in a compromised context: see the top of the file. // Runs on the crashing thread. // static void ExceptionHandler::RestoreHandlersLocked() { if (!handlers_installed) return; for (int i = 0; i < kNumHandledSignals; ++i) { SKIP_SIGILL(kExceptionSignals[i]); if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) { InstallDefaultHandler(kExceptionSignals[i]); } } handlers_installed = false; } // void ExceptionHandler::set_crash_handler(HandlerCallback callback) { // crash_handler_ = callback; // } // This function runs in a compromised context: see the top of the file. // Runs on the crashing thread. // static void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { // Give the first chance handler a chance to recover from this signal // // This is primarily used by V8. V8 uses guard regions to guarantee memory // safety in WebAssembly. This means some signals might be expected if they // originate from Wasm code while accessing the guard region. We give V8 the // chance to handle and recover from these signals first. if (g_first_chance_handler_ != nullptr && g_first_chance_handler_(sig, info, uc)) { return; } // All the exception signals are blocked at this point. pthread_mutex_lock(&g_handler_stack_mutex_); // Sometimes, Breakpad runs inside a process where some other buggy code // saves and restores signal handlers temporarily with 'signal' // instead of 'sigaction'. This loses the SA_SIGINFO flag associated // with this function. As a consequence, the values of 'info' and 'uc' // become totally bogus, generally inducing a crash. // // The following code tries to detect this case. When it does, it // resets the signal handlers with sigaction + SA_SIGINFO and returns. // This forces the signal to be thrown again, but this time the kernel // will call the function with the right arguments. struct sigaction cur_handler; if (sigaction(sig, NULL, &cur_handler) == 0 && cur_handler.sa_sigaction == SignalHandler && (cur_handler.sa_flags & SA_SIGINFO) == 0) { // Reset signal handler with the right flags. sigemptyset(&cur_handler.sa_mask); sigaddset(&cur_handler.sa_mask, sig); cur_handler.sa_sigaction = SignalHandler; cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO; if (sigaction(sig, &cur_handler, NULL) == -1) { // When resetting the handler fails, try to reset the // default one to avoid an infinite loop here. InstallDefaultHandler(sig); } pthread_mutex_unlock(&g_handler_stack_mutex_); return; } bool handled = false; for (int i = g_handler_stack_->size() - 1; !handled && i >= 0; --i) { handled = (*g_handler_stack_)[i]->HandleSignal(sig, info, uc); } // Upon returning from this signal handler, sig will become unmasked and then // it will be retriggered. If one of the ExceptionHandlers handled it // successfully, restore the default handler. Otherwise, restore the // previously installed handler. Then, when the signal is retriggered, it will // be delivered to the appropriate handler. if (handled) { InstallDefaultHandler(sig); } else { RestoreHandlersLocked(); } pthread_mutex_unlock(&g_handler_stack_mutex_); // info->si_code <= 0 iff SI_FROMUSER (SI_FROMKERNEL otherwise). if (info->si_code <= 0 || sig == SIGABRT) { // This signal was triggered by somebody sending us the signal with kill(). // In order to retrigger it, we have to queue a new signal by calling // kill() ourselves. The special case (si_pid == 0 && sig == SIGABRT) is // due to the kernel sending a SIGABRT from a user request via SysRQ. if (sys_tgkill(getpid(), syscall(__NR_gettid), sig) < 0) { // If we failed to kill ourselves (e.g. because a sandbox disallows us // to do so), we instead resort to terminating our process. This will // result in an incorrect exit code. _exit(1); } } else { // This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV). // No need to reissue the signal. It will automatically trigger again, // when we return from the signal handler. } } struct ThreadArgument { pid_t pid; // the crashing process const MinidumpDescriptor* minidump_descriptor; ExceptionHandler* handler; const void* context; // a CrashContext structure size_t context_size; }; // This is the entry function for the cloned process. We are in a compromised // context here: see the top of the file. // static int ExceptionHandler::ThreadEntry(void *arg) { const ThreadArgument *thread_arg = reinterpret_cast(arg); // Close the write end of the pipe. This allows us to fail if the parent dies // while waiting for the continue signal. sys_close(thread_arg->handler->fdes[1]); // Block here until the crashing process unblocks us when // we're allowed to use ptrace thread_arg->handler->WaitForContinueSignal(); sys_close(thread_arg->handler->fdes[0]); return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context, thread_arg->context_size) == false; } #ifdef MOZ_PHC static void GetPHCAddrInfo(siginfo_t* siginfo, mozilla::phc::AddrInfo* addr_info) { // Is this a crash involving a PHC allocation? if (siginfo->si_signo == SIGSEGV || siginfo->si_signo == SIGBUS) { ReplaceMalloc::IsPHCAllocation(siginfo->si_addr, addr_info); } } #endif // This function runs in a compromised context: see the top of the file. // Runs on the crashing thread. bool ExceptionHandler::HandleSignal(int /*sig*/, siginfo_t* info, void* uc) { mozilla::phc::AddrInfo addr_info; #ifdef MOZ_PHC GetPHCAddrInfo(info, &addr_info); #endif if (filter_ && !filter_(callback_context_)) return false; // Allow ourselves to be dumped if the signal is trusted. bool signal_trusted = info->si_code > 0; bool signal_pid_trusted = info->si_code == SI_USER || info->si_code == SI_TKILL; if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) { sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); } // Fill in all the holes in the struct to make Valgrind happy. memset(&g_crash_context_, 0, sizeof(g_crash_context_)); memcpy(&g_crash_context_.siginfo, info, sizeof(siginfo_t)); memcpy(&g_crash_context_.context, uc, sizeof(ucontext_t)); #if defined(__aarch64__) ucontext_t* uc_ptr = (ucontext_t*)uc; struct fpsimd_context* fp_ptr = (struct fpsimd_context*)&uc_ptr->uc_mcontext.__reserved; if (fp_ptr->head.magic == FPSIMD_MAGIC) { memcpy(&g_crash_context_.float_state, fp_ptr, sizeof(g_crash_context_.float_state)); } #elif !defined(__ARM_EABI__) && !defined(__mips__) // FP state is not part of user ABI on ARM Linux. // In case of MIPS Linux FP state is already part of ucontext_t // and 'float_state' is not a member of CrashContext. ucontext_t* uc_ptr = (ucontext_t*)uc; if (uc_ptr->uc_mcontext.fpregs) { memcpy(&g_crash_context_.float_state, uc_ptr->uc_mcontext.fpregs, sizeof(g_crash_context_.float_state)); } #endif g_crash_context_.tid = syscall(__NR_gettid); if (crash_handler_ != NULL) { if (crash_handler_(&g_crash_context_, sizeof(g_crash_context_), callback_context_)) { return true; } } return GenerateDump(&g_crash_context_, &addr_info); } // This is a public interface to HandleSignal that allows the client to // generate a crash dump. This function may run in a compromised context. bool ExceptionHandler::SimulateSignalDelivery(int sig) { siginfo_t siginfo = {}; // Mimic a trusted signal to allow tracing the process (see // ExceptionHandler::HandleSignal(). siginfo.si_code = SI_USER; siginfo.si_pid = getpid(); ucontext_t context; getcontext(&context); return HandleSignal(sig, &siginfo, &context); } // This function may run in a compromised context: see the top of the file. bool ExceptionHandler::GenerateDump( CrashContext *context, const mozilla::phc::AddrInfo* addr_info) { if (IsOutOfProcess()) { bool success = crash_generation_client_->RequestDump(context, sizeof(*context)); if (callback_) { success = callback_(minidump_descriptor_, callback_context_, addr_info, success); } return success; } // Allocating too much stack isn't a problem, and better to err on the side // of caution than smash it into random locations. static const unsigned kChildStackSize = 16000; PageAllocator allocator; uint8_t* stack = reinterpret_cast(allocator.Alloc(kChildStackSize)); if (!stack) return false; // clone() needs the top-most address. (scrub just to be safe) stack += kChildStackSize; my_memset(stack - 16, 0, 16); ThreadArgument thread_arg; thread_arg.handler = this; thread_arg.minidump_descriptor = &minidump_descriptor_; thread_arg.pid = getpid(); thread_arg.context = context; thread_arg.context_size = sizeof(*context); // We need to explicitly enable ptrace of parent processes on some // kernels, but we need to know the PID of the cloned process before we // can do this. Create a pipe here which we can use to block the // cloned process after creating it, until we have explicitly enabled ptrace if (sys_pipe(fdes) == -1) { // Creating the pipe failed. We'll log an error but carry on anyway, // as we'll probably still get a useful crash report. All that will happen // is the write() and read() calls will fail with EBADF static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump " "sys_pipe failed:"; logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1); logger::write(strerror(errno), strlen(strerror(errno))); logger::write("\n", 1); // Ensure fdes[0] and fdes[1] are invalid file descriptors. fdes[0] = fdes[1] = -1; } const pid_t child = sys_clone( ThreadEntry, stack, CLONE_FS | CLONE_UNTRACED, &thread_arg, NULL, NULL, NULL); if (child == -1) { sys_close(fdes[0]); sys_close(fdes[1]); return false; } if (child != 0) { static const char clonedMsg[] = "ExceptionHandler::GenerateDump cloned child "; char pidMsg[32] = {}; unsigned int pidLen = my_uint_len(child); my_uitos(pidMsg, child, pidLen); logger::write(clonedMsg, my_strlen(clonedMsg)); logger::write(pidMsg, pidLen); logger::write("\n", 1); } else { static const char childMsg[] = "ExceptionHandler::GenerateDump I'm the child\n"; logger::write(childMsg, my_strlen(childMsg)); } // Close the read end of the pipe. sys_close(fdes[0]); // Allow the child to ptrace us sys_prctl(PR_SET_PTRACER, child, 0, 0, 0); SendContinueSignalToChild(); int status = 0; const int r = HANDLE_EINTR(sys_waitpid(child, &status, __WALL)); sys_close(fdes[1]); if (r == -1) { static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:"; logger::write(msg, sizeof(msg) - 1); logger::write(strerror(errno), strlen(strerror(errno))); logger::write("\n", 1); } bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; if (callback_) success = callback_(minidump_descriptor_, callback_context_, addr_info, success); return success; } // This function runs in a compromised context: see the top of the file. void ExceptionHandler::SendContinueSignalToChild() { static const char okToContinueMessage = 'a'; int r; r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char))); if (r == -1) { static const char msg[] = "ExceptionHandler::SendContinueSignalToChild " "sys_write failed:"; logger::write(msg, sizeof(msg) - 1); logger::write(strerror(errno), strlen(strerror(errno))); logger::write("\n", 1); } const char* msg = "ExceptionHandler::SendContinueSignalToChild sent continue signal to child\n"; logger::write(msg, my_strlen(msg)); } // This function runs in a compromised context: see the top of the file. // Runs on the cloned process. void ExceptionHandler::WaitForContinueSignal() { int r; char receivedMessage; const char* waitMsg = "ExceptionHandler::WaitForContinueSignal waiting for continue signal...\n"; logger::write(waitMsg, my_strlen(waitMsg)); r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char))); if (r == -1) { static const char msg[] = "ExceptionHandler::WaitForContinueSignal " "sys_read failed:"; logger::write(msg, sizeof(msg) - 1); logger::write(strerror(errno), strlen(strerror(errno))); logger::write("\n", 1); } } // This function runs in a compromised context: see the top of the file. // Runs on the cloned process. bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, size_t context_size) { const bool may_skip_dump = minidump_descriptor_.skip_dump_if_principal_mapping_not_referenced(); const uintptr_t principal_mapping_address = minidump_descriptor_.address_within_principal_mapping(); const bool sanitize_stacks = minidump_descriptor_.sanitize_stacks(); if (minidump_descriptor_.IsMicrodumpOnConsole()) { return google_breakpad::WriteMicrodump( crashing_process, context, context_size, mapping_list_, may_skip_dump, principal_mapping_address, sanitize_stacks, *minidump_descriptor_.microdump_extra_info()); } if (minidump_descriptor_.IsFD()) { return google_breakpad::WriteMinidump(minidump_descriptor_.fd(), minidump_descriptor_.size_limit(), crashing_process, context, context_size, mapping_list_, app_memory_list_, may_skip_dump, principal_mapping_address, sanitize_stacks); } return google_breakpad::WriteMinidump(minidump_descriptor_.path(), minidump_descriptor_.size_limit(), crashing_process, context, context_size, mapping_list_, app_memory_list_, may_skip_dump, principal_mapping_address, sanitize_stacks); } // static bool ExceptionHandler::WriteMinidump(const string& dump_path, MinidumpCallback callback, void* callback_context) { MinidumpDescriptor descriptor(dump_path); ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1); return eh.WriteMinidump(); } // In order to making using EBP to calculate the desired value for ESP // a valid operation, ensure that this function is compiled with a // frame pointer using the following attribute. This attribute // is supported on GCC but not on clang. #if defined(__i386__) && defined(__GNUC__) && !defined(__clang__) __attribute__((optimize("no-omit-frame-pointer"))) #endif bool ExceptionHandler::WriteMinidump() { if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() && !minidump_descriptor_.IsMicrodumpOnConsole()) { // Update the path of the minidump so that this can be called multiple times // and new files are created for each minidump. This is done before the // generation happens, as clients may want to access the MinidumpDescriptor // after this call to find the exact path to the minidump file. minidump_descriptor_.UpdatePath(); } else if (minidump_descriptor_.IsFD()) { // Reposition the FD to its beginning and resize it to get rid of the // previous minidump info. lseek(minidump_descriptor_.fd(), 0, SEEK_SET); ignore_result(ftruncate(minidump_descriptor_.fd(), 0)); } // Allow this process to be dumped. sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); CrashContext context; int getcontext_result = getcontext(&context.context); if (getcontext_result) return false; #if defined(__i386__) // In CPUFillFromUContext in minidumpwriter.cc the stack pointer is retrieved // from REG_UESP instead of from REG_ESP. REG_UESP is the user stack pointer // and it only makes sense when running in kernel mode with a different stack // pointer. When WriteMiniDump is called during normal processing REG_UESP is // zero which leads to bad minidump files. if (!context.context.uc_mcontext.gregs[REG_UESP]) { // If REG_UESP is set to REG_ESP then that includes the stack space for the // CrashContext object in this function, which is about 128 KB. Since the // Linux dumper only records 32 KB of stack this would mean that nothing // useful would be recorded. A better option is to set REG_UESP to REG_EBP, // perhaps with a small negative offset in case there is any code that // objects to them being equal. context.context.uc_mcontext.gregs[REG_UESP] = context.context.uc_mcontext.gregs[REG_EBP] - 16; // The stack saving is based off of REG_ESP so it must be set to match the // new REG_UESP. context.context.uc_mcontext.gregs[REG_ESP] = context.context.uc_mcontext.gregs[REG_UESP]; } #endif #if !defined(__ARM_EABI__) && !defined(__aarch64__) && !defined(__mips__) // FPU state is not part of ARM EABI ucontext_t. memcpy(&context.float_state, context.context.uc_mcontext.fpregs, sizeof(context.float_state)); #endif context.tid = sys_gettid(); // Add an exception stream to the minidump for better reporting. memset(&context.siginfo, 0, sizeof(context.siginfo)); context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED; #if defined(__i386__) context.siginfo.si_addr = reinterpret_cast(context.context.uc_mcontext.gregs[REG_EIP]); #elif defined(__x86_64__) context.siginfo.si_addr = reinterpret_cast(context.context.uc_mcontext.gregs[REG_RIP]); #elif defined(__arm__) context.siginfo.si_addr = reinterpret_cast(context.context.uc_mcontext.arm_pc); #elif defined(__aarch64__) context.siginfo.si_addr = reinterpret_cast(context.context.uc_mcontext.pc); #elif defined(__mips__) context.siginfo.si_addr = reinterpret_cast(context.context.uc_mcontext.pc); #else #error "This code has not been ported to your platform yet." #endif // nullptr here for phc::AddrInfo* is ok because this is not a crash. return GenerateDump(&context, nullptr); } void ExceptionHandler::AddMappingInfo(const string& name, const wasteful_vector& identifier, uintptr_t start_address, size_t mapping_size, size_t file_offset) { MappingInfo info; info.start_addr = start_address; info.size = mapping_size; info.offset = file_offset; strncpy(info.name, name.c_str(), sizeof(info.name) - 1); info.name[sizeof(info.name) - 1] = '\0'; MappingEntry mapping; mapping.first = info; mapping.second.assign(identifier.begin(), identifier.end()); mapping_list_.push_back(mapping); } void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) { AppMemoryList::iterator iter = std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); if (iter != app_memory_list_.end()) { // Don't allow registering the same pointer twice. return; } AppMemory app_memory; app_memory.ptr = ptr; app_memory.length = length; app_memory_list_.push_back(app_memory); } void ExceptionHandler::UnregisterAppMemory(void* ptr) { AppMemoryList::iterator iter = std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); if (iter != app_memory_list_.end()) { app_memory_list_.erase(iter); } } // static bool ExceptionHandler::WriteMinidumpForChild(pid_t child, pid_t child_blamed_thread, const string& dump_path, MinidumpCallback callback, void* callback_context) { // This function is not run in a compromised context. MinidumpDescriptor descriptor(dump_path); descriptor.UpdatePath(); #if defined(MOZ_OXIDIZED_BREAKPAD) nsCString error_msg; if (!write_minidump_linux(descriptor.path(), child, child_blamed_thread, &error_msg)) return false; #else if (!google_breakpad::WriteMinidump(descriptor.path(), child, child_blamed_thread)) return false; #endif // nullptr here for phc::AddrInfo* is ok because this is not a crash. return callback ? callback(descriptor, callback_context, nullptr, true) : true; } void SetFirstChanceExceptionHandler(FirstChanceHandler callback) { g_first_chance_handler_ = callback; } } // namespace google_breakpad