|  | // 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 |