linux: Fix locating modules with multiple mappings from offset 0

The general strategy used by Crashpad to determine loaded modules is to
read the link_map to get the addresses of the dynamic arrays for all
loaded modules. Those addresses can then be used to query the MemoryMap
to locate the module's mappings, and in particular the base mapping
from which Crashpad can parse the entire loaded ELF file.

ELF modules are typically loaded in several mappings with varying
permissions for different segments. The previous strategy used to find
the base mapping for a module was to search backwards from the mapping
for the dynamic array until a mapping from file offset 0 was found for
the same file. This fails when the file is mapped multiple times from
file offset 0, which can happen if the first page of the file contains
a GNU_RELRO segment.

This new strategy queries the MemoryMap for ALL mappings associated
with the dynamic array's mapping, mapped from offset 0. The consumer
(process_reader_linux.cc) can then determine which mapping is the
correct base by attempting to parse a module at that address and
corroborating the PT_DYNAMIC or program header table address from the
parsed module with the values Crashpad gets from the link_map or
auxiliary vector.

Bug: crashpad:30
Change-Id: Ibfcbba512e8fccc8c65afef734ea5640b71e9f70
Reviewed-on: https://chromium-review.googlesource.com/1139396
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Joshua Peraza 2018-07-26 08:27:31 -07:00 committed by Commit Bot
parent 20294e79cc
commit 52ff1accbb
11 changed files with 592 additions and 123 deletions

View File

