Validate allocations against mappings

Bug 120032857 is seeing what appears to be allocations with incorrect
end addresses, leading to a much later crash when it tries to map
a zero page outside the valid virtual address space.  Detect allocations
that extend outside the highest or lowest memory mapping and crash
immediately instead.

Test: memunreachable_test
Bug: 120032857
Change-Id: I9be670a025143e7078360a6bf7a83219279614d9
This commit is contained in:
Colin Cross 2018-11-28 17:01:59 -08:00
parent d780dcbaa5
commit 3ca1976fa7
4 changed files with 39 additions and 0 deletions

View file

@ -35,6 +35,13 @@ bool HeapWalker::Allocation(uintptr_t begin, uintptr_t end) {
end = begin + 1;
}
Range range{begin, end};
if (valid_mappings_range_.end != 0 &&
(begin < valid_mappings_range_.begin || end > valid_mappings_range_.end)) {
MEM_LOG_ALWAYS_FATAL("allocation %p-%p is outside mapping range %p-%p",
reinterpret_cast<void*>(begin), reinterpret_cast<void*>(end),
reinterpret_cast<void*>(valid_mappings_range_.begin),
reinterpret_cast<void*>(valid_mappings_range_.end));
}
auto inserted = allocations_.insert(std::pair<Range, AllocationInfo>(range, AllocationInfo{}));
if (inserted.second) {
valid_allocations_range_.begin = std::min(valid_allocations_range_.begin, begin);
@ -87,6 +94,11 @@ void HeapWalker::RecurseRoot(const Range& root) {
}
}
void HeapWalker::Mapping(uintptr_t begin, uintptr_t end) {
valid_mappings_range_.begin = std::min(valid_mappings_range_.begin, begin);
valid_mappings_range_.end = std::max(valid_mappings_range_.end, end);
}
void HeapWalker::Root(uintptr_t begin, uintptr_t end) {
roots_.push_back(Range{begin, end});
}

View file

@ -59,6 +59,8 @@ class HeapWalker {
segv_page_count_(0) {
valid_allocations_range_.end = 0;
valid_allocations_range_.begin = ~valid_allocations_range_.end;
valid_mappings_range_.end = 0;
valid_mappings_range_.begin = ~valid_allocations_range_.end;
segv_handler_.install(
SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
@ -68,6 +70,7 @@ class HeapWalker {
~HeapWalker() {}
bool Allocation(uintptr_t begin, uintptr_t end);
void Mapping(uintptr_t begin, uintptr_t end);
void Root(uintptr_t begin, uintptr_t end);
void Root(const allocator::vector<uintptr_t>& vals);
@ -98,6 +101,7 @@ class HeapWalker {
AllocationMap allocations_;
size_t allocation_bytes_;
Range valid_allocations_range_;
Range valid_mappings_range_;
allocator::vector<Range> roots_;
allocator::vector<uintptr_t> root_vals_;

View file

@ -87,6 +87,11 @@ bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& thr
const allocator::vector<Mapping>& mappings,
const allocator::vector<uintptr_t>& refs) {
MEM_ALOGI("searching process %d for allocations", pid_);
for (auto it = mappings.begin(); it != mappings.end(); it++) {
heap_walker_.Mapping(it->begin, it->end);
}
allocator::vector<Mapping> heap_mappings{mappings};
allocator::vector<Mapping> anon_mappings{mappings};
allocator::vector<Mapping> globals_mappings{mappings};

View file

@ -73,6 +73,24 @@ TEST_F(HeapWalkerTest, zero) {
ASSERT_FALSE(heap_walker.Allocation(2, 3));
}
TEST_F(HeapWalkerTest, mapping) {
HeapWalker heap_walker(heap_);
heap_walker.Mapping(2, 3);
heap_walker.Mapping(4, 5);
ASSERT_TRUE(heap_walker.Allocation(2, 3));
ASSERT_TRUE(heap_walker.Allocation(4, 5));
// space between mappings is not checked, but could be in the future
ASSERT_TRUE(heap_walker.Allocation(3, 4));
// re-enable malloc, ASSERT_DEATH may allocate
disable_malloc_.Enable();
ASSERT_DEATH({ heap_walker.Allocation(1, 2); }, "0x1-0x2.*outside.*0x2-0x5");
ASSERT_DEATH({ heap_walker.Allocation(1, 3); }, "0x1-0x3.*outside.*0x2-0x5");
ASSERT_DEATH({ heap_walker.Allocation(4, 6); }, "0x4-0x6.*outside.*0x2-0x5");
ASSERT_DEATH({ heap_walker.Allocation(5, 6); }, "0x5-0x6.*outside.*0x2-0x5");
ASSERT_DEATH({ heap_walker.Allocation(1, 6); }, "0x1-0x6.*outside.*0x2-0x5");
}
#define buffer_begin(buffer) reinterpret_cast<uintptr_t>(buffer)
#define buffer_end(buffer) (reinterpret_cast<uintptr_t>(buffer) + sizeof(buffer))