aboutsummaryrefslogtreecommitdiff
path: root/externals/breakpad/src/processor/exploitability_linux.cc
diff options
context:
space:
mode:
authorDawid Potocki <dawid@dawidpotocki.com>2024-03-05 14:09:27 +1300
committerDawid Potocki <dawid@dawidpotocki.com>2024-03-05 20:34:15 +1300
commit063e15900bda8453fb0fc6751e78d064501ccbae (patch)
treea4cd5f01dbca33a262333aff10e1e035217a30c8 /externals/breakpad/src/processor/exploitability_linux.cc
parent537296095ab24eddcb196b5ef98004f91de9c8c2 (diff)
Replace broken submodules with vendored source codeHEADpatched
Diffstat (limited to 'externals/breakpad/src/processor/exploitability_linux.cc')
-rw-r--r--externals/breakpad/src/processor/exploitability_linux.cc320
1 files changed, 320 insertions, 0 deletions
diff --git a/externals/breakpad/src/processor/exploitability_linux.cc b/externals/breakpad/src/processor/exploitability_linux.cc
new file mode 100644
index 0000000000..76e78f45bc
--- /dev/null
+++ b/externals/breakpad/src/processor/exploitability_linux.cc
@@ -0,0 +1,320 @@
+// Copyright 2013 Google LLC
+//
+// 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 LLC 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
+
+#ifdef HAVE_CONFIG_H
+#include <config.h> // Must come first
+#endif
+
+#include "processor/exploitability_linux.h"
+
+#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"
+#ifdef __linux__
+#include "processor/disassembler_objdump.h"
+#endif
+#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";
+
+} // 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) {
+#ifndef __linux__
+ BPLOG(INFO) << "MinGW does not support fork and exec. Terminating method.";
+ return false;
+#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;
+ }
+
+ DisassemblerObjdump disassembler(context->GetContextCPU(), memory_region,
+ instruction_ptr);
+ if (!disassembler.IsValid()) {
+ BPLOG(INFO) << "Disassembling fault instruction failed.";
+ return false;
+ }
+
+ // Check if the operation is a write to memory.
+ // First, the instruction must one that can write to memory.
+ auto instruction = disassembler.operation();
+ if (!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")) {
+ uint64_t write_address = 0;
+
+ // Check that the destination is a memory address. CalculateDestAddress will
+ // return false if the destination is not a memory address.
+ if (!disassembler.CalculateDestAddress(*context, write_address)) {
+ return false;
+ }
+
+ // 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;
+ } else {
+ return false;
+ }
+#endif // __linux__
+}
+
+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;
+ default:
+ return false;
+ }
+}
+
+} // namespace google_breakpad