@ -31,12 +31,14 @@ class ElfImageReader::ProgramHeaderTable {
public: public:
virtual ~ProgramHeaderTable() {} virtual ~ProgramHeaderTable() {}
virtual bool VerifyLoadSegments() const = 0; virtual bool VerifyLoadSegments(bool verbose) const = 0;
virtual size_t Size() const = 0; virtual size_t Size() const = 0;
virtual bool GetDynamicSegment(VMAddress* address, VMSize* size) const = 0; virtual bool GetDynamicSegment(VMAddress* address, VMSize* size) const = 0;
virtual bool GetPreferredElfHeaderAddress(VMAddress* address) const = 0; virtual bool GetPreferredElfHeaderAddress(VMAddress* address,
bool verbose) const = 0;
virtual bool GetPreferredLoadedMemoryRange(VMAddress* address, virtual bool GetPreferredLoadedMemoryRange(VMAddress* address,
VMSize* size) const = 0; VMSize* size,
bool verbose) const = 0;
// Locate the next PT_NOTE segment starting at segment index start_index. If a // Locate the next PT_NOTE segment starting at segment index start_index. If a
// PT_NOTE segment is found, start_index is set to the next index after the // PT_NOTE segment is found, start_index is set to the next index after the
@ -58,14 +60,15 @@ class ElfImageReader::ProgramHeaderTableSpecific
bool Initialize(const ProcessMemoryRange& memory, bool Initialize(const ProcessMemoryRange& memory,
VMAddress address, VMAddress address,
VMSize num_segments) { VMSize num_segments,
bool verbose) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_); INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
table_.resize(num_segments); table_.resize(num_segments);
if (!memory.Read(address, sizeof(PhdrType) * num_segments, table_.data())) { if (!memory.Read(address, sizeof(PhdrType) * num_segments, table_.data())) {
return false; return false;
} }
if (!VerifyLoadSegments()) { if (!VerifyLoadSegments(verbose)) {
return false; return false;
} }
@ -73,7 +76,7 @@ class ElfImageReader::ProgramHeaderTableSpecific
return true; return true;
} }
bool VerifyLoadSegments() const override { bool VerifyLoadSegments(bool verbose) const override {
constexpr bool is_64_bit = std::is_same<PhdrType, Elf64_Phdr>::value; constexpr bool is_64_bit = std::is_same<PhdrType, Elf64_Phdr>::value;
VMAddress last_vaddr; VMAddress last_vaddr;
bool load_found = false; bool load_found = false;
@ -83,12 +86,12 @@ class ElfImageReader::ProgramHeaderTableSpecific
is_64_bit, header.p_vaddr, header.p_memsz); is_64_bit, header.p_vaddr, header.p_memsz);
if (!load_range.IsValid()) { if (!load_range.IsValid()) {
LOG(ERROR) << "bad load range"; LOG_IF(ERROR, verbose) << "bad load range";
return false; return false;
} }
if (load_found && header.p_vaddr <= last_vaddr) { if (load_found && header.p_vaddr <= last_vaddr) {
LOG(ERROR) << "out of order load segments"; LOG_IF(ERROR, verbose) << "out of order load segments";
return false; return false;
} }
load_found = true; load_found = true;
@ -100,7 +103,8 @@ class ElfImageReader::ProgramHeaderTableSpecific
size_t Size() const override { return sizeof(PhdrType) * table_.size(); } size_t Size() const override { return sizeof(PhdrType) * table_.size(); }
bool GetPreferredElfHeaderAddress(VMAddress* address) const override { bool GetPreferredElfHeaderAddress(VMAddress* address,
bool verbose) const override {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
for (const auto& header : table_) { for (const auto& header : table_) {
if (header.p_type == PT_LOAD && header.p_offset == 0) { if (header.p_type == PT_LOAD && header.p_offset == 0) {
@ -108,12 +112,13 @@ class ElfImageReader::ProgramHeaderTableSpecific
return true; return true;
} }
} }
LOG(ERROR) << "no preferred header address"; LOG_IF(ERROR, verbose) << "no preferred header address";
return false; return false;
} }
bool GetPreferredLoadedMemoryRange(VMAddress* base, bool GetPreferredLoadedMemoryRange(VMAddress* base,
VMSize* size) const override { VMSize* size,
bool verbose) const override {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
VMAddress preferred_base = 0; VMAddress preferred_base = 0;
@ -133,7 +138,7 @@ class ElfImageReader::ProgramHeaderTableSpecific
*size = preferred_end - preferred_base; *size = preferred_end - preferred_base;
return true; return true;
} }
LOG(ERROR) << "no load segments"; LOG_IF(ERROR, verbose) << "no load segments";
return false; return false;
} }
@ -340,7 +345,8 @@ ElfImageReader::ElfImageReader()
ElfImageReader::~ElfImageReader() {} ElfImageReader::~ElfImageReader() {}
bool ElfImageReader::Initialize(const ProcessMemoryRange& memory, bool ElfImageReader::Initialize(const ProcessMemoryRange& memory,
VMAddress address) { VMAddress address,
bool verbose) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_); INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
ehdr_address_ = address; ehdr_address_ = address;
if (!memory_.Initialize(memory)) { if (!memory_.Initialize(memory)) {
@ -354,13 +360,13 @@ bool ElfImageReader::Initialize(const ProcessMemoryRange& memory,
if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) { e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
LOG(ERROR) << "Incorrect ELF magic number"; LOG_IF(ERROR, verbose) << "Incorrect ELF magic number";
return false; return false;
} }
if (!(memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS64) && if (!(memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS64) &&
!(!memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS32)) { !(!memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS32)) {
LOG(ERROR) << "unexpected bitness"; LOG_IF(ERROR, verbose) << "unexpected bitness";
return false; return false;
} }
@ -370,12 +376,12 @@ bool ElfImageReader::Initialize(const ProcessMemoryRange& memory,
constexpr uint8_t expected_encoding = ELFDATA2MSB; constexpr uint8_t expected_encoding = ELFDATA2MSB;
#endif #endif
if (e_ident[EI_DATA] != expected_encoding) { if (e_ident[EI_DATA] != expected_encoding) {
LOG(ERROR) << "unexpected encoding"; LOG_IF(ERROR, verbose) << "unexpected encoding";
return false; return false;
} }
if (e_ident[EI_VERSION] != EV_CURRENT) { if (e_ident[EI_VERSION] != EV_CURRENT) {
LOG(ERROR) << "unexpected version"; LOG_IF(ERROR, verbose) << "unexpected version";
return false; return false;
} }
@ -388,15 +394,15 @@ bool ElfImageReader::Initialize(const ProcessMemoryRange& memory,
#define VERIFY_HEADER(header) \ #define VERIFY_HEADER(header) \
do { \ do { \
if (header.e_type != ET_EXEC && header.e_type != ET_DYN) { \ if (header.e_type != ET_EXEC && header.e_type != ET_DYN) { \
LOG(ERROR) << "unexpected image type"; \ LOG_IF(ERROR, verbose) << "unexpected image type"; \
return false; \ return false; \
} \ } \
if (header.e_version != EV_CURRENT) { \ if (header.e_version != EV_CURRENT) { \
LOG(ERROR) << "unexpected version"; \ LOG_IF(ERROR, verbose) << "unexpected version"; \
return false; \ return false; \
} \ } \
if (header.e_ehsize != sizeof(header)) { \ if (header.e_ehsize != sizeof(header)) { \
LOG(ERROR) << "unexpected header size"; \ LOG_IF(ERROR, verbose) << "unexpected header size"; \
return false; \ return false; \
} \ } \
} while (false); } while (false);
@ -407,21 +413,21 @@ bool ElfImageReader::Initialize(const ProcessMemoryRange& memory,
VERIFY_HEADER(header_32_); VERIFY_HEADER(header_32_);
} }
if (!InitializeProgramHeaders()) { if (!InitializeProgramHeaders(verbose)) {
return false; return false;
} }
VMAddress preferred_ehdr_address; VMAddress preferred_ehdr_address;
if (!program_headers_.get()->GetPreferredElfHeaderAddress( if (!program_headers_.get()->GetPreferredElfHeaderAddress(
&preferred_ehdr_address)) { &preferred_ehdr_address, verbose)) {
return false; return false;
} }
load_bias_ = ehdr_address_ - preferred_ehdr_address; load_bias_ = ehdr_address_ - preferred_ehdr_address;
VMAddress base_address; VMAddress base_address;
VMSize loaded_size; VMSize loaded_size;
if (!program_headers_.get()->GetPreferredLoadedMemoryRange(&base_address, if (!program_headers_.get()->GetPreferredLoadedMemoryRange(
&loaded_size)) { &base_address, &loaded_size, verbose)) {
return false; return false;
} }
base_address += load_bias_; base_address += load_bias_;
@ -443,12 +449,12 @@ bool ElfImageReader::Initialize(const ProcessMemoryRange& memory,
CheckedVMAddressRange range(memory_.Is64Bit(), base_address, loaded_size); CheckedVMAddressRange range(memory_.Is64Bit(), base_address, loaded_size);
if (!range.ContainsRange( if (!range.ContainsRange(
CheckedVMAddressRange(memory_.Is64Bit(), ehdr_address_, ehdr_size))) { CheckedVMAddressRange(memory_.Is64Bit(), ehdr_address_, ehdr_size))) {
LOG(ERROR) << "ehdr out of range"; LOG_IF(ERROR, verbose) << "ehdr out of range";
return false; return false;
} }
if (!range.ContainsRange(CheckedVMAddressRange( if (!range.ContainsRange(CheckedVMAddressRange(
memory.Is64Bit(), phdr_address, program_headers_->Size()))) { memory.Is64Bit(), phdr_address, program_headers_->Size()))) {
LOG(ERROR) << "phdrs out of range"; LOG_IF(ERROR, verbose) << "phdrs out of range";
return false; return false;
} }
@ -545,19 +551,40 @@ bool ElfImageReader::GetDebugAddress(VMAddress* debug) {
return GetAddressFromDynamicArray(DT_DEBUG, true, debug); return GetAddressFromDynamicArray(DT_DEBUG, true, debug);
} }
bool ElfImageReader::InitializeProgramHeaders() { bool ElfImageReader::GetDynamicArrayAddress(VMAddress* address) {
#define INITIALIZE_PROGRAM_HEADERS(PhdrType, header) \ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
do { \ VMAddress dyn_segment_address;
if (header.e_phentsize != sizeof(PhdrType)) { \ VMSize dyn_segment_size;
LOG(ERROR) << "unexpected phdr size"; \ if (!program_headers_.get()->GetDynamicSegment(&dyn_segment_address,
return false; \ &dyn_segment_size)) {
} \ LOG(ERROR) << "no dynamic segment";
auto phdrs = new ProgramHeaderTableSpecific<PhdrType>(); \ return false;
program_headers_.reset(phdrs); \ }
if (!phdrs->Initialize( \ *address = dyn_segment_address + GetLoadBias();
memory_, ehdr_address_ + header.e_phoff, header.e_phnum)) { \ return true;
return false; \ }
} \
VMAddress ElfImageReader::GetProgramHeaderTableAddress() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return ehdr_address_ +
(memory_.Is64Bit() ? header_64_.e_phoff : header_32_.e_phoff);
}
bool ElfImageReader::InitializeProgramHeaders(bool verbose) {
#define INITIALIZE_PROGRAM_HEADERS(PhdrType, header) \
do { \
if (header.e_phentsize != sizeof(PhdrType)) { \
LOG_IF(ERROR, verbose) << "unexpected phdr size"; \
return false; \
} \
auto phdrs = new ProgramHeaderTableSpecific<PhdrType>(); \
program_headers_.reset(phdrs); \
if (!phdrs->Initialize(memory_, \
ehdr_address_ + header.e_phoff, \
header.e_phnum, \
verbose)) { \
return false; \
} \
} while (false); } while (false);
if (memory_.Is64Bit()) { if (memory_.Is64Bit()) {

View File

@ -118,7 +118,13 @@ class ElfImageReader {
//! \param[in] memory A memory reader for the remote process. //! \param[in] memory A memory reader for the remote process.
//! \param[in] address The address in the remote process' address space where //! \param[in] address The address in the remote process' address space where
//! the ELF image is loaded. //! the ELF image is loaded.
bool Initialize(const ProcessMemoryRange& memory, VMAddress address); //! \param[in] verbose `true` if this method should log error messages during
//! initialization. Setting this value to `false` will reduce the error
//! messages relating to verifying the ELF image, but may not suppress
//! logging entirely.
bool Initialize(const ProcessMemoryRange& memory,
VMAddress address,
bool verbose = true);
//! \brief Returns the base address of the image's memory range. //! \brief Returns the base address of the image's memory range.
//! //!
@ -172,6 +178,16 @@ class ElfImageReader {
//! \return `true` if the debug address was found. //! \return `true` if the debug address was found.
bool GetDebugAddress(VMAddress* debug); bool GetDebugAddress(VMAddress* debug);
//! \brief Determine the address of `PT_DYNAMIC` segment.
//!
//! \param[out] address The address of the array, valid if this method returns
//! `true`.
//! \return `true` on success. Otherwise `false` with a message logged.
bool GetDynamicArrayAddress(VMAddress* address);
//! \brief Return the address of the program header table.
VMAddress GetProgramHeaderTableAddress();
//! \brief Return a NoteReader for this image, which scans all PT_NOTE //! \brief Return a NoteReader for this image, which scans all PT_NOTE
//! segments in the image. //! segments in the image.
//! //!
@ -238,7 +254,7 @@ class ElfImageReader {
template <typename PhdrType> template <typename PhdrType>
class ProgramHeaderTableSpecific; class ProgramHeaderTableSpecific;
bool InitializeProgramHeaders(); bool InitializeProgramHeaders(bool verbose);
bool InitializeDynamicArray(); bool InitializeDynamicArray();
bool InitializeDynamicSymbolTable(); bool InitializeDynamicSymbolTable();
bool GetAddressFromDynamicArray(uint64_t tag, bool log, VMAddress* address); bool GetAddressFromDynamicArray(uint64_t tag, bool log, VMAddress* address);

View File

@ -103,10 +103,10 @@ void LocateExecutable(PtraceConnection* connection,
ASSERT_TRUE(memory_map.Initialize(connection)); ASSERT_TRUE(memory_map.Initialize(connection));
const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs); const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs);
ASSERT_TRUE(phdr_mapping); ASSERT_TRUE(phdr_mapping);
const MemoryMap::Mapping* exe_mapping = std::vector<const MemoryMap::Mapping*> possible_mappings =
memory_map.FindFileMmapStart(*phdr_mapping); memory_map.FindFilePossibleMmapStarts(*phdr_mapping);
ASSERT_TRUE(exe_mapping); ASSERT_EQ(possible_mappings.size(), 1u);
*elf_address = exe_mapping->range.Base(); *elf_address = possible_mappings[0]->range.Base();
} }
#endif // OS_FUCHSIA #endif // OS_FUCHSIA

View File

@ -74,9 +74,10 @@ void TestAgainstTarget(PtraceConnection* connection) {
const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs); const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs);
ASSERT_TRUE(phdr_mapping); ASSERT_TRUE(phdr_mapping);
const MemoryMap::Mapping* exe_mapping = std::vector<const MemoryMap::Mapping*> exe_mappings =
mappings.FindFileMmapStart(*phdr_mapping); mappings.FindFilePossibleMmapStarts(*phdr_mapping);
LinuxVMAddress elf_address = exe_mapping->range.Base(); ASSERT_EQ(exe_mappings.size(), 1u);
LinuxVMAddress elf_address = exe_mappings[0]->range.Base();
ProcessMemoryLinux memory; ProcessMemoryLinux memory;
ASSERT_TRUE(memory.Initialize(connection->GetProcessID())); ASSERT_TRUE(memory.Initialize(connection->GetProcessID()));
@ -142,9 +143,24 @@ void TestAgainstTarget(PtraceConnection* connection) {
mappings.FindMapping(module.dynamic_array); mappings.FindMapping(module.dynamic_array);
ASSERT_TRUE(dyn_mapping); ASSERT_TRUE(dyn_mapping);
const MemoryMap::Mapping* module_mapping = std::vector<const MemoryMap::Mapping*> possible_mappings =
mappings.FindFileMmapStart(*dyn_mapping); mappings.FindFilePossibleMmapStarts(*dyn_mapping);
ASSERT_TRUE(module_mapping); ASSERT_GE(possible_mappings.size(), 1u);
std::unique_ptr<ElfImageReader> module_reader;
const MemoryMap::Mapping* module_mapping = nullptr;
for (const auto mapping : possible_mappings) {
auto parsed_module = std::make_unique<ElfImageReader>();
VMAddress dynamic_address;
if (parsed_module->Initialize(range, mapping->range.Base()) &&
parsed_module->GetDynamicArrayAddress(&dynamic_address) &&
dynamic_address == module.dynamic_array) {
module_reader = std::move(parsed_module);
module_mapping = mapping;
break;
}
}
ASSERT_TRUE(module_reader.get());
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
EXPECT_FALSE(module.name.empty()); EXPECT_FALSE(module.name.empty());
@ -168,20 +184,17 @@ void TestAgainstTarget(PtraceConnection* connection) {
module.name); module.name);
#endif // OS_ANDROID #endif // OS_ANDROID
ElfImageReader module_reader;
ASSERT_TRUE(module_reader.Initialize(range, module_mapping->range.Base()));
// Android's loader stops setting its own load bias after Android 4.4.4 // Android's loader stops setting its own load bias after Android 4.4.4
// (API 20) until Android 6.0 (API 23). // (API 20) until Android 6.0 (API 23).
if (is_android_loader && android_runtime_api > 20 && if (is_android_loader && android_runtime_api > 20 &&
android_runtime_api < 23) { android_runtime_api < 23) {
EXPECT_EQ(module.load_bias, 0); EXPECT_EQ(module.load_bias, 0);
} else { } else {
EXPECT_EQ(module.load_bias, module_reader.GetLoadBias()); EXPECT_EQ(module.load_bias, module_reader->GetLoadBias());
} }
CheckedLinuxAddressRange module_range( CheckedLinuxAddressRange module_range(
connection->Is64Bit(), module_reader.Address(), module_reader.Size()); connection->Is64Bit(), module_reader->Address(), module_reader->Size());
EXPECT_TRUE(module_range.ContainsValue(module.dynamic_array)); EXPECT_TRUE(module_range.ContainsValue(module.dynamic_array));
} }
} }

