aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/processor/exploitability_linux.cc153
-rw-r--r--src/processor/exploitability_linux.h52
-rw-r--r--src/processor/exploitability_unittest.cc12
-rw-r--r--src/processor/testdata/linux_in_module_outside_executable_part.dmpbin0 -> 49096 bytes
-rw-r--r--src/processor/testdata/linux_inside_elf_header.dmpbin0 -> 52616 bytes
-rw-r--r--src/processor/testdata/linux_inside_module_exe_region1.dmpbin0 -> 56256 bytes
-rw-r--r--src/processor/testdata/linux_inside_module_exe_region2.dmpbin0 -> 48600 bytes
-rw-r--r--src/processor/testdata/linux_outside_module.dmpbin0 -> 44600 bytes
-rw-r--r--src/processor/testdata/linux_raise_sigabrt.dmpbin0 -> 52424 bytes
9 files changed, 207 insertions, 10 deletions
diff --git a/src/processor/exploitability_linux.cc b/src/processor/exploitability_linux.cc
index 1b6aabf0..c1c291ce 100644
--- a/src/processor/exploitability_linux.cc
+++ b/src/processor/exploitability_linux.cc
@@ -36,6 +36,8 @@
#include "processor/exploitability_linux.h"
+#include <elf.h>
+
#include "google_breakpad/common/minidump_exception_linux.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/process_state.h"
@@ -109,8 +111,13 @@ ExploitabilityRating ExploitabilityLinux::CheckPlatformExploitability() {
return EXPLOITABILITY_ERR_PROCESSING;
}
+ if (this->ArchitectureType() == UNSUPPORTED_ARCHITECTURE) {
+ BPLOG(INFO) << "Unsupported architecture.";
+ return EXPLOITABILITY_ERR_PROCESSING;
+ }
// Getting the instruction pointer.
if (!context->GetInstructionPointer(&instruction_ptr)) {
+ BPLOG(INFO) << "Failed to retrieve instruction pointer.";
return EXPLOITABILITY_ERR_PROCESSING;
}
@@ -119,35 +126,161 @@ ExploitabilityRating ExploitabilityLinux::CheckPlatformExploitability() {
return EXPLOITABILITY_HIGH;
}
+ // There was no strong evidence suggesting exploitability, but the minidump
+ // does not appear totally benign either.
return EXPLOITABILITY_INTERESTING;
}
+LinuxArchitectureType ExploitabilityLinux::ArchitectureType() {
+ // GetContextCPU() should have already been successfully called before
+ // calling this method. Thus there should be a raw exception stream for
+ // the minidump.
+ MinidumpException *exception = dump_->GetException();
+ const DumpContext *dump_context =
+ exception ?
+ exception->GetContext() : NULL;
+ if (dump_context == NULL) {
+ BPLOG(INFO) << "No raw dump context.";
+ return UNSUPPORTED_ARCHITECTURE;
+ }
+
+ // Check the architecture type.
+ switch (dump_context->GetContextCPU()) {
+ case MD_CONTEXT_ARM:
+ case MD_CONTEXT_X86:
+ return LINUX_32_BIT;
+ case MD_CONTEXT_ARM64:
+ case MD_CONTEXT_AMD64:
+ return LINUX_64_BIT;
+ default:
+ // This should not happen. The four architectures above should be
+ // the only Linux architectures.
+ BPLOG(INFO) << "Unsupported architecture.";
+ return UNSUPPORTED_ARCHITECTURE;
+ }
+}
+
bool ExploitabilityLinux::InstructionPointerInCode(uint64_t instruction_ptr) {
- // Here we get memory mapping. Most minidumps will not contain a memory
- // mapping, so we will commonly resort to checking modules.
+ // Get memory mapping. Most minidumps will not contain a memory
+ // mapping, so processing will commonly resort to checking modules.
MinidumpMemoryInfoList *mem_info_list = dump_->GetMemoryInfoList();
const MinidumpMemoryInfo *mem_info =
mem_info_list ?
mem_info_list->GetMemoryInfoForAddress(instruction_ptr) : NULL;
- // Checking if the memory mapping at the instruction pointer is executable.
- // If there is no memory mapping, we will use the modules as reference.
+ // Check if the memory mapping at the instruction pointer is executable.
+ // If there is no memory mapping, processing will use modules as reference.
if (mem_info != NULL) {
return mem_info->IsExecutable();
}
- // If the memory mapping retrieval fails, we will check the modules
+ // If the memory mapping retrieval fails, check the modules
// to see if the instruction pointer is inside a module.
- // TODO(liuandrew): Check if the instruction pointer lies in an executable
- // region within the module.
MinidumpModuleList *minidump_module_list = dump_->GetModuleList();
- return !minidump_module_list ||
- minidump_module_list->GetModuleForAddress(instruction_ptr);
+ const MinidumpModule *minidump_module =
+ minidump_module_list ?
+ minidump_module_list->GetModuleForAddress(instruction_ptr) : NULL;
+
+ // If the instruction pointer isn't in a module, return false.
+ if (minidump_module == NULL) {
+ return false;
+ }
+
+ // Get ELF header data from the instruction pointer's module.
+ const uint64_t base_address = minidump_module->base_address();
+ MinidumpMemoryList *memory_list = dump_->GetMemoryList();
+ MinidumpMemoryRegion *memory_region =
+ memory_list ?
+ memory_list->GetMemoryRegionForAddress(base_address) : NULL;
+
+ // The minidump does not have the correct memory region.
+ // This returns true because even though there is no memory data available,
+ // the evidence so far suggests that the instruction pointer is not at a
+ // bad location.
+ if (memory_region == NULL) {
+ return true;
+ }
+
+ // Examine ELF headers. Depending on the architecture, the size of the
+ // ELF headers can differ.
+ LinuxArchitectureType architecture = this->ArchitectureType();
+ if (architecture == LINUX_32_BIT) {
+ // Check if the ELF header is within the memory region and if the
+ // instruction pointer lies within the ELF header.
+ if (memory_region->GetSize() < sizeof(Elf32_Ehdr) ||
+ instruction_ptr < base_address + sizeof(Elf32_Ehdr)) {
+ return false;
+ }
+ // Load 32-bit ELF header.
+ Elf32_Ehdr header;
+ this->LoadElfHeader(memory_region, base_address, &header);
+ // Check if the program header table is within the memory region, and
+ // validate that the program header entry size is correct.
+ if (header.e_phentsize != sizeof(Elf32_Phdr) ||
+ memory_region->GetSize() <
+ header.e_phoff +
+ ((uint64_t) header.e_phentsize * (uint64_t) header.e_phnum)) {
+ return false;
+ }
+ // Load 32-bit Program Header Table.
+ scoped_array<Elf32_Phdr> program_headers(new Elf32_Phdr[header.e_phnum]);
+ this->LoadElfHeaderTable(memory_region,
+ base_address + header.e_phoff,
+ header.e_phnum,
+ program_headers.get());
+ // Find correct program header that corresponds to the instruction pointer.
+ for (int i = 0; i < header.e_phnum; i++) {
+ const Elf32_Phdr& program_header = program_headers[i];
+ // Check if instruction pointer lies within this program header's region.
+ if (instruction_ptr >= program_header.p_vaddr &&
+ instruction_ptr < program_header.p_vaddr + program_header.p_memsz) {
+ // Return whether this program header region is executable.
+ return program_header.p_flags & PF_X;
+ }
+ }
+ } else if (architecture == LINUX_64_BIT) {
+ // Check if the ELF header is within the memory region and if the
+ // instruction pointer lies within the ELF header.
+ if (memory_region->GetSize() < sizeof(Elf64_Ehdr) ||
+ instruction_ptr < base_address + sizeof(Elf64_Ehdr)) {
+ return false;
+ }
+ // Load 64-bit ELF header.
+ Elf64_Ehdr header;
+ this->LoadElfHeader(memory_region, base_address, &header);
+ // Check if the program header table is within the memory region, and
+ // validate that the program header entry size is correct.
+ if (header.e_phentsize != sizeof(Elf64_Phdr) ||
+ memory_region->GetSize() <
+ header.e_phoff +
+ ((uint64_t) header.e_phentsize * (uint64_t) header.e_phnum)) {
+ return false;
+ }
+ // Load 64-bit Program Header Table.
+ scoped_array<Elf64_Phdr> program_headers(new Elf64_Phdr[header.e_phnum]);
+ this->LoadElfHeaderTable(memory_region,
+ base_address + header.e_phoff,
+ header.e_phnum,
+ program_headers.get());
+ // Find correct program header that corresponds to the instruction pointer.
+ for (int i = 0; i < header.e_phnum; i++) {
+ const Elf64_Phdr& program_header = program_headers[i];
+ // Check if instruction pointer lies within this program header's region.
+ if (instruction_ptr >= program_header.p_vaddr &&
+ instruction_ptr < program_header.p_vaddr + program_header.p_memsz) {
+ // Return whether this program header region is executable.
+ return program_header.p_flags & PF_X;
+ }
+ }
+ }
+
+ // The instruction pointer was not in an area identified by the ELF headers.
+ return false;
}
bool ExploitabilityLinux::BenignCrashTrigger(const MDRawExceptionStream
*raw_exception_stream) {
- // Here we check the cause of crash.
+ // 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) {
diff --git a/src/processor/exploitability_linux.h b/src/processor/exploitability_linux.h
index 42f9bc52..2deb4991 100644
--- a/src/processor/exploitability_linux.h
+++ b/src/processor/exploitability_linux.h
@@ -37,11 +37,23 @@
#ifndef GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_LINUX_H_
#define GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_LINUX_H_
+#include "common/scoped_ptr.h"
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/processor/exploitability.h"
namespace google_breakpad {
+enum LinuxArchitectureType {
+ // A 32-bit Linux architecture.
+ LINUX_32_BIT,
+
+ // A 64-bit Linux architecture.
+ LINUX_64_BIT,
+
+ // Some other architecture that is not Linux.
+ UNSUPPORTED_ARCHITECTURE
+};
+
class ExploitabilityLinux : public Exploitability {
public:
ExploitabilityLinux(Minidump *dump,
@@ -57,6 +69,46 @@ class ExploitabilityLinux : public Exploitability {
// This method checks the exception that triggered the creation of the
// minidump and reports whether the exception suggests no exploitability.
bool BenignCrashTrigger(const MDRawExceptionStream *raw_exception_stream);
+
+ // Checks if the minidump architecture is 32-bit or 64-bit.
+ LinuxArchitectureType ArchitectureType();
+
+ // Loads ELF header data of the module present in the given memory
+ // region into the scoped pointer.
+ // This method takes a scoped pointer in which the ELF header data is
+ // loaded, the memory region containing the ELF header, and the base
+ // address of the ELF header.
+ template<typename T>
+ void LoadElfHeader(MinidumpMemoryRegion *memory,
+ uint64_t base_address,
+ T *header) {
+ for (size_t i = 0; i < sizeof(T); i++) {
+ uint8_t my_byte = 0;
+ memory->GetMemoryAtAddress(base_address + i, &my_byte);
+ *(reinterpret_cast<char *>(header) + i) = my_byte;
+ }
+ }
+
+ // Loads the Program Header Table of the module present in the given
+ // memory region into the scoped array.
+ // This method takes a scoped array in which the header table data is
+ // loaded, the memory region containing the table, the base address of
+ // the program header table, and the number of entries in the table.
+ template<typename T>
+ void LoadElfHeaderTable(MinidumpMemoryRegion *memory,
+ uint64_t base_address,
+ uint16_t e_phnum,
+ T table[]) {
+ uint64_t offset = 0;
+ for (size_t i = 0; i < e_phnum; i++) {
+ T *entry = &table[i];
+ for (size_t j = 0; j < sizeof(T); j++) {
+ uint8_t my_byte = 0;
+ memory->GetMemoryAtAddress(base_address + offset++, &my_byte);
+ *(reinterpret_cast<char *>(entry) + j) = my_byte;
+ }
+ }
+ }
};
} // namespace google_breakpad
diff --git a/src/processor/exploitability_unittest.cc b/src/processor/exploitability_unittest.cc
index cc46f9ea..abb5b3ba 100644
--- a/src/processor/exploitability_unittest.cc
+++ b/src/processor/exploitability_unittest.cc
@@ -119,6 +119,18 @@ TEST(ExploitabilityTest, TestLinuxEngine) {
ExploitabilityFor("linux_null_dereference.dmp"));
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
ExploitabilityFor("linux_jmp_to_0.dmp"));
+ ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
+ ExploitabilityFor("linux_inside_elf_header.dmp"));
+ ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
+ ExploitabilityFor("linux_outside_module.dmp"));
+ ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE,
+ ExploitabilityFor("linux_raise_sigabrt.dmp"));
+ ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
+ ExploitabilityFor("linux_in_module_outside_executable_part.dmp"));
+ ASSERT_EQ(google_breakpad::EXPLOITABILITY_INTERESTING,
+ ExploitabilityFor("linux_inside_module_exe_region1.dmp"));
+ ASSERT_EQ(google_breakpad::EXPLOITABILITY_INTERESTING,
+ ExploitabilityFor("linux_inside_module_exe_region2.dmp"));
}
}
diff --git a/src/processor/testdata/linux_in_module_outside_executable_part.dmp b/src/processor/testdata/linux_in_module_outside_executable_part.dmp
new file mode 100644
index 00000000..23fcc505
--- /dev/null
+++ b/src/processor/testdata/linux_in_module_outside_executable_part.dmp
Binary files differ
diff --git a/src/processor/testdata/linux_inside_elf_header.dmp b/src/processor/testdata/linux_inside_elf_header.dmp
new file mode 100644
index 00000000..96b6acef
--- /dev/null
+++ b/src/processor/testdata/linux_inside_elf_header.dmp
Binary files differ
diff --git a/src/processor/testdata/linux_inside_module_exe_region1.dmp b/src/processor/testdata/linux_inside_module_exe_region1.dmp
new file mode 100644
index 00000000..62a74132
--- /dev/null
+++ b/src/processor/testdata/linux_inside_module_exe_region1.dmp
Binary files differ
diff --git a/src/processor/testdata/linux_inside_module_exe_region2.dmp b/src/processor/testdata/linux_inside_module_exe_region2.dmp
new file mode 100644
index 00000000..b8473e76
--- /dev/null
+++ b/src/processor/testdata/linux_inside_module_exe_region2.dmp
Binary files differ
diff --git a/src/processor/testdata/linux_outside_module.dmp b/src/processor/testdata/linux_outside_module.dmp
new file mode 100644
index 00000000..97f78bdd
--- /dev/null
+++ b/src/processor/testdata/linux_outside_module.dmp
Binary files differ
diff --git a/src/processor/testdata/linux_raise_sigabrt.dmp b/src/processor/testdata/linux_raise_sigabrt.dmp
new file mode 100644
index 00000000..7b6dde50
--- /dev/null
+++ b/src/processor/testdata/linux_raise_sigabrt.dmp
Binary files differ