diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/processor/exploitability_linux.cc | 153 | ||||
-rw-r--r-- | src/processor/exploitability_linux.h | 52 | ||||
-rw-r--r-- | src/processor/exploitability_unittest.cc | 12 | ||||
-rw-r--r-- | src/processor/testdata/linux_in_module_outside_executable_part.dmp | bin | 0 -> 49096 bytes | |||
-rw-r--r-- | src/processor/testdata/linux_inside_elf_header.dmp | bin | 0 -> 52616 bytes | |||
-rw-r--r-- | src/processor/testdata/linux_inside_module_exe_region1.dmp | bin | 0 -> 56256 bytes | |||
-rw-r--r-- | src/processor/testdata/linux_inside_module_exe_region2.dmp | bin | 0 -> 48600 bytes | |||
-rw-r--r-- | src/processor/testdata/linux_outside_module.dmp | bin | 0 -> 44600 bytes | |||
-rw-r--r-- | src/processor/testdata/linux_raise_sigabrt.dmp | bin | 0 -> 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 Binary files differnew file mode 100644 index 00000000..23fcc505 --- /dev/null +++ b/src/processor/testdata/linux_in_module_outside_executable_part.dmp diff --git a/src/processor/testdata/linux_inside_elf_header.dmp b/src/processor/testdata/linux_inside_elf_header.dmp Binary files differnew file mode 100644 index 00000000..96b6acef --- /dev/null +++ b/src/processor/testdata/linux_inside_elf_header.dmp diff --git a/src/processor/testdata/linux_inside_module_exe_region1.dmp b/src/processor/testdata/linux_inside_module_exe_region1.dmp Binary files differnew file mode 100644 index 00000000..62a74132 --- /dev/null +++ b/src/processor/testdata/linux_inside_module_exe_region1.dmp diff --git a/src/processor/testdata/linux_inside_module_exe_region2.dmp b/src/processor/testdata/linux_inside_module_exe_region2.dmp Binary files differnew file mode 100644 index 00000000..b8473e76 --- /dev/null +++ b/src/processor/testdata/linux_inside_module_exe_region2.dmp diff --git a/src/processor/testdata/linux_outside_module.dmp b/src/processor/testdata/linux_outside_module.dmp Binary files differnew file mode 100644 index 00000000..97f78bdd --- /dev/null +++ b/src/processor/testdata/linux_outside_module.dmp diff --git a/src/processor/testdata/linux_raise_sigabrt.dmp b/src/processor/testdata/linux_raise_sigabrt.dmp Binary files differnew file mode 100644 index 00000000..7b6dde50 --- /dev/null +++ b/src/processor/testdata/linux_raise_sigabrt.dmp |