View File

@ -347,20 +347,45 @@ void ProcessReaderLinux::InitializeModules() {
return; return;
} }
const MemoryMap::Mapping* exe_mapping;
if (!(exe_mapping = GetMemoryMap()->FindMapping(phdrs)) ||
!(exe_mapping = GetMemoryMap()->FindFileMmapStart(*exe_mapping))) {
return;
}
ProcessMemoryRange range; ProcessMemoryRange range;
if (!range.Initialize(Memory(), is_64_bit_)) { if (!range.Initialize(Memory(), is_64_bit_)) {
return; return;
} }
auto exe_reader = std::make_unique<ElfImageReader>(); // The strategy used for identifying loaded modules depends on ELF files
if (!exe_reader->Initialize(range, exe_mapping->range.Base())) { // conventionally loading their header and program headers into memory.
return; // Locating the correct module could fail if the headers aren't mapped, are
// mapped at an unexpected location, or if there are other mappings
// constructed to look like the ELF module being searched for.
const MemoryMap::Mapping* exe_mapping = nullptr;
std::unique_ptr<ElfImageReader> exe_reader;
{
const MemoryMap::Mapping* phdr_mapping = memory_map_.FindMapping(phdrs);
if (!phdr_mapping) {
return;
}
std::vector<const MemoryMap::Mapping*> possible_mappings =
memory_map_.FindFilePossibleMmapStarts(*phdr_mapping);
for (auto riter = possible_mappings.rbegin();
riter != possible_mappings.rend();
++riter) {
auto mapping = *riter;
auto parsed_exe = std::make_unique<ElfImageReader>();
if (parsed_exe->Initialize(
range,
mapping->range.Base(),
/* verbose= */ possible_mappings.size() == 1) &&
parsed_exe->GetProgramHeaderTableAddress() == phdrs) {
exe_mapping = mapping;
exe_reader = std::move(parsed_exe);
break;
}
}
if (!exe_mapping) {
LOG(ERROR) << "no exe mappings " << possible_mappings.size();
return;
}
} }
LinuxVMAddress debug_address; LinuxVMAddress debug_address;
@ -386,19 +411,42 @@ void ProcessReaderLinux::InitializeModules() {
aux.GetValue(AT_BASE, &loader_base); aux.GetValue(AT_BASE, &loader_base);
for (const DebugRendezvous::LinkEntry& entry : debug.Modules()) { for (const DebugRendezvous::LinkEntry& entry : debug.Modules()) {
const MemoryMap::Mapping* mapping; const MemoryMap::Mapping* module_mapping = nullptr;
if (!(mapping = memory_map_.FindMapping(entry.dynamic_array)) || std::unique_ptr<ElfImageReader> elf_reader;
!(mapping = memory_map_.FindFileMmapStart(*mapping))) { {
continue; const MemoryMap::Mapping* dyn_mapping =
} memory_map_.FindMapping(entry.dynamic_array);
if (!dyn_mapping) {
continue;
}
auto elf_reader = std::make_unique<ElfImageReader>(); std::vector<const MemoryMap::Mapping*> possible_mappings =
if (!elf_reader->Initialize(range, mapping->range.Base())) { memory_map_.FindFilePossibleMmapStarts(*dyn_mapping);
continue; for (auto riter = possible_mappings.rbegin();
riter != possible_mappings.rend();
++riter) {
auto mapping = *riter;
auto parsed_module = std::make_unique<ElfImageReader>();
VMAddress dynamic_address;
if (parsed_module->Initialize(
range,
mapping->range.Base(),
/* verbose= */ possible_mappings.size() == 1) &&
parsed_module->GetDynamicArrayAddress(&dynamic_address) &&
dynamic_address == entry.dynamic_array) {
module_mapping = mapping;
elf_reader = std::move(parsed_module);
break;
}
}
if (!module_mapping) {
LOG(ERROR) << "no module mappings " << possible_mappings.size();
continue;
}
} }
Module module = {}; Module module = {};
module.name = !entry.name.empty() ? entry.name : mapping->name; module.name = !entry.name.empty() ? entry.name : module_mapping->name;
module.elf_reader = elf_reader.get(); module.elf_reader = elf_reader.get();
module.type = loader_base && elf_reader->Address() == loader_base module.type = loader_base && elf_reader->Address() == loader_base
? ModuleSnapshot::kModuleTypeDynamicLoader ? ModuleSnapshot::kModuleTypeDynamicLoader

View File

@ -14,6 +14,8 @@
#include "snapshot/linux/process_reader_linux.h" #include "snapshot/linux/process_reader_linux.h"
#include <dlfcn.h>
#include <elf.h>
#include <errno.h> #include <errno.h>
#include <link.h> #include <link.h>
#include <pthread.h> #include <pthread.h>
@ -38,7 +40,11 @@
#include "test/linux/fake_ptrace_connection.h" #include "test/linux/fake_ptrace_connection.h"
#include "test/linux/get_tls.h" #include "test/linux/get_tls.h"
#include "test/multiprocess.h" #include "test/multiprocess.h"
#include "test/scoped_module_handle.h"
#include "test/test_paths.h"
#include "util/file/file_io.h" #include "util/file/file_io.h"
#include "util/file/file_writer.h"
#include "util/file/filesystem.h"
#include "util/linux/direct_ptrace_connection.h" #include "util/linux/direct_ptrace_connection.h"
#include "util/misc/address_sanitizer.h" #include "util/misc/address_sanitizer.h"
#include "util/misc/from_pointer_cast.h" #include "util/misc/from_pointer_cast.h"
@ -507,7 +513,220 @@ void ExpectModulesFromSelf(
#endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21 #endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21
} }
bool WriteTestModule(const base::FilePath& module_path) {
#if defined(ARCH_CPU_64_BITS)
using Ehdr = Elf64_Ehdr;
using Phdr = Elf64_Phdr;
using Shdr = Elf64_Shdr;
using Dyn = Elf64_Dyn;
using Sym = Elf64_Sym;
unsigned char elf_class = ELFCLASS64;
#else
using Ehdr = Elf32_Ehdr;
using Phdr = Elf32_Phdr;
using Shdr = Elf32_Shdr;
using Dyn = Elf32_Dyn;
using Sym = Elf32_Sym;
unsigned char elf_class = ELFCLASS32;
#endif
struct {
Ehdr ehdr;
struct {
Phdr load1;
Phdr load2;
Phdr dynamic;
} phdr_table;
struct {
Dyn hash;
Dyn strtab;
Dyn symtab;
Dyn strsz;
Dyn syment;
Dyn null;
} dynamic_array;
struct {
Elf32_Word nbucket;
Elf32_Word nchain;
Elf32_Word bucket;
Elf32_Word chain;
} hash_table;
struct {
} string_table;
struct {
Sym und_symbol;
} symbol_table;
struct {
Shdr null;
Shdr dynamic;
Shdr string_table;
} shdr_table;
} module = {};
module.ehdr.e_ident[EI_MAG0] = ELFMAG0;
module.ehdr.e_ident[EI_MAG1] = ELFMAG1;
module.ehdr.e_ident[EI_MAG2] = ELFMAG2;
module.ehdr.e_ident[EI_MAG3] = ELFMAG3;
module.ehdr.e_ident[EI_CLASS] = elf_class;
#if defined(ARCH_CPU_LITTLE_ENDIAN)
module.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
#else
module.ehdr.e_ident[EI_DATA] = ELFDATA2MSB;
#endif // ARCH_CPU_LITTLE_ENDIAN
module.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
module.ehdr.e_type = ET_DYN;
#if defined(ARCH_CPU_X86)
module.ehdr.e_machine = EM_386;
#elif defined(ARCH_CPU_X86_64)
module.ehdr.e_machine = EM_X86_64;
#elif defined(ARCH_CPU_ARMEL)
module.ehdr.e_machine = EM_ARM;
#elif defined(ARCH_CPU_ARM64)
module.ehdr.e_machine = EM_AARCH64;
#elif defined(ARCH_CPU_MIPSEL) || defined(ARCH_CPU_MIPS64EL)
module.ehdr.e_machine = EM_MIPS;
#endif
module.ehdr.e_version = EV_CURRENT;
module.ehdr.e_ehsize = sizeof(module.ehdr);
module.ehdr.e_phoff = offsetof(decltype(module), phdr_table);
module.ehdr.e_phnum = sizeof(module.phdr_table) / sizeof(Phdr);
module.ehdr.e_phentsize = sizeof(Phdr);
module.ehdr.e_shoff = offsetof(decltype(module), shdr_table);
module.ehdr.e_shentsize = sizeof(Shdr);
module.ehdr.e_shnum = sizeof(module.shdr_table) / sizeof(Shdr);
module.ehdr.e_shstrndx = SHN_UNDEF;
constexpr size_t load2_vaddr = 0x200000;
module.phdr_table.load1.p_type = PT_LOAD;
module.phdr_table.load1.p_offset = 0;
module.phdr_table.load1.p_vaddr = 0;
module.phdr_table.load1.p_filesz = sizeof(module);
module.phdr_table.load1.p_memsz = sizeof(module);
module.phdr_table.load1.p_flags = PF_R;
module.phdr_table.load1.p_align = load2_vaddr;
module.phdr_table.load2.p_type = PT_LOAD;
module.phdr_table.load2.p_offset = 0;
module.phdr_table.load2.p_vaddr = load2_vaddr;
module.phdr_table.load2.p_filesz = sizeof(module);
module.phdr_table.load2.p_memsz = sizeof(module);
module.phdr_table.load2.p_flags = PF_R | PF_W;
module.phdr_table.load2.p_align = load2_vaddr;
module.phdr_table.dynamic.p_type = PT_DYNAMIC;
module.phdr_table.dynamic.p_offset =
offsetof(decltype(module), dynamic_array);
module.phdr_table.dynamic.p_vaddr =
load2_vaddr + module.phdr_table.dynamic.p_offset;
module.phdr_table.dynamic.p_filesz = sizeof(module.dynamic_array);
module.phdr_table.dynamic.p_memsz = sizeof(module.dynamic_array);
module.phdr_table.dynamic.p_flags = PF_R | PF_W;
module.phdr_table.dynamic.p_align = 8;
module.dynamic_array.hash.d_tag = DT_HASH;
module.dynamic_array.hash.d_un.d_ptr = offsetof(decltype(module), hash_table);
module.dynamic_array.strtab.d_tag = DT_STRTAB;
module.dynamic_array.strtab.d_un.d_ptr =
offsetof(decltype(module), string_table);
module.dynamic_array.symtab.d_tag = DT_SYMTAB;
module.dynamic_array.symtab.d_un.d_ptr =
offsetof(decltype(module), symbol_table);
module.dynamic_array.strsz.d_tag = DT_STRSZ;
module.dynamic_array.strsz.d_un.d_val = sizeof(module.string_table);
module.dynamic_array.syment.d_tag = DT_SYMENT;
module.dynamic_array.syment.d_un.d_val = sizeof(Sym);
module.dynamic_array.null.d_tag = DT_NULL;
module.hash_table.nbucket = 1;
module.hash_table.nchain = 1;
module.hash_table.bucket = 0;
module.hash_table.chain = 0;
module.shdr_table.null.sh_type = SHT_NULL;
module.shdr_table.dynamic.sh_name = 0;
module.shdr_table.dynamic.sh_type = SHT_DYNAMIC;
module.shdr_table.dynamic.sh_flags = SHF_WRITE | SHF_ALLOC;
module.shdr_table.dynamic.sh_addr = module.phdr_table.dynamic.p_vaddr;
module.shdr_table.dynamic.sh_offset = module.phdr_table.dynamic.p_offset;
module.shdr_table.dynamic.sh_size = module.phdr_table.dynamic.p_filesz;
module.shdr_table.dynamic.sh_link =
offsetof(decltype(module.shdr_table), string_table) / sizeof(Shdr);
module.shdr_table.string_table.sh_name = 0;
module.shdr_table.string_table.sh_type = SHT_STRTAB;
module.shdr_table.string_table.sh_offset =
offsetof(decltype(module), string_table);
FileWriter writer;
if (!writer.Open(module_path,
FileWriteMode::kCreateOrFail,
FilePermissions::kWorldReadable)) {
ADD_FAILURE();
return false;
}
if (!writer.Write(&module, sizeof(module))) {
ADD_FAILURE();
return false;
}
return true;
}
ScopedModuleHandle LoadTestModule(const std::string& module_name) {
base::FilePath module_path(
TestPaths::Executable().DirName().Append(module_name));
if (!WriteTestModule(module_path)) {
return ScopedModuleHandle(nullptr);
}
EXPECT_TRUE(IsRegularFile(module_path));
ScopedModuleHandle handle(
dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL));
EXPECT_TRUE(handle.valid())
<< "dlopen: " << module_path.value() << " " << dlerror();
EXPECT_TRUE(LoggingRemoveFile(module_path));
return handle;
}
void ExpectTestModule(ProcessReaderLinux* reader,
const std::string& module_name) {
for (const auto& module : reader->Modules()) {
if (module.name.find(module_name) != std::string::npos) {
ASSERT_TRUE(module.elf_reader);
VMAddress dynamic_addr;
ASSERT_TRUE(module.elf_reader->GetDynamicArrayAddress(&dynamic_addr));
auto dynamic_mapping = reader->GetMemoryMap()->FindMapping(dynamic_addr);
auto mappings =
reader->GetMemoryMap()->FindFilePossibleMmapStarts(*dynamic_mapping);
EXPECT_EQ(mappings.size(), 2u);
return;
}
}
ADD_FAILURE() << "Test module not found";
}
TEST(ProcessReaderLinux, SelfModules) { TEST(ProcessReaderLinux, SelfModules) {
const std::string module_name = "test_module.so";
ScopedModuleHandle empty_test_module(LoadTestModule(module_name));
ASSERT_TRUE(empty_test_module.valid());
FakePtraceConnection connection; FakePtraceConnection connection;
connection.Initialize(getpid()); connection.Initialize(getpid());
@ -515,15 +734,19 @@ TEST(ProcessReaderLinux, SelfModules) {
ASSERT_TRUE(process_reader.Initialize(&connection)); ASSERT_TRUE(process_reader.Initialize(&connection));
ExpectModulesFromSelf(process_reader.Modules()); ExpectModulesFromSelf(process_reader.Modules());
ExpectTestModule(&process_reader, module_name);
} }
class ChildModuleTest : public Multiprocess { class ChildModuleTest : public Multiprocess {
public: public:
ChildModuleTest() : Multiprocess() {} ChildModuleTest() : Multiprocess(), module_name_("test_module.so") {}
~ChildModuleTest() = default; ~ChildModuleTest() = default;
private: private:
void MultiprocessParent() override { void MultiprocessParent() override {
char c;
ASSERT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, sizeof(c)));
DirectPtraceConnection connection; DirectPtraceConnection connection;
ASSERT_TRUE(connection.Initialize(ChildPID())); ASSERT_TRUE(connection.Initialize(ChildPID()));
@ -531,9 +754,20 @@ class ChildModuleTest : public Multiprocess {
ASSERT_TRUE(process_reader.Initialize(&connection)); ASSERT_TRUE(process_reader.Initialize(&connection));
ExpectModulesFromSelf(process_reader.Modules()); ExpectModulesFromSelf(process_reader.Modules());
ExpectTestModule(&process_reader, module_name_);
} }
void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); } void MultiprocessChild() override {
ScopedModuleHandle empty_test_module(LoadTestModule(module_name_));
ASSERT_TRUE(empty_test_module.valid());
char c;
ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &c, sizeof(c)));
CheckedReadFileAtEOF(ReadPipeHandle());
}
const std::string module_name_;
DISALLOW_COPY_AND_ASSIGN(ChildModuleTest); DISALLOW_COPY_AND_ASSIGN(ChildModuleTest);
}; };

