| // Copyright (c) 2013 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. |
| |
| // exploitability_linux.cc: Linux specific exploitability engine. |
| // |
| // Provides a guess at the exploitability of the crash for the Linux |
| // platform given a minidump and process_state. |
| // |
| // Author: Matthew Riley |
| |
| #include "processor/exploitability_linux.h" |
| |
| #ifndef _WIN32 |
| #include <regex.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include <sstream> |
| #include <iterator> |
| #endif // _WIN32 |
| |
| #include <string.h> |
| |
| #include "google_breakpad/common/minidump_exception_linux.h" |
| #include "google_breakpad/processor/call_stack.h" |
| #include "google_breakpad/processor/process_state.h" |
| #include "google_breakpad/processor/stack_frame.h" |
| #include "processor/logging.h" |
| |
| namespace { |
| |
| // Prefixes for memory mapping names. |
| constexpr char kHeapPrefix[] = "[heap"; |
| constexpr char kStackPrefix[] = "[stack"; |
| |
| // This function in libc is called if the program was compiled with |
| // -fstack-protector and a function's stack canary changes. |
| constexpr char kStackCheckFailureFunction[] = "__stack_chk_fail"; |
| |
| // This function in libc is called if the program was compiled with |
| // -D_FORTIFY_SOURCE=2, a function like strcpy() is called, and the runtime |
| // can determine that the call would overflow the target buffer. |
| constexpr char kBoundsCheckFailureFunction[] = "__chk_fail"; |
| |
| #ifndef _WIN32 |
| const unsigned int MAX_INSTRUCTION_LEN = 15; |
| const unsigned int MAX_OBJDUMP_BUFFER_LEN = 4096; |
| #endif // _WIN32 |
| |
| } // namespace |
| |
| namespace google_breakpad { |
| |
| ExploitabilityLinux::ExploitabilityLinux(Minidump *dump, |
| ProcessState *process_state) |
| : Exploitability(dump, process_state), |
| enable_objdump_(false) { } |
| |
| ExploitabilityLinux::ExploitabilityLinux(Minidump *dump, |
| ProcessState *process_state, |
| bool enable_objdump) |
| : Exploitability(dump, process_state), |
| enable_objdump_(enable_objdump) { } |
| |
| |
| ExploitabilityRating ExploitabilityLinux::CheckPlatformExploitability() { |
| // Check the crashing thread for functions suggesting a buffer overflow or |
| // stack smash. |
| if (process_state_->requesting_thread() != -1) { |
| CallStack* crashing_thread = |
| process_state_->threads()->at(process_state_->requesting_thread()); |
| const vector<StackFrame*>& crashing_thread_frames = |
| *crashing_thread->frames(); |
| for (size_t i = 0; i < crashing_thread_frames.size(); ++i) { |
| if (crashing_thread_frames[i]->function_name == |
| kStackCheckFailureFunction) { |
| return EXPLOITABILITY_HIGH; |
| } |
| |
| if (crashing_thread_frames[i]->function_name == |
| kBoundsCheckFailureFunction) { |
| return EXPLOITABILITY_HIGH; |
| } |
| } |
| } |
| |
| // Getting exception data. (It should exist for all minidumps.) |
| MinidumpException *exception = dump_->GetException(); |
| if (exception == NULL) { |
| BPLOG(INFO) << "No exception record."; |
| return EXPLOITABILITY_ERR_PROCESSING; |
| } |
| const MDRawExceptionStream *raw_exception_stream = exception->exception(); |
| if (raw_exception_stream == NULL) { |
| BPLOG(INFO) << "No raw exception stream."; |
| return EXPLOITABILITY_ERR_PROCESSING; |
| } |
| |
| // Checking for benign exceptions that caused the crash. |
| if (this->BenignCrashTrigger(raw_exception_stream)) { |
| return EXPLOITABILITY_NONE; |
| } |
| |
| // Check if the instruction pointer is in a valid instruction region |
| // by finding if it maps to an executable part of memory. |
| uint64_t instruction_ptr = 0; |
| uint64_t stack_ptr = 0; |
| |
| const MinidumpContext *context = exception->GetContext(); |
| if (context == NULL) { |
| BPLOG(INFO) << "No exception context."; |
| return EXPLOITABILITY_ERR_PROCESSING; |
| } |
| |
| // Getting the instruction pointer. |
| if (!context->GetInstructionPointer(&instruction_ptr)) { |
| BPLOG(INFO) << "Failed to retrieve instruction pointer."; |
| return EXPLOITABILITY_ERR_PROCESSING; |
| } |
| |
| // Getting the stack pointer. |
| if (!context->GetStackPointer(&stack_ptr)) { |
| BPLOG(INFO) << "Failed to retrieve stack pointer."; |
| return EXPLOITABILITY_ERR_PROCESSING; |
| } |
| |
| // Checking for the instruction pointer in a valid instruction region, |
| // a misplaced stack pointer, and an executable stack or heap. |
| if (!this->InstructionPointerInCode(instruction_ptr) || |
| this->StackPointerOffStack(stack_ptr) || |
| this->ExecutableStackOrHeap()) { |
| return EXPLOITABILITY_HIGH; |
| } |
| |
| // Check for write to read only memory or invalid memory, shelling out |
| // to objdump is enabled. |
| if (enable_objdump_ && this->EndedOnIllegalWrite(instruction_ptr)) { |
| return EXPLOITABILITY_HIGH; |
| } |
| |
| // There was no strong evidence suggesting exploitability, but the minidump |
| // does not appear totally benign either. |
| return EXPLOITABILITY_INTERESTING; |
| } |
| |
| bool ExploitabilityLinux::EndedOnIllegalWrite(uint64_t instruction_ptr) { |
| #ifdef _WIN32 |
| BPLOG(INFO) << "MinGW does not support fork and exec. Terminating method."; |
| #else |
| // Get memory region containing instruction pointer. |
| MinidumpMemoryList *memory_list = dump_->GetMemoryList(); |
| MinidumpMemoryRegion *memory_region = |
| memory_list ? |
| memory_list->GetMemoryRegionForAddress(instruction_ptr) : NULL; |
| if (!memory_region) { |
| BPLOG(INFO) << "No memory region around instruction pointer."; |
| return false; |
| } |
| |
| // Get exception data to find architecture. |
| string architecture = ""; |
| MinidumpException *exception = dump_->GetException(); |
| // This should never evaluate to true, since this should not be reachable |
| // without checking for exception data earlier. |
| if (!exception) { |
| BPLOG(INFO) << "No exception data."; |
| return false; |
| } |
| const MDRawExceptionStream *raw_exception_stream = exception->exception(); |
| const MinidumpContext *context = exception->GetContext(); |
| // This should not evaluate to true, for the same reason mentioned above. |
| if (!raw_exception_stream || !context) { |
| BPLOG(INFO) << "No exception or architecture data."; |
| return false; |
| } |
| // Check architecture and set architecture variable to corresponding flag |
| // in objdump. |
| switch (context->GetContextCPU()) { |
| case MD_CONTEXT_X86: |
| architecture = "i386"; |
| break; |
| case MD_CONTEXT_AMD64: |
| architecture = "i386:x86-64"; |
| break; |
| default: |
| // Unsupported architecture. Note that ARM architectures are not |
| // supported because objdump does not support ARM. |
| return false; |
| break; |
| } |
| |
| // Get memory region around instruction pointer and the number of bytes |
| // before and after the instruction pointer in the memory region. |
| const uint8_t *raw_memory = memory_region->GetMemory(); |
| const uint64_t base = memory_region->GetBase(); |
| if (base > instruction_ptr) { |
| BPLOG(ERROR) << "Memory region base value exceeds instruction pointer."; |
| return false; |
| } |
| const uint64_t offset = instruction_ptr - base; |
| if (memory_region->GetSize() < MAX_INSTRUCTION_LEN + offset) { |
| BPLOG(INFO) << "Not enough bytes left to guarantee complete instruction."; |
| return false; |
| } |
| |
| // Convert bytes into objdump output. |
| char objdump_output_buffer[MAX_OBJDUMP_BUFFER_LEN] = {0}; |
| DisassembleBytes(architecture, |
| raw_memory + offset, |
| MAX_OBJDUMP_BUFFER_LEN, |
| objdump_output_buffer); |
| |
| string line; |
| if (!GetObjdumpInstructionLine(objdump_output_buffer, &line)) { |
| return false; |
| } |
| |
| // Convert objdump instruction line into the operation and operands. |
| string instruction = ""; |
| string dest = ""; |
| string src = ""; |
| TokenizeObjdumpInstruction(line, &instruction, &dest, &src); |
| |
| // Check if the operation is a write to memory. First, the instruction |
| // must one that can write to memory. Second, the write destination |
| // must be a spot in memory rather than a register. Since there are no |
| // symbols from objdump, the destination will be enclosed by brackets. |
| if (dest.size() > 2 && dest.at(0) == '[' && dest.at(dest.size() - 1) == ']' && |
| (!instruction.compare("mov") || !instruction.compare("inc") || |
| !instruction.compare("dec") || !instruction.compare("and") || |
| !instruction.compare("or") || !instruction.compare("xor") || |
| !instruction.compare("not") || !instruction.compare("neg") || |
| !instruction.compare("add") || !instruction.compare("sub") || |
| !instruction.compare("shl") || !instruction.compare("shr"))) { |
| // Strip away enclosing brackets from the destination address. |
| dest = dest.substr(1, dest.size() - 2); |
| uint64_t write_address = 0; |
| CalculateAddress(dest, *context, &write_address); |
| |
| // If the program crashed as a result of a write, the destination of |
| // the write must have been an address that did not permit writing. |
| // However, if the address is under 4k, due to program protections, |
| // the crash does not suggest exploitability for writes with such a |
| // low target address. |
| return write_address > 4096; |
| } |
| #endif // _WIN32 |
| return false; |
| } |
| |
| #ifndef _WIN32 |
| bool ExploitabilityLinux::CalculateAddress(const string &address_expression, |
| const DumpContext &context, |
| uint64_t *write_address) { |
| // The destination should be the format reg+a or reg-a, where reg |
| // is a register and a is a hexadecimal constant. Although more complex |
| // expressions can make valid instructions, objdump's disassembly outputs |
| // it in this simpler format. |
| // TODO(liuandrew): Handle more complex formats, should they arise. |
| |
| if (!write_address) { |
| BPLOG(ERROR) << "Null parameter."; |
| return false; |
| } |
| |
| // Clone parameter into a non-const string. |
| string expression = address_expression; |
| |
| // Parse out the constant that is added to the address (if it exists). |
| size_t delim = expression.find('+'); |
| bool positive_add_constant = true; |
| // Check if constant is subtracted instead of added. |
| if (delim == string::npos) { |
| positive_add_constant = false; |
| delim = expression.find('-'); |
| } |
| uint32_t add_constant = 0; |
| // Save constant and remove it from the expression. |
| if (delim != string::npos) { |
| if (!sscanf(expression.substr(delim + 1).c_str(), "%x", &add_constant)) { |
| BPLOG(ERROR) << "Failed to scan constant."; |
| return false; |
| } |
| expression = expression.substr(0, delim); |
| } |
| |
| // Set the the write address to the corresponding register. |
| // TODO(liuandrew): Add support for partial registers, such as |
| // the rax/eax/ax/ah/al chain. |
| switch (context.GetContextCPU()) { |
| case MD_CONTEXT_X86: |
| if (!expression.compare("eax")) { |
| *write_address = context.GetContextX86()->eax; |
| } else if (!expression.compare("ebx")) { |
| *write_address = context.GetContextX86()->ebx; |
| } else if (!expression.compare("ecx")) { |
| *write_address = context.GetContextX86()->ecx; |
| } else if (!expression.compare("edx")) { |
| *write_address = context.GetContextX86()->edx; |
| } else if (!expression.compare("edi")) { |
| *write_address = context.GetContextX86()->edi; |
| } else if (!expression.compare("esi")) { |
| *write_address = context.GetContextX86()->esi; |
| } else if (!expression.compare("ebp")) { |
| *write_address = context.GetContextX86()->ebp; |
| } else if (!expression.compare("esp")) { |
| *write_address = context.GetContextX86()->esp; |
| } else if (!expression.compare("eip")) { |
| *write_address = context.GetContextX86()->eip; |
| } else { |
| BPLOG(ERROR) << "Unsupported register"; |
| return false; |
| } |
| break; |
| case MD_CONTEXT_AMD64: |
| if (!expression.compare("rax")) { |
| *write_address = context.GetContextAMD64()->rax; |
| } else if (!expression.compare("rbx")) { |
| *write_address = context.GetContextAMD64()->rbx; |
| } else if (!expression.compare("rcx")) { |
| *write_address = context.GetContextAMD64()->rcx; |
| } else if (!expression.compare("rdx")) { |
| *write_address = context.GetContextAMD64()->rdx; |
| } else if (!expression.compare("rdi")) { |
| *write_address = context.GetContextAMD64()->rdi; |
| } else if (!expression.compare("rsi")) { |
| *write_address = context.GetContextAMD64()->rsi; |
| } else if (!expression.compare("rbp")) { |
| *write_address = context.GetContextAMD64()->rbp; |
| } else if (!expression.compare("rsp")) { |
| *write_address = context.GetContextAMD64()->rsp; |
| } else if (!expression.compare("rip")) { |
| *write_address = context.GetContextAMD64()->rip; |
| } else if (!expression.compare("r8")) { |
| *write_address = context.GetContextAMD64()->r8; |
| } else if (!expression.compare("r9")) { |
| *write_address = context.GetContextAMD64()->r9; |
| } else if (!expression.compare("r10")) { |
| *write_address = context.GetContextAMD64()->r10; |
| } else if (!expression.compare("r11")) { |
| *write_address = context.GetContextAMD64()->r11; |
| } else if (!expression.compare("r12")) { |
| *write_address = context.GetContextAMD64()->r12; |
| } else if (!expression.compare("r13")) { |
| *write_address = context.GetContextAMD64()->r13; |
| } else if (!expression.compare("r14")) { |
| *write_address = context.GetContextAMD64()->r14; |
| } else if (!expression.compare("r15")) { |
| *write_address = context.GetContextAMD64()->r15; |
| } else { |
| BPLOG(ERROR) << "Unsupported register"; |
| return false; |
| } |
| break; |
| default: |
| // This should not occur since the same switch condition |
| // should have terminated this method. |
| return false; |
| break; |
| } |
| |
| // Add or subtract constant from write address (if applicable). |
| *write_address = |
| positive_add_constant ? |
| *write_address + add_constant : *write_address - add_constant; |
| |
| return true; |
| } |
| |
| // static |
| bool ExploitabilityLinux::GetObjdumpInstructionLine( |
| const char *objdump_output_buffer, |
| string *instruction_line) { |
| // Put buffer data into stream to output line-by-line. |
| std::stringstream objdump_stream; |
| objdump_stream.str(string(objdump_output_buffer)); |
| |
| // Pipe each output line into the string until the string contains the first |
| // instruction from objdump. All lines before the "<.data>:" section are |
| // skipped. Loop until the line shows the first instruction or there are no |
| // lines left. |
| bool data_section_seen = false; |
| do { |
| if (!getline(objdump_stream, *instruction_line)) { |
| BPLOG(INFO) << "Objdump instructions not found"; |
| return false; |
| } |
| if (instruction_line->find("<.data>:") != string::npos) { |
| data_section_seen = true; |
| } |
| } while (!data_section_seen || instruction_line->find("0:") == string::npos); |
| // This first instruction contains the above substring. |
| |
| return true; |
| } |
| |
| bool ExploitabilityLinux::TokenizeObjdumpInstruction(const string &line, |
| string *operation, |
| string *dest, |
| string *src) { |
| if (!operation || !dest || !src) { |
| BPLOG(ERROR) << "Null parameters passed."; |
| return false; |
| } |
| |
| // Set all pointer values to empty strings. |
| *operation = ""; |
| *dest = ""; |
| *src = ""; |
| |
| // Tokenize the objdump line. |
| vector<string> tokens; |
| std::istringstream line_stream(line); |
| copy(std::istream_iterator<string>(line_stream), |
| std::istream_iterator<string>(), |
| std::back_inserter(tokens)); |
| |
| // Regex for the data in hex form. Each byte is two hex digits. |
| regex_t regex; |
| regcomp(®ex, "^[[:xdigit:]]{2}$", REG_EXTENDED | REG_NOSUB); |
| |
| // Find and set the location of the operator. The operator appears |
| // directly after the chain of bytes that define the instruction. The |
| // operands will be the last token, given that the instruction has operands. |
| // If not, the operator is the last token. The loop skips the first token |
| // because the first token is the instruction number (namely "0:"). |
| string operands = ""; |
| for (size_t i = 1; i < tokens.size(); i++) { |
| // Check if current token no longer is in byte format. |
| if (regexec(®ex, tokens[i].c_str(), 0, NULL, 0)) { |
| // instruction = tokens[i]; |
| *operation = tokens[i]; |
| // If the operator is the last token, there are no operands. |
| if (i != tokens.size() - 1) { |
| operands = tokens[tokens.size() - 1]; |
| } |
| break; |
| } |
| } |
| regfree(®ex); |
| |
| if (operation->empty()) { |
| BPLOG(ERROR) << "Failed to parse out operation from objdump instruction."; |
| return false; |
| } |
| |
| // Split operands into source and destination (if applicable). |
| if (!operands.empty()) { |
| size_t delim = operands.find(','); |
| if (delim == string::npos) { |
| *dest = operands; |
| } else { |
| *dest = operands.substr(0, delim); |
| *src = operands.substr(delim + 1); |
| } |
| } |
| return true; |
| } |
| |
| bool ExploitabilityLinux::DisassembleBytes(const string &architecture, |
| const uint8_t *raw_bytes, |
| const unsigned int buffer_len, |
| char *objdump_output_buffer) { |
| if (!raw_bytes || !objdump_output_buffer) { |
| BPLOG(ERROR) << "Bad input parameters."; |
| return false; |
| } |
| |
| // Write raw bytes around instruction pointer to a temporary file to |
| // pass as an argument to objdump. |
| char raw_bytes_tmpfile[] = "/tmp/breakpad_mem_region-raw_bytes-XXXXXX"; |
| int raw_bytes_fd = mkstemp(raw_bytes_tmpfile); |
| if (raw_bytes_fd < 0) { |
| BPLOG(ERROR) << "Failed to create tempfile."; |
| unlink(raw_bytes_tmpfile); |
| return false; |
| } |
| if (write(raw_bytes_fd, raw_bytes, MAX_INSTRUCTION_LEN) |
| != MAX_INSTRUCTION_LEN) { |
| BPLOG(ERROR) << "Writing of raw bytes failed."; |
| unlink(raw_bytes_tmpfile); |
| return false; |
| } |
| |
| char cmd[1024] = {0}; |
| snprintf(cmd, |
| 1024, |
| "objdump -D -b binary -M intel -m %s %s", |
| architecture.c_str(), |
| raw_bytes_tmpfile); |
| FILE *objdump_fp = popen(cmd, "r"); |
| if (!objdump_fp) { |
| fclose(objdump_fp); |
| unlink(raw_bytes_tmpfile); |
| BPLOG(ERROR) << "Failed to call objdump."; |
| return false; |
| } |
| if (fread(objdump_output_buffer, 1, buffer_len, objdump_fp) <= 0) { |
| fclose(objdump_fp); |
| unlink(raw_bytes_tmpfile); |
| BPLOG(ERROR) << "Failed to read objdump output."; |
| return false; |
| } |
| fclose(objdump_fp); |
| unlink(raw_bytes_tmpfile); |
| return true; |
| } |
| #endif // _WIN32 |
| |
| bool ExploitabilityLinux::StackPointerOffStack(uint64_t stack_ptr) { |
| MinidumpLinuxMapsList *linux_maps_list = dump_->GetLinuxMapsList(); |
| // Inconclusive if there are no mappings available. |
| if (!linux_maps_list) { |
| return false; |
| } |
| const MinidumpLinuxMaps *linux_maps = |
| linux_maps_list->GetLinuxMapsForAddress(stack_ptr); |
| // Checks if the stack pointer maps to a valid mapping and if the mapping |
| // is not the stack. If the mapping has no name, it is inconclusive whether |
| // it is off the stack. |
| return !linux_maps || (linux_maps->GetPathname().compare("") && |
| linux_maps->GetPathname().compare( |
| 0, strlen(kStackPrefix), kStackPrefix)); |
| } |
| |
| bool ExploitabilityLinux::ExecutableStackOrHeap() { |
| MinidumpLinuxMapsList *linux_maps_list = dump_->GetLinuxMapsList(); |
| if (linux_maps_list) { |
| for (size_t i = 0; i < linux_maps_list->get_maps_count(); i++) { |
| const MinidumpLinuxMaps *linux_maps = |
| linux_maps_list->GetLinuxMapsAtIndex(i); |
| // Check for executable stack or heap for each mapping. |
| if (linux_maps && (!linux_maps->GetPathname().compare( |
| 0, strlen(kStackPrefix), kStackPrefix) || |
| !linux_maps->GetPathname().compare( |
| 0, strlen(kHeapPrefix), kHeapPrefix)) && |
| linux_maps->IsExecutable()) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool ExploitabilityLinux::InstructionPointerInCode(uint64_t instruction_ptr) { |
| // Get Linux memory mapping from /proc/self/maps. Checking whether the |
| // region the instruction pointer is in has executable permission can tell |
| // whether it is in a valid code region. If there is no mapping for the |
| // instruction pointer, it is indicative that the instruction pointer is |
| // not within a module, which implies that it is outside a valid area. |
| MinidumpLinuxMapsList *linux_maps_list = dump_->GetLinuxMapsList(); |
| const MinidumpLinuxMaps *linux_maps = |
| linux_maps_list ? |
| linux_maps_list->GetLinuxMapsForAddress(instruction_ptr) : NULL; |
| return linux_maps ? linux_maps->IsExecutable() : false; |
| } |
| |
| bool ExploitabilityLinux::BenignCrashTrigger(const MDRawExceptionStream |
| *raw_exception_stream) { |
| // Check the cause of crash. |
| // If the exception of the crash is a benign exception, |
| // it is probably not exploitable. |
| switch (raw_exception_stream->exception_record.exception_code) { |
| case MD_EXCEPTION_CODE_LIN_SIGHUP: |
| case MD_EXCEPTION_CODE_LIN_SIGINT: |
| case MD_EXCEPTION_CODE_LIN_SIGQUIT: |
| case MD_EXCEPTION_CODE_LIN_SIGTRAP: |
| case MD_EXCEPTION_CODE_LIN_SIGABRT: |
| case MD_EXCEPTION_CODE_LIN_SIGFPE: |
| case MD_EXCEPTION_CODE_LIN_SIGKILL: |
| case MD_EXCEPTION_CODE_LIN_SIGUSR1: |
| case MD_EXCEPTION_CODE_LIN_SIGUSR2: |
| case MD_EXCEPTION_CODE_LIN_SIGPIPE: |
| case MD_EXCEPTION_CODE_LIN_SIGALRM: |
| case MD_EXCEPTION_CODE_LIN_SIGTERM: |
| case MD_EXCEPTION_CODE_LIN_SIGCHLD: |
| case MD_EXCEPTION_CODE_LIN_SIGCONT: |
| case MD_EXCEPTION_CODE_LIN_SIGSTOP: |
| case MD_EXCEPTION_CODE_LIN_SIGTSTP: |
| case MD_EXCEPTION_CODE_LIN_SIGTTIN: |
| case MD_EXCEPTION_CODE_LIN_SIGTTOU: |
| case MD_EXCEPTION_CODE_LIN_SIGURG: |
| case MD_EXCEPTION_CODE_LIN_SIGXCPU: |
| case MD_EXCEPTION_CODE_LIN_SIGXFSZ: |
| case MD_EXCEPTION_CODE_LIN_SIGVTALRM: |
| case MD_EXCEPTION_CODE_LIN_SIGPROF: |
| case MD_EXCEPTION_CODE_LIN_SIGWINCH: |
| case MD_EXCEPTION_CODE_LIN_SIGIO: |
| case MD_EXCEPTION_CODE_LIN_SIGPWR: |
| case MD_EXCEPTION_CODE_LIN_SIGSYS: |
| case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED: |
| return true; |
| break; |
| default: |
| return false; |
| break; |
| } |
| } |
| |
| } // namespace google_breakpad |