aboutsummaryrefslogtreecommitdiff
path: root/src/processor
diff options
context:
space:
mode:
authorLiu.andrew.x@gmail.com <Liu.andrew.x@gmail.com>2015-07-28 00:53:44 +0000
committerLiu.andrew.x@gmail.com <Liu.andrew.x@gmail.com>2015-07-28 00:53:44 +0000
commit2997f4590734f904190d46ee4ecf7dd4c8e5019b (patch)
treec6c27a3ae7e39b303bac1b25d46aef8c6b20962c /src/processor
parentFix incorrect comment. (diff)
downloadbreakpad-2997f4590734f904190d46ee4ecf7dd4c8e5019b.tar.xz
Add support for Linux memory mapping stream and remove ELF header usage
when checking exploitability rating. Linux minidumps do not support MD_MEMORY_INFO_LIST_STREAM, meaning the processor cannot retrieve its memory mappings. However, it has its own stream, MD_LINUX_MAPS, which contains memory mappings specific to Linux (it contains the contents of /proc/self/maps). This CL allows the minidump to gather information from the memory mappings for Linux minidumps. In addition, exploitability rating for Linux dumps now use memory mappings instead of checking the ELF headers of binaries. The basis for the change is that checking the ELF headers requires the minidumps to store the memory from the ELF headers, while the memory mapping data is already present, meaning the size of a minidump will be unchanged. As a result, of removing ELF header analysis, two unit tests have been removed. Arguably, the cases that those unit tests check do not merit a high exploitability rating and do not warrant a solid conclusion that was given earlier. R=ivanpe@chromium.org Review URL: https://codereview.chromium.org/1251593007 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1476 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/processor')
-rw-r--r--src/processor/exploitability_linux.cc160
-rw-r--r--src/processor/exploitability_linux.h51
-rw-r--r--src/processor/exploitability_unittest.cc4
-rw-r--r--src/processor/minidump.cc143
-rw-r--r--src/processor/proc_maps_linux.cc103
-rw-r--r--src/processor/proc_maps_linux_unittest.cc250
-rw-r--r--src/processor/testdata/linux_in_module_outside_executable_part.dmpbin49096 -> 0 bytes
-rw-r--r--src/processor/testdata/linux_inside_elf_header.dmpbin52616 -> 0 bytes
8 files changed, 506 insertions, 205 deletions
diff --git a/src/processor/exploitability_linux.cc b/src/processor/exploitability_linux.cc
index c1c291ce..3e99f89e 100644
--- a/src/processor/exploitability_linux.cc
+++ b/src/processor/exploitability_linux.cc
@@ -36,8 +36,6 @@
#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"
@@ -111,10 +109,6 @@ 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.";
@@ -131,151 +125,17 @@ ExploitabilityRating ExploitabilityLinux::CheckPlatformExploitability() {
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) {
- // 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;
-
- // 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, check the modules
- // to see if the instruction pointer is inside a module.
- MinidumpModuleList *minidump_module_list = dump_->GetModuleList();
- 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;
+ // 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
diff --git a/src/processor/exploitability_linux.h b/src/processor/exploitability_linux.h
index 2deb4991..6332b074 100644
--- a/src/processor/exploitability_linux.h
+++ b/src/processor/exploitability_linux.h
@@ -43,17 +43,6 @@
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,
@@ -69,46 +58,6 @@ 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 abb5b3ba..8a42baa0 100644
--- a/src/processor/exploitability_unittest.cc
+++ b/src/processor/exploitability_unittest.cc
@@ -120,13 +120,9 @@ TEST(ExploitabilityTest, TestLinuxEngine) {
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,
diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc
index 08f64652..1a997d6a 100644
--- a/src/processor/minidump.cc
+++ b/src/processor/minidump.cc
@@ -55,6 +55,7 @@
#include <iostream>
#include <limits>
#include <map>
+#include <sstream>
#include <vector>
#include "processor/range_map-inl.h"
@@ -71,6 +72,7 @@ namespace google_breakpad {
using std::istream;
using std::ifstream;
using std::numeric_limits;
+using std::stringstream;
using std::vector;
// Returns true iff |context_size| matches exactly one of the sizes of the
@@ -3972,6 +3974,142 @@ void MinidumpMemoryInfoList::Print() {
}
}
+//
+// MinidumpLinuxMaps
+//
+
+MinidumpLinuxMaps::MinidumpLinuxMaps(Minidump *minidump)
+ : MinidumpObject(minidump) {
+}
+
+void MinidumpLinuxMaps::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpLinuxMaps cannot print invalid data";
+ return;
+ }
+ std::cout << GetPathname() << std::endl;
+}
+
+//
+// MinidumpLinuxMapsList
+//
+
+MinidumpLinuxMapsList::MinidumpLinuxMapsList(Minidump *minidump)
+ : MinidumpStream(minidump),
+ maps_(NULL),
+ maps_count_(0) {
+}
+
+MinidumpLinuxMapsList::~MinidumpLinuxMapsList() {
+ for (unsigned int i = 0; i < maps_->size(); i++) {
+ delete (*maps_)[i];
+ }
+ delete maps_;
+}
+
+const MinidumpLinuxMaps *MinidumpLinuxMapsList::GetLinuxMapsForAddress(
+ uint64_t address) const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpLinuxMapsList for GetLinuxMapsForAddress";
+ return NULL;
+ }
+
+ // Search every memory mapping.
+ for (unsigned int index = 0; index < maps_count_; index++) {
+ // Check if address is within bounds of the current memory region.
+ if ((*maps_)[index]->GetBase() <= address &&
+ (*maps_)[index]->GetBase() + (*maps_)[index]->GetSize() > address) {
+ return (*maps_)[index];
+ }
+ }
+
+ // No mapping encloses the memory address.
+ BPLOG(ERROR) << "MinidumpLinuxMapsList has no mapping at "
+ << HexString(address);
+ return NULL;
+}
+
+const MinidumpLinuxMaps *MinidumpLinuxMapsList::GetLinuxMapsAtIndex(
+ unsigned int index) const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpLinuxMapsList for GetLinuxMapsAtIndex";
+ return NULL;
+ }
+
+ // Index out of bounds.
+ if (index >= maps_count_) {
+ BPLOG(ERROR) << "MinidumpLinuxMapsList index of out range: "
+ << index
+ << "/"
+ << maps_count_;
+ return NULL;
+ }
+ return (*maps_)[index];
+}
+
+bool MinidumpLinuxMapsList::Read(uint32_t expected_size) {
+ // Invalidate cached data.
+ delete maps_;
+ maps_ = NULL;
+ maps_count_ = 0;
+
+ valid_ = false;
+
+ // Load and check expected stream length.
+ uint32_t length = 0;
+ if (!minidump_->SeekToStreamType(MD_LINUX_MAPS, &length)) {
+ BPLOG(ERROR) << "MinidumpLinuxMapsList stream type not found";
+ return false;
+ }
+ if (expected_size != length) {
+ BPLOG(ERROR) << "MinidumpLinuxMapsList size mismatch: "
+ << expected_size
+ << " != "
+ << length;
+ return false;
+ }
+
+ // Create a vector to read stream data. The vector needs to have
+ // at least enough capacity to read all the data.
+ vector<char> mapping_bytes(length);
+ if (!minidump_->ReadBytes(&mapping_bytes[0], length)) {
+ BPLOG(ERROR) << "MinidumpLinuxMapsList failed to read bytes";
+ return false;
+ }
+ string map_string(mapping_bytes.begin(), mapping_bytes.end());
+ vector<MappedMemoryRegion> all_regions;
+
+ // Parse string into mapping data.
+ if (!ParseProcMaps(map_string, &all_regions)) {
+ return false;
+ }
+
+ scoped_ptr<MinidumpLinuxMappings> maps(new MinidumpLinuxMappings());
+
+ // Push mapping data into wrapper classes.
+ for (size_t i = 0; i < all_regions.size(); i++) {
+ scoped_ptr<MinidumpLinuxMaps> ele(new MinidumpLinuxMaps(minidump_));
+ ele->region_ = all_regions[i];
+ ele->valid_ = true;
+ maps->push_back(ele.release());
+ }
+
+ // Set instance variables.
+ maps_ = maps.release();
+ maps_count_ = maps_->size();
+ valid_ = true;
+ return true;
+}
+
+void MinidumpLinuxMapsList::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpLinuxMapsList cannot print valid data";
+ return;
+ }
+ for (size_t i = 0; i < maps_->size(); i++) {
+ (*maps_)[i]->Print();
+ }
+}
//
// Minidump
@@ -4292,6 +4430,11 @@ MinidumpMemoryInfoList* Minidump::GetMemoryInfoList() {
return GetStream(&memory_info_list);
}
+MinidumpLinuxMapsList *Minidump::GetLinuxMapsList() {
+ MinidumpLinuxMapsList *linux_maps_list;
+ return GetStream(&linux_maps_list);
+}
+
static const char* get_stream_name(uint32_t stream_type) {
switch (stream_type) {
case MD_UNUSED_STREAM:
diff --git a/src/processor/proc_maps_linux.cc b/src/processor/proc_maps_linux.cc
new file mode 100644
index 00000000..72947286
--- /dev/null
+++ b/src/processor/proc_maps_linux.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "google_breakpad/processor/proc_maps_linux.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+#include <inttypes.h>
+#endif
+
+#include "processor/logging.h"
+
+#if defined(OS_ANDROID) && !defined(__LP64__)
+// In 32-bit mode, Bionic's inttypes.h defines PRI/SCNxPTR as an
+// unsigned long int, which is incompatible with Bionic's stdint.h
+// defining uintptr_t as an unsigned int:
+// https://code.google.com/p/android/issues/detail?id=57218
+#undef SCNxPTR
+#define SCNxPTR "x"
+#endif
+
+namespace google_breakpad {
+
+bool ParseProcMaps(const std::string& input,
+ std::vector<MappedMemoryRegion>* regions_out) {
+ std::vector<MappedMemoryRegion> regions;
+
+ // This isn't async safe nor terribly efficient, but it doesn't need to be at
+ // this point in time.
+
+ // Split the string by newlines.
+ std::vector<std::string> lines;
+ std::string line = "";
+ for (size_t i = 0; i < input.size(); i++) {
+ if (input[i] != '\n' && input[i] != '\r') {
+ line.push_back(input[i]);
+ } else if (line.size() > 0) {
+ lines.push_back(line);
+ line.clear();
+ }
+ }
+ if (line.size() > 0) {
+ BPLOG(ERROR) << "Input doesn't end in newline";
+ return false;
+ }
+
+ for (size_t i = 0; i < lines.size(); ++i) {
+ MappedMemoryRegion region;
+ const char* line = lines[i].c_str();
+ char permissions[5] = {'\0'}; // Ensure NUL-terminated string.
+ int path_index = 0;
+
+ // Sample format from man 5 proc:
+ //
+ // address perms offset dev inode pathname
+ // 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
+ //
+ // The final %n term captures the offset in the input string, which is used
+ // to determine the path name. It *does not* increment the return value.
+ // Refer to man 3 sscanf for details.
+ if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4c %lx %hhx:%hhx %ld %n",
+ &region.start, &region.end, permissions, &region.offset,
+ &region.major_device, &region.minor_device, &region.inode,
+ &path_index) < 7) {
+ BPLOG(ERROR) << "sscanf failed for line: " << line;
+ return false;
+ }
+
+ region.permissions = 0;
+
+ if (permissions[0] == 'r')
+ region.permissions |= MappedMemoryRegion::READ;
+ else if (permissions[0] != '-')
+ return false;
+
+ if (permissions[1] == 'w')
+ region.permissions |= MappedMemoryRegion::WRITE;
+ else if (permissions[1] != '-')
+ return false;
+
+ if (permissions[2] == 'x')
+ region.permissions |= MappedMemoryRegion::EXECUTE;
+ else if (permissions[2] != '-')
+ return false;
+
+ if (permissions[3] == 'p')
+ region.permissions |= MappedMemoryRegion::PRIVATE;
+ else if (permissions[3] != 's' && permissions[3] != 'S') // Shared memory.
+ return false;
+
+ // Pushing then assigning saves us a string copy.
+ regions.push_back(region);
+ regions.back().path.assign(line + path_index);
+ }
+
+ regions_out->swap(regions);
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/src/processor/proc_maps_linux_unittest.cc b/src/processor/proc_maps_linux_unittest.cc
new file mode 100644
index 00000000..1ff4b031
--- /dev/null
+++ b/src/processor/proc_maps_linux_unittest.cc
@@ -0,0 +1,250 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "breakpad_googletest_includes.h"
+#include "google_breakpad/processor/proc_maps_linux.h"
+
+namespace {
+
+TEST(ProcMapsTest, Empty) {
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ EXPECT_TRUE(ParseProcMaps("", &regions));
+ EXPECT_EQ(0u, regions.size());
+}
+
+TEST(ProcMapsTest, NoSpaces) {
+ static const char kNoSpaces[] =
+ "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat\n";
+
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ ASSERT_TRUE(ParseProcMaps(kNoSpaces, &regions));
+ ASSERT_EQ(1u, regions.size());
+
+ EXPECT_EQ(0x00400000u, regions[0].start);
+ EXPECT_EQ(0x0040b000u, regions[0].end);
+ EXPECT_EQ(0x00002200u, regions[0].offset);
+ EXPECT_EQ("/bin/cat", regions[0].path);
+}
+
+TEST(ProcMapsTest, Spaces) {
+ static const char kSpaces[] =
+ "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/space cat\n";
+
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ ASSERT_TRUE(ParseProcMaps(kSpaces, &regions));
+ ASSERT_EQ(1u, regions.size());
+
+ EXPECT_EQ(0x00400000u, regions[0].start);
+ EXPECT_EQ(0x0040b000u, regions[0].end);
+ EXPECT_EQ(0x00002200u, regions[0].offset);
+ EXPECT_EQ("/bin/space cat", regions[0].path);
+}
+
+TEST(ProcMapsTest, NoNewline) {
+ static const char kNoSpaces[] =
+ "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat";
+
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ ASSERT_FALSE(ParseProcMaps(kNoSpaces, &regions));
+}
+
+TEST(ProcMapsTest, NoPath) {
+ static const char kNoPath[] =
+ "00400000-0040b000 rw-p 00000000 00:00 0 \n";
+
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ ASSERT_TRUE(ParseProcMaps(kNoPath, &regions));
+ ASSERT_EQ(1u, regions.size());
+
+ EXPECT_EQ(0x00400000u, regions[0].start);
+ EXPECT_EQ(0x0040b000u, regions[0].end);
+ EXPECT_EQ(0x00000000u, regions[0].offset);
+ EXPECT_EQ("", regions[0].path);
+}
+
+TEST(ProcMapsTest, Heap) {
+ static const char kHeap[] =
+ "022ac000-022cd000 rw-p 00000000 00:00 0 [heap]\n";
+
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ ASSERT_TRUE(ParseProcMaps(kHeap, &regions));
+ ASSERT_EQ(1u, regions.size());
+
+ EXPECT_EQ(0x022ac000u, regions[0].start);
+ EXPECT_EQ(0x022cd000u, regions[0].end);
+ EXPECT_EQ(0x00000000u, regions[0].offset);
+ EXPECT_EQ("[heap]", regions[0].path);
+}
+
+#if defined(ARCH_CPU_32_BITS)
+TEST(ProcMapsTest, Stack32) {
+ static const char kStack[] =
+ "beb04000-beb25000 rw-p 00000000 00:00 0 [stack]\n";
+
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ ASSERT_TRUE(ParseProcMaps(kStack, &regions));
+ ASSERT_EQ(1u, regions.size());
+
+ EXPECT_EQ(0xbeb04000u, regions[0].start);
+ EXPECT_EQ(0xbeb25000u, regions[0].end);
+ EXPECT_EQ(0x00000000u, regions[0].offset);
+ EXPECT_EQ("[stack]", regions[0].path);
+}
+#elif defined(ARCH_CPU_64_BITS)
+TEST(ProcMapsTest, Stack64) {
+ static const char kStack[] =
+ "7fff69c5b000-7fff69c7d000 rw-p 00000000 00:00 0 [stack]\n";
+
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ ASSERT_TRUE(ParseProcMaps(kStack, &regions));
+ ASSERT_EQ(1u, regions.size());
+
+ EXPECT_EQ(0x7fff69c5b000u, regions[0].start);
+ EXPECT_EQ(0x7fff69c7d000u, regions[0].end);
+ EXPECT_EQ(0x00000000u, regions[0].offset);
+ EXPECT_EQ("[stack]", regions[0].path);
+}
+#endif
+
+TEST(ProcMapsTest, Multiple) {
+ static const char kMultiple[] =
+ "00400000-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n"
+ "0060a000-0060b000 r--p 0000a000 fc:00 794418 /bin/cat\n"
+ "0060b000-0060c000 rw-p 0000b000 fc:00 794418 /bin/cat\n";
+
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ ASSERT_TRUE(ParseProcMaps(kMultiple, &regions));
+ ASSERT_EQ(3u, regions.size());
+
+ EXPECT_EQ(0x00400000u, regions[0].start);
+ EXPECT_EQ(0x0040b000u, regions[0].end);
+ EXPECT_EQ(0x00000000u, regions[0].offset);
+ EXPECT_EQ("/bin/cat", regions[0].path);
+
+ EXPECT_EQ(0x0060a000u, regions[1].start);
+ EXPECT_EQ(0x0060b000u, regions[1].end);
+ EXPECT_EQ(0x0000a000u, regions[1].offset);
+ EXPECT_EQ("/bin/cat", regions[1].path);
+
+ EXPECT_EQ(0x0060b000u, regions[2].start);
+ EXPECT_EQ(0x0060c000u, regions[2].end);
+ EXPECT_EQ(0x0000b000u, regions[2].offset);
+ EXPECT_EQ("/bin/cat", regions[2].path);
+}
+
+TEST(ProcMapsTest, Permissions) {
+ static struct {
+ const char* input;
+ uint8_t permissions;
+ } kTestCases[] = {
+ {"00400000-0040b000 ---s 00000000 fc:00 794418 /bin/cat\n", 0},
+ {"00400000-0040b000 ---S 00000000 fc:00 794418 /bin/cat\n", 0},
+ {"00400000-0040b000 r--s 00000000 fc:00 794418 /bin/cat\n",
+ google_breakpad::MappedMemoryRegion::READ},
+ {"00400000-0040b000 -w-s 00000000 fc:00 794418 /bin/cat\n",
+ google_breakpad::MappedMemoryRegion::WRITE},
+ {"00400000-0040b000 --xs 00000000 fc:00 794418 /bin/cat\n",
+ google_breakpad::MappedMemoryRegion::EXECUTE},
+ {"00400000-0040b000 rwxs 00000000 fc:00 794418 /bin/cat\n",
+ google_breakpad::MappedMemoryRegion::READ
+ | google_breakpad::MappedMemoryRegion::WRITE
+ | google_breakpad::MappedMemoryRegion::EXECUTE},
+ {"00400000-0040b000 ---p 00000000 fc:00 794418 /bin/cat\n",
+ google_breakpad::MappedMemoryRegion::PRIVATE},
+ {"00400000-0040b000 r--p 00000000 fc:00 794418 /bin/cat\n",
+ google_breakpad::MappedMemoryRegion::READ
+ | google_breakpad::MappedMemoryRegion::PRIVATE},
+ {"00400000-0040b000 -w-p 00000000 fc:00 794418 /bin/cat\n",
+ google_breakpad::MappedMemoryRegion::WRITE
+ | google_breakpad::MappedMemoryRegion::PRIVATE},
+ {"00400000-0040b000 --xp 00000000 fc:00 794418 /bin/cat\n",
+ google_breakpad::MappedMemoryRegion::EXECUTE
+ | google_breakpad::MappedMemoryRegion::PRIVATE},
+ {"00400000-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n",
+ google_breakpad::MappedMemoryRegion::READ
+ | google_breakpad::MappedMemoryRegion::WRITE
+ | google_breakpad::MappedMemoryRegion::EXECUTE
+ | google_breakpad::MappedMemoryRegion::PRIVATE},
+ };
+
+ for (size_t i = 0; i < sizeof(kTestCases) / sizeof(kTestCases[0]); ++i) {
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ EXPECT_TRUE(ParseProcMaps(kTestCases[i].input, &regions));
+ EXPECT_EQ(1u, regions.size());
+ if (regions.empty())
+ continue;
+ EXPECT_EQ(kTestCases[i].permissions, regions[0].permissions);
+ }
+}
+
+TEST(ProcMapsTest, MissingFields) {
+ static const char* kTestCases[] = {
+ "00400000\n", // Missing end + beyond.
+ "00400000-0040b000\n", // Missing perms + beyond.
+ "00400000-0040b000 r-xp\n", // Missing offset + beyond.
+ "00400000-0040b000 r-xp 00000000\n", // Missing device + beyond.
+ "00400000-0040b000 r-xp 00000000 fc:00\n", // Missing inode + beyond.
+ "00400000-0040b000 00000000 fc:00 794418 /bin/cat\n", // Missing perms.
+ "00400000-0040b000 r-xp fc:00 794418 /bin/cat\n", // Missing offset.
+ "00400000-0040b000 r-xp 00000000 fc:00 /bin/cat\n", // Missing inode.
+ "00400000 r-xp 00000000 fc:00 794418 /bin/cat\n", // Missing end.
+ "-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n", // Missing start.
+ "00400000-0040b000 r-xp 00000000 794418 /bin/cat\n", // Missing device.
+ };
+
+ for (size_t i = 0; i < sizeof(kTestCases) / sizeof(kTestCases[0]); ++i) {
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ EXPECT_FALSE(ParseProcMaps(kTestCases[i], &regions));
+ }
+}
+
+TEST(ProcMapsTest, InvalidInput) {
+ static const char* kTestCases[] = {
+ "thisisal-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n",
+ "0040000d-linvalid rwxp 00000000 fc:00 794418 /bin/cat\n",
+ "00400000-0040b000 inpu 00000000 fc:00 794418 /bin/cat\n",
+ "00400000-0040b000 rwxp tforproc fc:00 794418 /bin/cat\n",
+ "00400000-0040b000 rwxp 00000000 ma:ps 794418 /bin/cat\n",
+ "00400000-0040b000 rwxp 00000000 fc:00 parse! /bin/cat\n",
+ };
+
+ for (size_t i = 0; i < sizeof(kTestCases) / sizeof(kTestCases[0]); ++i) {
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ EXPECT_FALSE(ParseProcMaps(kTestCases[i], &regions));
+ }
+}
+
+TEST(ProcMapsTest, ParseProcMapsEmptyString) {
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ EXPECT_TRUE(ParseProcMaps("", &regions));
+ EXPECT_EQ(0ULL, regions.size());
+}
+
+// Testing a couple of remotely possible weird things in the input:
+// - Line ending with \r\n or \n\r.
+// - File name contains quotes.
+// - File name has whitespaces.
+TEST(ProcMapsTest, ParseProcMapsWeirdCorrectInput) {
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ const std::string kContents =
+ "00400000-0040b000 r-xp 00000000 fc:00 2106562 "
+ " /bin/cat\r\n"
+ "7f53b7dad000-7f53b7f62000 r-xp 00000000 fc:00 263011 "
+ " /lib/x86_64-linux-gnu/libc-2.15.so\n\r"
+ "7f53b816d000-7f53b818f000 r-xp 00000000 fc:00 264284 "
+ " /lib/x86_64-linux-gnu/ld-2.15.so\n"
+ "7fff9c7ff000-7fff9c800000 r-xp 00000000 00:00 0 "
+ " \"vd so\"\n"
+ "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 "
+ " [vsys call]\n";
+ EXPECT_TRUE(ParseProcMaps(kContents, &regions));
+ EXPECT_EQ(5ULL, regions.size());
+ EXPECT_EQ("/bin/cat", regions[0].path);
+ EXPECT_EQ("/lib/x86_64-linux-gnu/libc-2.15.so", regions[1].path);
+ EXPECT_EQ("/lib/x86_64-linux-gnu/ld-2.15.so", regions[2].path);
+ EXPECT_EQ("\"vd so\"", regions[3].path);
+ EXPECT_EQ("[vsys call]", regions[4].path);
+}
+
+} // namespace
diff --git a/src/processor/testdata/linux_in_module_outside_executable_part.dmp b/src/processor/testdata/linux_in_module_outside_executable_part.dmp
deleted file mode 100644
index 23fcc505..00000000
--- a/src/processor/testdata/linux_in_module_outside_executable_part.dmp
+++ /dev/null
Binary files differ
diff --git a/src/processor/testdata/linux_inside_elf_header.dmp b/src/processor/testdata/linux_inside_elf_header.dmp
deleted file mode 100644
index 96b6acef..00000000
--- a/src/processor/testdata/linux_inside_elf_header.dmp
+++ /dev/null
Binary files differ