View File

@ -36,6 +36,11 @@ void ScopedModuleHandle::Impl::Close(ModuleHandle handle) {
ScopedModuleHandle::ScopedModuleHandle(ModuleHandle handle) : handle_(handle) {} ScopedModuleHandle::ScopedModuleHandle(ModuleHandle handle) : handle_(handle) {}
ScopedModuleHandle::ScopedModuleHandle(ScopedModuleHandle&& other)
: handle_(other.handle_) {
other.handle_ = nullptr;
}
ScopedModuleHandle::~ScopedModuleHandle() { ScopedModuleHandle::~ScopedModuleHandle() {
if (valid()) { if (valid()) {
Impl::Close(handle_); Impl::Close(handle_);

View File

@ -57,6 +57,7 @@ class ScopedModuleHandle {
using ModuleHandle = Impl::ModuleHandle; using ModuleHandle = Impl::ModuleHandle;
explicit ScopedModuleHandle(ModuleHandle handle); explicit ScopedModuleHandle(ModuleHandle handle);
ScopedModuleHandle(ScopedModuleHandle&& handle);
~ScopedModuleHandle(); ~ScopedModuleHandle();
//! \return The module handle being managed. //! \return The module handle being managed.

View File

@ -296,40 +296,39 @@ const MemoryMap::Mapping* MemoryMap::FindMappingWithName(
return nullptr; return nullptr;
} }
const MemoryMap::Mapping* MemoryMap::FindFileMmapStart( std::vector<const MemoryMap::Mapping*> MemoryMap::FindFilePossibleMmapStarts(
const Mapping& mapping) const { const Mapping& mapping) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
size_t index = 0; std::vector<const Mapping*> possible_starts;
for (; index < mappings_.size(); ++index) {
if (mappings_[index].Equals(mapping)) {
break;
}
}
if (index >= mappings_.size()) {
LOG(ERROR) << "mapping not found";
return nullptr;
}
// If the mapping is anonymous, as is for the VDSO, there is no mapped file to // If the mapping is anonymous, as is for the VDSO, there is no mapped file to
// find the start of, so just return the input mapping. // find the start of, so just return the input mapping.
if (mapping.device == 0 && mapping.inode == 0) { if (mapping.device == 0 && mapping.inode == 0) {
return &mappings_[index]; for (const auto& candidate : mappings_) {
if (mapping.Equals(candidate)) {
possible_starts.push_back(&candidate);
return possible_starts;
}
}
LOG(ERROR) << "mapping not found";
return std::vector<const Mapping*>();
} }
do { for (const auto& candidate : mappings_) {
// There may by anonymous mappings or other files mapped into the holes, if (candidate.device == mapping.device &&
// so check that the mapping uses the same file as the input, but keep candidate.inode == mapping.inode &&
// searching if it doesn't. candidate.offset == 0) {
if (mappings_[index].device == mapping.device && possible_starts.push_back(&candidate);
mappings_[index].inode == mapping.inode &&
mappings_[index].offset == 0) {
return &mappings_[index];
} }
} while (index--); if (mapping.Equals(candidate)) {
return possible_starts;
}
}
LOG(ERROR) << "mapping not found"; LOG(ERROR) << "mapping not found";
return nullptr; return std::vector<const Mapping*>();
} }
} // namespace crashpad } // namespace crashpad

View File

@ -76,20 +76,27 @@ class MemoryMap {
//! it was obtained from. //! it was obtained from.
const Mapping* FindMappingWithName(const std::string& name) const; const Mapping* FindMappingWithName(const std::string& name) const;
//! \brief Find the first Mapping in a series of mappings for the same file. //! \brief Find Mappings that share a Mapping's file, mapped from offset 0.
//! //!
//! Executables and libaries are typically loaded into several mappings with //! Executables and libaries are typically loaded into several mappings with
//! varying permissions for different segments. This method searches for the //! varying permissions for different segments. Portions of an ELF file may
//! mapping with the highest address at or below \a mapping, which maps the //! be mapped multiple times as part of loading the file, for example, when
//! same file as \a mapping from file offset 0. //! initializing GNU_RELRO segments. This method searches for mappings at or
//! below \a mapping in memory that are mapped from the same file as \a
//! mapping from offset 0.
//! //!
//! If \a mapping is not found, `nullptr` is returned. If \a mapping is found //! This method is intended to help identify the possible base address for
//! but does not map a file, \a mapping is returned. //! loaded modules, but it is the caller's responsibility to determine which
//! returned mapping is correct.
//!
//! If \a mapping does not refer to a valid mapping, an empty vector will be
//! returned and a message will be logged. If \a mapping is found but does not
//! map a file, \a mapping is returned in \a possible_starts.
//! //!
//! \param[in] mapping A Mapping whose series to find the start of. //! \param[in] mapping A Mapping whose series to find the start of.
//! \return The first Mapping in the series or `nullptr` on failure with a //! \return a vector of the possible mapping starts.
//! message logged. std::vector<const Mapping*> FindFilePossibleMmapStarts(
const Mapping* FindFileMmapStart(const Mapping& mapping) const; const Mapping& mapping) const;
private: private:
std::vector<Mapping> mappings_; std::vector<Mapping> mappings_;

View File

@ -355,8 +355,8 @@ TEST(MemoryMap, MapRunningChild) {
// Expects first and third pages from mapping_start to refer to the same mapped // Expects first and third pages from mapping_start to refer to the same mapped
// file. The second page should not. // file. The second page should not.
void ExpectFindFileMmapStart(LinuxVMAddress mapping_start, void ExpectFindFilePossibleMmapStarts(LinuxVMAddress mapping_start,
LinuxVMSize page_size) { LinuxVMSize page_size) {
FakePtraceConnection connection; FakePtraceConnection connection;
ASSERT_TRUE(connection.Initialize(getpid())); ASSERT_TRUE(connection.Initialize(getpid()));
@ -373,17 +373,27 @@ void ExpectFindFileMmapStart(LinuxVMAddress mapping_start,
ASSERT_NE(mapping1, mapping2); ASSERT_NE(mapping1, mapping2);
ASSERT_NE(mapping2, mapping3); ASSERT_NE(mapping2, mapping3);
EXPECT_EQ(map.FindFileMmapStart(*mapping1), mapping1); std::vector<const MemoryMap::Mapping*> mappings;
EXPECT_EQ(map.FindFileMmapStart(*mapping2), mapping2);
EXPECT_EQ(map.FindFileMmapStart(*mapping3), mapping1); mappings = map.FindFilePossibleMmapStarts(*mapping1);
ASSERT_EQ(mappings.size(), 1u);
EXPECT_EQ(mappings[0], mapping1);
mappings = map.FindFilePossibleMmapStarts(*mapping2);
ASSERT_EQ(mappings.size(), 1u);
EXPECT_EQ(mappings[0], mapping2);
mappings = map.FindFilePossibleMmapStarts(*mapping3);
ASSERT_EQ(mappings.size(), 1u);
EXPECT_EQ(mappings[0], mapping1);
} }
TEST(MemoryMap, FindFileMmapStart) { TEST(MemoryMap, FindFilePossibleMmapStarts) {
const size_t page_size = getpagesize(); const size_t page_size = getpagesize();
ScopedTempDir temp_dir; ScopedTempDir temp_dir;
base::FilePath path = base::FilePath path = temp_dir.path().Append(
temp_dir.path().Append(FILE_PATH_LITERAL("FindFileMmapStartTestFile")); FILE_PATH_LITERAL("FindFilePossibleMmapStartsTestFile"));
ScopedFileHandle handle; ScopedFileHandle handle;
size_t file_length = page_size * 3; size_t file_length = page_size * 3;
ASSERT_NO_FATAL_FAILURE(InitializeFile(path, file_length, &handle)); ASSERT_NO_FATAL_FAILURE(InitializeFile(path, file_length, &handle));
@ -418,9 +428,19 @@ TEST(MemoryMap, FindFileMmapStart) {
ASSERT_NE(mapping1, mapping2); ASSERT_NE(mapping1, mapping2);
ASSERT_NE(mapping2, mapping3); ASSERT_NE(mapping2, mapping3);
EXPECT_EQ(map.FindFileMmapStart(*mapping1), mapping1); std::vector<const MemoryMap::Mapping*> mappings;
EXPECT_EQ(map.FindFileMmapStart(*mapping2), mapping1);
EXPECT_EQ(map.FindFileMmapStart(*mapping3), mapping1); mappings = map.FindFilePossibleMmapStarts(*mapping1);
ASSERT_EQ(mappings.size(), 1u);
EXPECT_EQ(mappings[0], mapping1);
mappings = map.FindFilePossibleMmapStarts(*mapping2);
ASSERT_EQ(mappings.size(), 1u);
EXPECT_EQ(mappings[0], mapping1);
mappings = map.FindFilePossibleMmapStarts(*mapping3);
ASSERT_EQ(mappings.size(), 1u);
EXPECT_EQ(mappings[0], mapping1);
#if defined(ARCH_CPU_64_BITS) #if defined(ARCH_CPU_64_BITS)
constexpr bool is_64_bit = true; constexpr bool is_64_bit = true;
@ -429,7 +449,7 @@ TEST(MemoryMap, FindFileMmapStart) {
#endif #endif
MemoryMap::Mapping bad_mapping; MemoryMap::Mapping bad_mapping;
bad_mapping.range.SetRange(is_64_bit, 0, 1); bad_mapping.range.SetRange(is_64_bit, 0, 1);
EXPECT_EQ(map.FindFileMmapStart(bad_mapping), nullptr); EXPECT_EQ(map.FindFilePossibleMmapStarts(bad_mapping).size(), 0u);
} }
// Make the second page an anonymous mapping // Make the second page an anonymous mapping
@ -449,12 +469,12 @@ TEST(MemoryMap, FindFileMmapStart) {
MAP_PRIVATE | MAP_FIXED, MAP_PRIVATE | MAP_FIXED,
handle.get(), handle.get(),
page_size * 2)); page_size * 2));
ExpectFindFileMmapStart(mapping_start, page_size); ExpectFindFilePossibleMmapStarts(mapping_start, page_size);
// Map the second page to another file. // Map the second page to another file.
ScopedFileHandle handle2; ScopedFileHandle handle2;
base::FilePath path2 = base::FilePath path2 = temp_dir.path().Append(
temp_dir.path().Append(FILE_PATH_LITERAL("FindFileMmapStartTestFile2")); FILE_PATH_LITERAL("FindFilePossibleMmapStartsTestFile2"));
ASSERT_NO_FATAL_FAILURE(InitializeFile(path2, page_size, &handle2)); ASSERT_NO_FATAL_FAILURE(InitializeFile(path2, page_size, &handle2));
page2_mapping.ResetMmap(file_mapping.addr_as<char*>() + page_size, page2_mapping.ResetMmap(file_mapping.addr_as<char*>() + page_size,
@ -463,7 +483,106 @@ TEST(MemoryMap, FindFileMmapStart) {
MAP_PRIVATE | MAP_FIXED, MAP_PRIVATE | MAP_FIXED,
handle2.get(), handle2.get(),
0); 0);
ExpectFindFileMmapStart(mapping_start, page_size); ExpectFindFilePossibleMmapStarts(mapping_start, page_size);
}
TEST(MemoryMap, FindFilePossibleMmapStarts_MultipleStarts) {
ScopedTempDir temp_dir;
base::FilePath path =
temp_dir.path().Append(FILE_PATH_LITERAL("MultipleStartsTestFile"));
const size_t page_size = getpagesize();
ScopedFileHandle handle;
ASSERT_NO_FATAL_FAILURE(InitializeFile(path, page_size * 2, &handle));
// Locate a sequence of pages to setup a test in.
char* seq_addr;
{
ScopedMmap whole_mapping;
ASSERT_TRUE(whole_mapping.ResetMmap(
nullptr, page_size * 8, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0));
seq_addr = whole_mapping.addr_as<char*>();
}
// Arrange file and anonymous mappings in the sequence.
ScopedMmap file_mapping0;
ASSERT_TRUE(file_mapping0.ResetMmap(seq_addr,
page_size,
PROT_READ,
MAP_PRIVATE | MAP_FIXED,
handle.get(),
page_size));
ScopedMmap file_mapping1;
ASSERT_TRUE(file_mapping1.ResetMmap(seq_addr + page_size,
page_size * 2,
PROT_READ,
MAP_PRIVATE | MAP_FIXED,
handle.get(),
0));
ScopedMmap file_mapping2;
ASSERT_TRUE(file_mapping2.ResetMmap(seq_addr + page_size * 3,
page_size,
PROT_READ,
MAP_PRIVATE | MAP_FIXED,
handle.get(),
0));
// Skip a page
ScopedMmap file_mapping3;
ASSERT_TRUE(file_mapping3.ResetMmap(seq_addr + page_size * 5,
page_size,
PROT_READ,
MAP_PRIVATE | MAP_FIXED,
handle.get(),
0));
ScopedMmap anon_mapping;
ASSERT_TRUE(anon_mapping.ResetMmap(seq_addr + page_size * 6,
page_size,
PROT_READ,
MAP_PRIVATE | MAP_ANON | MAP_FIXED,
-1,
0));
ScopedMmap file_mapping4;
ASSERT_TRUE(file_mapping4.ResetMmap(seq_addr + page_size * 7,
page_size,
PROT_READ,
MAP_PRIVATE | MAP_FIXED,
handle.get(),
0));
FakePtraceConnection connection;
ASSERT_TRUE(connection.Initialize(getpid()));
MemoryMap map;
ASSERT_TRUE(map.Initialize(&connection));
auto mapping = map.FindMapping(file_mapping0.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
auto possible_starts = map.FindFilePossibleMmapStarts(*mapping);
EXPECT_EQ(possible_starts.size(), 0u);
mapping = map.FindMapping(file_mapping1.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
possible_starts = map.FindFilePossibleMmapStarts(*mapping);
EXPECT_EQ(possible_starts.size(), 1u);
mapping = map.FindMapping(file_mapping2.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
possible_starts = map.FindFilePossibleMmapStarts(*mapping);
EXPECT_EQ(possible_starts.size(), 2u);
mapping = map.FindMapping(file_mapping3.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
possible_starts = map.FindFilePossibleMmapStarts(*mapping);
EXPECT_EQ(possible_starts.size(), 3u);
mapping = map.FindMapping(file_mapping4.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
possible_starts = map.FindFilePossibleMmapStarts(*mapping);
EXPECT_EQ(possible_starts.size(), 4u);
} }
} // namespace } // namespace