mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-27 23:41:02 +08:00
win: Add memory map range intersection helper
To be used for improved version of ReadMemory() that is memory-map aware, in particular for reading the environment block in https://codereview.chromium.org/1360863006/. R=mark@chromium.org BUG=crashpad:20, crashpad:46 Review URL: https://codereview.chromium.org/1372183002 .
This commit is contained in:
parent
7942b87fcb
commit
4df538f283
@ -104,11 +104,31 @@ class CheckedRange {
|
||||
return that.base() >= base() && that.end() <= end();
|
||||
}
|
||||
|
||||
//! \brief Returns whether the range overlaps another range.
|
||||
//!
|
||||
//! \param[in] that The (possibly) overlapping range.
|
||||
//!
|
||||
//! \return `true` if `this` range, the first range, overlaps \a that,
|
||||
//! the provided range. `false` otherwise.
|
||||
//!
|
||||
//! Ranges are considered to be closed-open [base, end) for this test. Zero
|
||||
//! length ranges are never considered to overlap another range.
|
||||
//!
|
||||
//! This method must only be called if IsValid() would return `true` for both
|
||||
//! CheckedRange objects involved.
|
||||
bool OverlapsRange(const CheckedRange<ValueType, SizeType>& that) const {
|
||||
DCHECK(IsValid());
|
||||
DCHECK(that.IsValid());
|
||||
|
||||
if (size() == 0 || that.size() == 0)
|
||||
return false;
|
||||
|
||||
return base() < that.end() && that.base() < end();
|
||||
}
|
||||
|
||||
private:
|
||||
ValueType base_;
|
||||
SizeType size_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CheckedRange);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -155,7 +155,7 @@ TEST(CheckedRange, IsValid) {
|
||||
TEST(CheckedRange, ContainsValue) {
|
||||
const struct TestData {
|
||||
uint32_t value;
|
||||
bool valid;
|
||||
bool contains;
|
||||
} kTestData[] = {
|
||||
{0, false},
|
||||
{1, false},
|
||||
@ -190,7 +190,7 @@ TEST(CheckedRange, ContainsValue) {
|
||||
SCOPED_TRACE(base::StringPrintf(
|
||||
"index %" PRIuS ", value 0x%x", index, testcase.value));
|
||||
|
||||
EXPECT_EQ(testcase.valid, parent_range.ContainsValue(testcase.value));
|
||||
EXPECT_EQ(testcase.contains, parent_range.ContainsValue(testcase.value));
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,7 +198,7 @@ TEST(CheckedRange, ContainsRange) {
|
||||
const struct TestData {
|
||||
uint32_t base;
|
||||
uint32_t size;
|
||||
bool valid;
|
||||
bool contains;
|
||||
} kTestData[] = {
|
||||
{0, 0, false},
|
||||
{0, 1, false},
|
||||
@ -242,7 +242,60 @@ TEST(CheckedRange, ContainsRange) {
|
||||
|
||||
CheckedRange<uint32_t> child_range(testcase.base, testcase.size);
|
||||
ASSERT_TRUE(child_range.IsValid());
|
||||
EXPECT_EQ(testcase.valid, parent_range.ContainsRange(child_range));
|
||||
EXPECT_EQ(testcase.contains, parent_range.ContainsRange(child_range));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CheckedRange, OverlapsRange) {
|
||||
const struct TestData {
|
||||
uint32_t base;
|
||||
uint32_t size;
|
||||
bool overlaps;
|
||||
} kTestData[] = {
|
||||
{0, 0, false},
|
||||
{0, 1, false},
|
||||
{0x2000, 0x1000, true},
|
||||
{0, 0x2000, false},
|
||||
{0x3000, 0x1000, false},
|
||||
{0x1800, 0x1000, true},
|
||||
{0x1800, 0x2000, true},
|
||||
{0x2800, 0x1000, true},
|
||||
{0x2000, 0x800, true},
|
||||
{0x2800, 0x800, true},
|
||||
{0x2400, 0x800, true},
|
||||
{0x2800, 0, false},
|
||||
{0x2000, 0xffffdfff, true},
|
||||
{0x2800, 0xffffd7ff, true},
|
||||
{0x3000, 0xffffcfff, false},
|
||||
{0xfffffffe, 1, false},
|
||||
{0xffffffff, 0, false},
|
||||
{0x1fff, 0, false},
|
||||
{0x2000, 0, false},
|
||||
{0x2001, 0, false},
|
||||
{0x2fff, 0, false},
|
||||
{0x3000, 0, false},
|
||||
{0x3001, 0, false},
|
||||
{0x1fff, 1, false},
|
||||
{0x2000, 1, true},
|
||||
{0x2001, 1, true},
|
||||
{0x2fff, 1, true},
|
||||
{0x3000, 1, false},
|
||||
{0x3001, 1, false},
|
||||
};
|
||||
|
||||
CheckedRange<uint32_t> first_range(0x2000, 0x1000);
|
||||
ASSERT_TRUE(first_range.IsValid());
|
||||
|
||||
for (size_t index = 0; index < arraysize(kTestData); ++index) {
|
||||
const TestData& testcase = kTestData[index];
|
||||
SCOPED_TRACE(base::StringPrintf("index %" PRIuS ", base 0x%x, size 0x%x",
|
||||
index,
|
||||
testcase.base,
|
||||
testcase.size));
|
||||
|
||||
CheckedRange<uint32_t> second_range(testcase.base, testcase.size);
|
||||
ASSERT_TRUE(second_range.IsValid());
|
||||
EXPECT_EQ(testcase.overlaps, first_range.OverlapsRange(second_range));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <winternl.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "base/logging.h"
|
||||
@ -106,6 +107,13 @@ bool ReadStruct(HANDLE process, WinVMAddress at, T* into) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RegionIsInaccessible(const ProcessInfo::MemoryInfo& memory_info) {
|
||||
return memory_info.state == MEM_FREE ||
|
||||
(memory_info.state == MEM_COMMIT &&
|
||||
((memory_info.protect & PAGE_NOACCESS) ||
|
||||
(memory_info.protect & PAGE_GUARD)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
template <class Traits>
|
||||
@ -421,7 +429,72 @@ bool ProcessInfo::Modules(std::vector<Module>* modules) const {
|
||||
|
||||
const std::vector<ProcessInfo::MemoryInfo>& ProcessInfo::MemoryInformation()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return memory_info_;
|
||||
}
|
||||
|
||||
std::vector<CheckedRange<WinVMAddress, WinVMSize>>
|
||||
ProcessInfo::GetReadableRanges(
|
||||
const CheckedRange<WinVMAddress, WinVMSize>& range) const {
|
||||
return GetReadableRangesOfMemoryMap(range, MemoryInformation());
|
||||
}
|
||||
|
||||
std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap(
|
||||
const CheckedRange<WinVMAddress, WinVMSize>& range,
|
||||
const std::vector<ProcessInfo::MemoryInfo>& memory_info) {
|
||||
using Range = CheckedRange<WinVMAddress, WinVMSize>;
|
||||
|
||||
// Find all the ranges that overlap the target range, maintaining their order.
|
||||
std::vector<ProcessInfo::MemoryInfo> overlapping;
|
||||
for (const auto& mi : memory_info) {
|
||||
if (range.OverlapsRange(Range(mi.base_address, mi.region_size)))
|
||||
overlapping.push_back(mi);
|
||||
}
|
||||
if (overlapping.empty())
|
||||
return std::vector<Range>();
|
||||
|
||||
// For the first and last, trim to the boundary of the incoming range.
|
||||
ProcessInfo::MemoryInfo& front = overlapping.front();
|
||||
WinVMAddress original_front_base_address = front.base_address;
|
||||
front.base_address = std::max(front.base_address, range.base());
|
||||
front.region_size =
|
||||
(original_front_base_address + front.region_size) - front.base_address;
|
||||
|
||||
ProcessInfo::MemoryInfo& back = overlapping.back();
|
||||
WinVMAddress back_end = back.base_address + back.region_size;
|
||||
back.region_size = std::min(range.end(), back_end) - back.base_address;
|
||||
|
||||
// Discard all non-accessible.
|
||||
overlapping.erase(std::remove_if(overlapping.begin(),
|
||||
overlapping.end(),
|
||||
[](const ProcessInfo::MemoryInfo& mi) {
|
||||
return RegionIsInaccessible(mi);
|
||||
}),
|
||||
overlapping.end());
|
||||
if (overlapping.empty())
|
||||
return std::vector<Range>();
|
||||
|
||||
// Convert to return type.
|
||||
std::vector<Range> as_ranges;
|
||||
for (const auto& mi : overlapping) {
|
||||
as_ranges.push_back(Range(mi.base_address, mi.region_size));
|
||||
DCHECK(as_ranges.back().IsValid());
|
||||
}
|
||||
|
||||
// Coalesce remaining regions.
|
||||
std::vector<Range> result;
|
||||
result.push_back(as_ranges[0]);
|
||||
for (size_t i = 1; i < as_ranges.size(); ++i) {
|
||||
if (result.back().end() == as_ranges[i].base()) {
|
||||
result.back().SetRange(result.back().base(),
|
||||
result.back().size() + as_ranges[i].size());
|
||||
} else {
|
||||
result.push_back(as_ranges[i]);
|
||||
}
|
||||
DCHECK(result.back().IsValid());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "util/misc/initialization_state_dcheck.h"
|
||||
#include "util/numeric/checked_range.h"
|
||||
#include "util/win/address_types.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -129,6 +130,16 @@ class ProcessInfo {
|
||||
//! \brief Retrieves information about all pages mapped into the process.
|
||||
const std::vector<MemoryInfo>& MemoryInformation() const;
|
||||
|
||||
//! \brief Given a range to be read from the target process, returns a vector
|
||||
//! of ranges, representing the readable portions of the original range.
|
||||
//!
|
||||
//! \param[in] range The range being identified.
|
||||
//!
|
||||
//! \return A vector of ranges corresponding to the portion of \a range that
|
||||
//! is readable based on the memory map.
|
||||
std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRanges(
|
||||
const CheckedRange<WinVMAddress, WinVMSize>& range) const;
|
||||
|
||||
private:
|
||||
template <class Traits>
|
||||
friend bool GetProcessBasicInformation(HANDLE process,
|
||||
@ -159,6 +170,16 @@ class ProcessInfo {
|
||||
DISALLOW_COPY_AND_ASSIGN(ProcessInfo);
|
||||
};
|
||||
|
||||
//! \brief Given a memory map of a process, and a range to be read from the
|
||||
//! target process, returns a vector of ranges, representing the readable
|
||||
//! portions of the original range.
|
||||
//!
|
||||
//! This is a free function for testing, but prefer
|
||||
//! ProcessInfo::GetReadableRanges().
|
||||
std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap(
|
||||
const CheckedRange<WinVMAddress, WinVMSize>& range,
|
||||
const std::vector<ProcessInfo::MemoryInfo>& memory_info);
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_WIN_PROCESS_INFO_H_
|
||||
|
@ -199,6 +199,180 @@ TEST(ProcessInfo, OtherProcessWOW64) {
|
||||
}
|
||||
#endif // ARCH_CPU_64_BITS
|
||||
|
||||
TEST(ProcessInfo, AccessibleRangesNone) {
|
||||
std::vector<ProcessInfo::MemoryInfo> memory_info;
|
||||
MEMORY_BASIC_INFORMATION mbi = {0};
|
||||
|
||||
mbi.BaseAddress = 0;
|
||||
mbi.RegionSize = 10;
|
||||
mbi.State = MEM_FREE;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
||||
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(2, 4),
|
||||
memory_info);
|
||||
|
||||
EXPECT_TRUE(result.empty());
|
||||
}
|
||||
|
||||
TEST(ProcessInfo, AccessibleRangesOneInside) {
|
||||
std::vector<ProcessInfo::MemoryInfo> memory_info;
|
||||
MEMORY_BASIC_INFORMATION mbi = {0};
|
||||
|
||||
mbi.BaseAddress = 0;
|
||||
mbi.RegionSize = 10;
|
||||
mbi.State = MEM_COMMIT;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
||||
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(2, 4),
|
||||
memory_info);
|
||||
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(2, result[0].base());
|
||||
EXPECT_EQ(4, result[0].size());
|
||||
}
|
||||
|
||||
TEST(ProcessInfo, AccessibleRangesOneTruncatedSize) {
|
||||
std::vector<ProcessInfo::MemoryInfo> memory_info;
|
||||
MEMORY_BASIC_INFORMATION mbi = {0};
|
||||
|
||||
mbi.BaseAddress = 0;
|
||||
mbi.RegionSize = 10;
|
||||
mbi.State = MEM_COMMIT;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
mbi.BaseAddress = reinterpret_cast<void*>(10);
|
||||
mbi.RegionSize = 20;
|
||||
mbi.State = MEM_FREE;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
||||
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
|
||||
memory_info);
|
||||
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(5, result[0].base());
|
||||
EXPECT_EQ(5, result[0].size());
|
||||
}
|
||||
|
||||
TEST(ProcessInfo, AccessibleRangesOneMovedStart) {
|
||||
std::vector<ProcessInfo::MemoryInfo> memory_info;
|
||||
MEMORY_BASIC_INFORMATION mbi = {0};
|
||||
|
||||
mbi.BaseAddress = 0;
|
||||
mbi.RegionSize = 10;
|
||||
mbi.State = MEM_FREE;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
mbi.BaseAddress = reinterpret_cast<void*>(10);
|
||||
mbi.RegionSize = 20;
|
||||
mbi.State = MEM_COMMIT;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
||||
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
|
||||
memory_info);
|
||||
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(10, result[0].base());
|
||||
EXPECT_EQ(5, result[0].size());
|
||||
}
|
||||
|
||||
TEST(ProcessInfo, AccessibleRangesCoalesced) {
|
||||
std::vector<ProcessInfo::MemoryInfo> memory_info;
|
||||
MEMORY_BASIC_INFORMATION mbi = {0};
|
||||
|
||||
mbi.BaseAddress = 0;
|
||||
mbi.RegionSize = 10;
|
||||
mbi.State = MEM_FREE;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
mbi.BaseAddress = reinterpret_cast<void*>(10);
|
||||
mbi.RegionSize = 2;
|
||||
mbi.State = MEM_COMMIT;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
mbi.BaseAddress = reinterpret_cast<void*>(12);
|
||||
mbi.RegionSize = 5;
|
||||
mbi.State = MEM_RESERVE;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
||||
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(11, 4),
|
||||
memory_info);
|
||||
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(11, result[0].base());
|
||||
EXPECT_EQ(4, result[0].size());
|
||||
}
|
||||
|
||||
TEST(ProcessInfo, AccessibleRangesMiddleUnavailable) {
|
||||
std::vector<ProcessInfo::MemoryInfo> memory_info;
|
||||
MEMORY_BASIC_INFORMATION mbi = {0};
|
||||
|
||||
mbi.BaseAddress = 0;
|
||||
mbi.RegionSize = 10;
|
||||
mbi.State = MEM_COMMIT;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
mbi.BaseAddress = reinterpret_cast<void*>(10);
|
||||
mbi.RegionSize = 5;
|
||||
mbi.State = MEM_FREE;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
mbi.BaseAddress = reinterpret_cast<void*>(15);
|
||||
mbi.RegionSize = 100;
|
||||
mbi.State = MEM_COMMIT;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
||||
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 45),
|
||||
memory_info);
|
||||
|
||||
ASSERT_EQ(result.size(), 2u);
|
||||
EXPECT_EQ(5, result[0].base());
|
||||
EXPECT_EQ(5, result[0].size());
|
||||
EXPECT_EQ(15, result[1].base());
|
||||
EXPECT_EQ(35, result[1].size());
|
||||
}
|
||||
|
||||
TEST(ProcessInfo, RequestedBeforeMap) {
|
||||
std::vector<ProcessInfo::MemoryInfo> memory_info;
|
||||
MEMORY_BASIC_INFORMATION mbi = {0};
|
||||
|
||||
mbi.BaseAddress = reinterpret_cast<void*>(10);
|
||||
mbi.RegionSize = 10;
|
||||
mbi.State = MEM_COMMIT;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
||||
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
|
||||
memory_info);
|
||||
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(10, result[0].base());
|
||||
EXPECT_EQ(5, result[0].size());
|
||||
}
|
||||
|
||||
TEST(ProcessInfo, RequestedAfterMap) {
|
||||
std::vector<ProcessInfo::MemoryInfo> memory_info;
|
||||
MEMORY_BASIC_INFORMATION mbi = {0};
|
||||
|
||||
mbi.BaseAddress = reinterpret_cast<void*>(10);
|
||||
mbi.RegionSize = 10;
|
||||
mbi.State = MEM_COMMIT;
|
||||
memory_info.push_back(ProcessInfo::MemoryInfo(mbi));
|
||||
|
||||
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
||||
GetReadableRangesOfMemoryMap(
|
||||
CheckedRange<WinVMAddress, WinVMSize>(15, 100), memory_info);
|
||||
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(15, result[0].base());
|
||||
EXPECT_EQ(5, result[0].size());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
Loading…
x
Reference in New Issue
Block a user