| // Copyright 2017 The Crashpad Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "util/linux/memory_map.h" |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include "base/files/file_path.h" |
| #include "base/strings/stringprintf.h" |
| #include "build/build_config.h" |
| #include "gtest/gtest.h" |
| #include "test/errors.h" |
| #include "test/file.h" |
| #include "test/linux/fake_ptrace_connection.h" |
| #include "test/multiprocess.h" |
| #include "test/scoped_temp_dir.h" |
| #include "third_party/lss/lss.h" |
| #include "util/file/file_io.h" |
| #include "util/file/scoped_remove_file.h" |
| #include "util/linux/direct_ptrace_connection.h" |
| #include "util/misc/clock.h" |
| #include "util/misc/from_pointer_cast.h" |
| #include "util/posix/scoped_mmap.h" |
| |
| namespace crashpad { |
| namespace test { |
| namespace { |
| |
| TEST(MemoryMap, SelfLargeFiles) { |
| // This test is meant to test the handler's ability to understand files |
| // mapped from large offsets, even if the handler wasn't built with |
| // _FILE_OFFSET_BITS=64. ScopedTempDir needs to stat files to determine |
| // whether to recurse into directories, which may will fail without large file |
| // support. ScopedRemoveFile doesn't have that restriction. |
| ScopedTempDir dir; |
| ScopedRemoveFile large_file_path(dir.path().Append("crashpad_test_file")); |
| ScopedFileHandle handle( |
| LoggingOpenFileForReadAndWrite(large_file_path.get(), |
| FileWriteMode::kCreateOrFail, |
| FilePermissions::kWorldReadable)); |
| ASSERT_TRUE(handle.is_valid()); |
| |
| // sys_fallocate supports large files as long as the kernel supports them, |
| // regardless of _FILE_OFFSET_BITS. |
| off64_t off = 1llu + UINT32_MAX; |
| ASSERT_EQ(sys_fallocate(handle.get(), 0, off, getpagesize()), 0) |
| << ErrnoMessage("fallocate"); |
| |
| ScopedMmap mapping; |
| void* addr = sys_mmap( |
| nullptr, getpagesize(), PROT_READ, MAP_SHARED, handle.get(), off); |
| ASSERT_TRUE(addr); |
| ASSERT_TRUE(mapping.ResetAddrLen(addr, getpagesize())); |
| |
| FakePtraceConnection connection; |
| ASSERT_TRUE(connection.Initialize(getpid())); |
| MemoryMap map; |
| ASSERT_TRUE(map.Initialize(&connection)); |
| } |
| |
| TEST(MemoryMap, SelfBasic) { |
| ScopedMmap mmapping; |
| ASSERT_TRUE(mmapping.ResetMmap(nullptr, |
| getpagesize(), |
| PROT_EXEC | PROT_READ, |
| MAP_SHARED | MAP_ANON, |
| -1, |
| 0)); |
| |
| FakePtraceConnection connection; |
| ASSERT_TRUE(connection.Initialize(getpid())); |
| |
| MemoryMap map; |
| ASSERT_TRUE(map.Initialize(&connection)); |
| |
| auto stack_address = FromPointerCast<LinuxVMAddress>(&map); |
| const MemoryMap::Mapping* mapping = map.FindMapping(stack_address); |
| ASSERT_TRUE(mapping); |
| EXPECT_GE(stack_address, mapping->range.Base()); |
| EXPECT_LT(stack_address, mapping->range.End()); |
| EXPECT_TRUE(mapping->readable); |
| EXPECT_TRUE(mapping->writable); |
| |
| auto code_address = FromPointerCast<LinuxVMAddress>(getpid); |
| mapping = map.FindMapping(code_address); |
| ASSERT_TRUE(mapping); |
| EXPECT_GE(code_address, mapping->range.Base()); |
| EXPECT_LT(code_address, mapping->range.End()); |
| #if !defined(OS_ANDROID) |
| // Android Q+ supports execute only memory. |
| EXPECT_TRUE(mapping->readable); |
| #endif |
| EXPECT_FALSE(mapping->writable); |
| EXPECT_TRUE(mapping->executable); |
| |
| auto mapping_address = mmapping.addr_as<LinuxVMAddress>(); |
| mapping = map.FindMapping(mapping_address); |
| ASSERT_TRUE(mapping); |
| mapping = map.FindMapping(mapping_address + mmapping.len() - 1); |
| ASSERT_TRUE(mapping); |
| EXPECT_EQ(mapping_address, mapping->range.Base()); |
| EXPECT_EQ(mapping_address + mmapping.len(), mapping->range.End()); |
| EXPECT_TRUE(mapping->readable); |
| EXPECT_FALSE(mapping->writable); |
| EXPECT_TRUE(mapping->executable); |
| EXPECT_TRUE(mapping->shareable); |
| } |
| |
| void InitializeFile(const base::FilePath& path, |
| size_t size, |
| ScopedFileHandle* handle) { |
| ASSERT_FALSE(FileExists(path)); |
| |
| handle->reset(LoggingOpenFileForReadAndWrite( |
| path, FileWriteMode::kReuseOrCreate, FilePermissions::kOwnerOnly)); |
| ASSERT_TRUE(handle->is_valid()); |
| std::string file_contents(size, std::string::value_type()); |
| CheckedWriteFile(handle->get(), file_contents.c_str(), file_contents.size()); |
| } |
| |
| class MapChildTest : public Multiprocess { |
| public: |
| MapChildTest() : Multiprocess(), page_size_(getpagesize()) {} |
| ~MapChildTest() {} |
| |
| private: |
| void MultiprocessParent() override { |
| LinuxVMAddress code_address; |
| CheckedReadFileExactly( |
| ReadPipeHandle(), &code_address, sizeof(code_address)); |
| |
| LinuxVMAddress stack_address; |
| CheckedReadFileExactly( |
| ReadPipeHandle(), &stack_address, sizeof(stack_address)); |
| |
| LinuxVMAddress mapped_address; |
| CheckedReadFileExactly( |
| ReadPipeHandle(), &mapped_address, sizeof(mapped_address)); |
| |
| LinuxVMAddress mapped_file_address; |
| CheckedReadFileExactly( |
| ReadPipeHandle(), &mapped_file_address, sizeof(mapped_file_address)); |
| LinuxVMSize path_length; |
| CheckedReadFileExactly(ReadPipeHandle(), &path_length, sizeof(path_length)); |
| std::string mapped_file_name(path_length, std::string::value_type()); |
| CheckedReadFileExactly(ReadPipeHandle(), &mapped_file_name[0], path_length); |
| |
| DirectPtraceConnection connection; |
| ASSERT_TRUE(connection.Initialize(ChildPID())); |
| |
| MemoryMap map; |
| ASSERT_TRUE(map.Initialize(&connection)); |
| |
| const MemoryMap::Mapping* mapping = map.FindMapping(code_address); |
| ASSERT_TRUE(mapping); |
| EXPECT_GE(code_address, mapping->range.Base()); |
| EXPECT_LT(code_address, mapping->range.End()); |
| #if !defined(OS_ANDROID) |
| // Android Q+ supports execute only memory. |
| EXPECT_TRUE(mapping->readable); |
| #endif |
| EXPECT_TRUE(mapping->executable); |
| EXPECT_FALSE(mapping->writable); |
| |
| mapping = map.FindMapping(stack_address); |
| ASSERT_TRUE(mapping); |
| EXPECT_GE(stack_address, mapping->range.Base()); |
| EXPECT_LT(stack_address, mapping->range.End()); |
| EXPECT_TRUE(mapping->readable); |
| EXPECT_TRUE(mapping->writable); |
| |
| mapping = map.FindMapping(mapped_address); |
| ASSERT_TRUE(mapping); |
| EXPECT_EQ(mapped_address, mapping->range.Base()); |
| EXPECT_EQ(mapped_address + page_size_, mapping->range.End()); |
| EXPECT_FALSE(mapping->readable); |
| EXPECT_FALSE(mapping->writable); |
| EXPECT_FALSE(mapping->executable); |
| EXPECT_TRUE(mapping->shareable); |
| |
| mapping = map.FindMapping(mapped_file_address); |
| ASSERT_TRUE(mapping); |
| EXPECT_EQ(mapped_file_address, mapping->range.Base()); |
| EXPECT_EQ(mapping->offset, static_cast<int64_t>(page_size_)); |
| EXPECT_TRUE(mapping->readable); |
| EXPECT_TRUE(mapping->writable); |
| EXPECT_FALSE(mapping->executable); |
| EXPECT_FALSE(mapping->shareable); |
| EXPECT_EQ(mapping->name, mapped_file_name); |
| struct stat file_stat; |
| ASSERT_EQ(stat(mapped_file_name.c_str(), &file_stat), 0) |
| << ErrnoMessage("stat"); |
| EXPECT_EQ(mapping->device, file_stat.st_dev); |
| EXPECT_EQ(mapping->inode, file_stat.st_ino); |
| EXPECT_EQ(map.FindMappingWithName(mapping->name), mapping); |
| } |
| |
| void MultiprocessChild() override { |
| auto code_address = FromPointerCast<LinuxVMAddress>(getpid); |
| CheckedWriteFile(WritePipeHandle(), &code_address, sizeof(code_address)); |
| |
| auto stack_address = FromPointerCast<LinuxVMAddress>(&code_address); |
| CheckedWriteFile(WritePipeHandle(), &stack_address, sizeof(stack_address)); |
| |
| ScopedMmap mapping; |
| ASSERT_TRUE(mapping.ResetMmap( |
| nullptr, page_size_, PROT_NONE, MAP_SHARED | MAP_ANON, -1, 0)); |
| auto mapped_address = mapping.addr_as<LinuxVMAddress>(); |
| CheckedWriteFile( |
| WritePipeHandle(), &mapped_address, sizeof(mapped_address)); |
| |
| ScopedTempDir temp_dir; |
| base::FilePath path = |
| temp_dir.path().Append(FILE_PATH_LITERAL("test_file")); |
| ScopedFileHandle handle; |
| ASSERT_NO_FATAL_FAILURE(InitializeFile(path, page_size_ * 2, &handle)); |
| |
| ScopedMmap file_mapping; |
| ASSERT_TRUE(file_mapping.ResetMmap(nullptr, |
| page_size_, |
| PROT_READ | PROT_WRITE, |
| MAP_PRIVATE, |
| handle.get(), |
| page_size_)); |
| |
| auto mapped_file_address = file_mapping.addr_as<LinuxVMAddress>(); |
| CheckedWriteFile( |
| WritePipeHandle(), &mapped_file_address, sizeof(mapped_file_address)); |
| LinuxVMSize path_length = path.value().size(); |
| CheckedWriteFile(WritePipeHandle(), &path_length, sizeof(path_length)); |
| CheckedWriteFile(WritePipeHandle(), path.value().c_str(), path_length); |
| |
| CheckedReadFileAtEOF(ReadPipeHandle()); |
| } |
| |
| const size_t page_size_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MapChildTest); |
| }; |
| |
| TEST(MemoryMap, MapChild) { |
| MapChildTest test; |
| test.Run(); |
| } |
| |
| // Some systems optimize mappings by allocating new mappings inside existing |
| // mappings with matching permissions. Work around this by allocating one large |
| // mapping and then switching up the permissions of individual pages to force |
| // populating more entries in the maps file. |
| void InitializeMappings(ScopedMmap* mappings, |
| size_t num_mappings, |
| size_t mapping_size) { |
| ASSERT_TRUE(mappings->ResetMmap(nullptr, |
| mapping_size * num_mappings, |
| PROT_READ, |
| MAP_PRIVATE | MAP_ANON, |
| -1, |
| 0)); |
| |
| auto region = mappings->addr_as<LinuxVMAddress>(); |
| for (size_t index = 0; index < num_mappings; index += 2) { |
| ASSERT_EQ(mprotect(reinterpret_cast<void*>(region + index * mapping_size), |
| mapping_size, |
| PROT_READ | PROT_WRITE), |
| 0) |
| << ErrnoMessage("mprotect"); |
| } |
| } |
| |
| void ExpectMappings(const MemoryMap& map, |
| LinuxVMAddress region_addr, |
| size_t num_mappings, |
| size_t mapping_size) { |
| for (size_t index = 0; index < num_mappings; ++index) { |
| SCOPED_TRACE(base::StringPrintf("index %zu", index)); |
| |
| auto mapping_address = region_addr + index * mapping_size; |
| const MemoryMap::Mapping* mapping = map.FindMapping(mapping_address); |
| ASSERT_TRUE(mapping); |
| EXPECT_EQ(mapping_address, mapping->range.Base()); |
| EXPECT_EQ(mapping_address + mapping_size, mapping->range.End()); |
| EXPECT_TRUE(mapping->readable); |
| EXPECT_FALSE(mapping->shareable); |
| EXPECT_FALSE(mapping->executable); |
| if (index % 2 == 0) { |
| EXPECT_TRUE(mapping->writable); |
| } else { |
| EXPECT_FALSE(mapping->writable); |
| } |
| } |
| } |
| |
| TEST(MemoryMap, SelfLargeMapFile) { |
| constexpr size_t kNumMappings = 1024; |
| const size_t page_size = getpagesize(); |
| ScopedMmap mappings; |
| |
| ASSERT_NO_FATAL_FAILURE( |
| InitializeMappings(&mappings, kNumMappings, page_size)); |
| |
| FakePtraceConnection connection; |
| ASSERT_TRUE(connection.Initialize(getpid())); |
| |
| MemoryMap map; |
| ASSERT_TRUE(map.Initialize(&connection)); |
| |
| ExpectMappings( |
| map, mappings.addr_as<LinuxVMAddress>(), kNumMappings, page_size); |
| } |
| |
| class MapRunningChildTest : public Multiprocess { |
| public: |
| MapRunningChildTest() : Multiprocess(), page_size_(getpagesize()) {} |
| ~MapRunningChildTest() {} |
| |
| private: |
| void MultiprocessParent() override { |
| // Let the child get started |
| LinuxVMAddress region_addr; |
| CheckedReadFileExactly(ReadPipeHandle(), ®ion_addr, sizeof(region_addr)); |
| |
| for (int iter = 0; iter < 8; ++iter) { |
| SCOPED_TRACE(base::StringPrintf("iter %d", iter)); |
| |
| // Let the child get back to its work |
| SleepNanoseconds(1000); |
| |
| DirectPtraceConnection connection; |
| ASSERT_TRUE(connection.Initialize(ChildPID())); |
| |
| MemoryMap map; |
| ASSERT_TRUE(map.Initialize(&connection)); |
| |
| // We should at least find the original mappings. The extra mappings may |
| // or not be found depending on scheduling. |
| ExpectMappings(map, region_addr, kNumMappings, page_size_); |
| } |
| } |
| |
| void MultiprocessChild() override { |
| ASSERT_EQ(fcntl(ReadPipeHandle(), F_SETFL, O_NONBLOCK), 0) |
| << ErrnoMessage("fcntl"); |
| |
| ScopedMmap mappings; |
| ASSERT_NO_FATAL_FAILURE( |
| InitializeMappings(&mappings, kNumMappings, page_size_)); |
| |
| // Let the parent start mapping us |
| auto region_addr = mappings.addr_as<LinuxVMAddress>(); |
| CheckedWriteFile(WritePipeHandle(), ®ion_addr, sizeof(region_addr)); |
| |
| // But don't stop there! |
| constexpr size_t kNumExtraMappings = 256; |
| ScopedMmap extra_mappings; |
| |
| while (true) { |
| ASSERT_NO_FATAL_FAILURE( |
| InitializeMappings(&extra_mappings, kNumExtraMappings, page_size_)); |
| |
| // Quit when the parent is done |
| char c; |
| FileOperationResult res = ReadFile(ReadPipeHandle(), &c, sizeof(c)); |
| if (res == 0) { |
| break; |
| } |
| ASSERT_EQ(errno, EAGAIN); |
| } |
| } |
| |
| static constexpr size_t kNumMappings = 1024; |
| const size_t page_size_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MapRunningChildTest); |
| }; |
| |
| TEST(MemoryMap, MapRunningChild) { |
| MapRunningChildTest test; |
| test.Run(); |
| } |
| |
| // Expects first and third pages from mapping_start to refer to the same mapped |
| // file. The second page should not. |
| void ExpectFindFilePossibleMmapStarts(LinuxVMAddress mapping_start, |
| LinuxVMSize page_size) { |
| FakePtraceConnection connection; |
| ASSERT_TRUE(connection.Initialize(getpid())); |
| |
| MemoryMap map; |
| ASSERT_TRUE(map.Initialize(&connection)); |
| |
| auto mapping1 = map.FindMapping(mapping_start); |
| ASSERT_TRUE(mapping1); |
| auto mapping2 = map.FindMapping(mapping_start + page_size); |
| ASSERT_TRUE(mapping2); |
| auto mapping3 = map.FindMapping(mapping_start + page_size * 2); |
| ASSERT_TRUE(mapping3); |
| |
| ASSERT_NE(mapping1, mapping2); |
| ASSERT_NE(mapping2, mapping3); |
| |
| auto mappings = map.FindFilePossibleMmapStarts(*mapping1); |
| ASSERT_EQ(mappings->Count(), 1u); |
| EXPECT_EQ(mappings->Next(), mapping1); |
| |
| mappings = map.FindFilePossibleMmapStarts(*mapping2); |
| ASSERT_EQ(mappings->Count(), 1u); |
| EXPECT_EQ(mappings->Next(), mapping2); |
| |
| mappings = map.FindFilePossibleMmapStarts(*mapping3); |
| #if defined(OS_ANDROID) |
| EXPECT_EQ(mappings->Count(), 2u); |
| #else |
| ASSERT_EQ(mappings->Count(), 1u); |
| EXPECT_EQ(mappings->Next(), mapping1); |
| #endif |
| } |
| |
| TEST(MemoryMap, FindFilePossibleMmapStarts) { |
| const size_t page_size = getpagesize(); |
| |
| ScopedTempDir temp_dir; |
| base::FilePath path = temp_dir.path().Append( |
| FILE_PATH_LITERAL("FindFilePossibleMmapStartsTestFile")); |
| ScopedFileHandle handle; |
| size_t file_length = page_size * 3; |
| ASSERT_NO_FATAL_FAILURE(InitializeFile(path, file_length, &handle)); |
| |
| ScopedMmap file_mapping; |
| ASSERT_TRUE(file_mapping.ResetMmap( |
| nullptr, file_length, PROT_READ, MAP_PRIVATE, handle.get(), 0)); |
| auto mapping_start = file_mapping.addr_as<LinuxVMAddress>(); |
| |
| // Change the permissions on the second page to split the mapping into three |
| // parts. |
| ASSERT_EQ(mprotect(file_mapping.addr_as<char*>() + page_size, |
| page_size, |
| PROT_READ | PROT_WRITE), |
| 0); |
| |
| // Basic |
| { |
| FakePtraceConnection connection; |
| ASSERT_TRUE(connection.Initialize(getpid())); |
| |
| MemoryMap map; |
| ASSERT_TRUE(map.Initialize(&connection)); |
| |
| auto mapping1 = map.FindMapping(mapping_start); |
| ASSERT_TRUE(mapping1); |
| auto mapping2 = map.FindMapping(mapping_start + page_size); |
| ASSERT_TRUE(mapping2); |
| auto mapping3 = map.FindMapping(mapping_start + page_size * 2); |
| ASSERT_TRUE(mapping3); |
| |
| ASSERT_NE(mapping1, mapping2); |
| ASSERT_NE(mapping2, mapping3); |
| |
| #if defined(OS_ANDROID) |
| auto mappings = map.FindFilePossibleMmapStarts(*mapping1); |
| EXPECT_EQ(mappings->Count(), 1u); |
| EXPECT_EQ(mappings->Next(), mapping1); |
| EXPECT_EQ(mappings->Next(), nullptr); |
| |
| mappings = map.FindFilePossibleMmapStarts(*mapping2); |
| EXPECT_EQ(mappings->Count(), 2u); |
| |
| mappings = map.FindFilePossibleMmapStarts(*mapping3); |
| EXPECT_EQ(mappings->Count(), 3u); |
| #else |
| auto mappings = map.FindFilePossibleMmapStarts(*mapping1); |
| ASSERT_EQ(mappings->Count(), 1u); |
| EXPECT_EQ(mappings->Next(), mapping1); |
| EXPECT_EQ(mappings->Next(), nullptr); |
| |
| mappings = map.FindFilePossibleMmapStarts(*mapping2); |
| ASSERT_EQ(mappings->Count(), 1u); |
| EXPECT_EQ(mappings->Next(), mapping1); |
| |
| mappings = map.FindFilePossibleMmapStarts(*mapping3); |
| ASSERT_EQ(mappings->Count(), 1u); |
| EXPECT_EQ(mappings->Next(), mapping1); |
| #endif |
| |
| #if defined(ARCH_CPU_64_BITS) |
| constexpr bool is_64_bit = true; |
| #else |
| constexpr bool is_64_bit = false; |
| #endif |
| MemoryMap::Mapping bad_mapping; |
| bad_mapping.range.SetRange(is_64_bit, 0, 1); |
| mappings = map.FindFilePossibleMmapStarts(bad_mapping); |
| EXPECT_EQ(mappings->Count(), 0u); |
| EXPECT_EQ(mappings->Next(), nullptr); |
| } |
| |
| // Make the second page an anonymous mapping |
| file_mapping.ResetAddrLen(file_mapping.addr_as<void*>(), page_size); |
| ScopedMmap page2_mapping; |
| ASSERT_TRUE(page2_mapping.ResetMmap(file_mapping.addr_as<char*>() + page_size, |
| page_size, |
| PROT_READ | PROT_WRITE, |
| MAP_PRIVATE | MAP_ANON | MAP_FIXED, |
| -1, |
| 0)); |
| ScopedMmap page3_mapping; |
| ASSERT_TRUE( |
| page3_mapping.ResetMmap(file_mapping.addr_as<char*>() + page_size * 2, |
| page_size, |
| PROT_READ, |
| MAP_PRIVATE | MAP_FIXED, |
| handle.get(), |
| page_size * 2)); |
| ExpectFindFilePossibleMmapStarts(mapping_start, page_size); |
| |
| // Map the second page to another file. |
| ScopedFileHandle handle2; |
| base::FilePath path2 = temp_dir.path().Append( |
| FILE_PATH_LITERAL("FindFilePossibleMmapStartsTestFile2")); |
| ASSERT_NO_FATAL_FAILURE(InitializeFile(path2, page_size, &handle2)); |
| |
| page2_mapping.ResetMmap(file_mapping.addr_as<char*>() + page_size, |
| page_size, |
| PROT_READ, |
| MAP_PRIVATE | MAP_FIXED, |
| handle2.get(), |
| 0); |
| 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); |
| #if defined(OS_ANDROID) |
| EXPECT_EQ(possible_starts->Count(), 1u); |
| #else |
| EXPECT_EQ(possible_starts->Count(), 0u); |
| #endif |
| |
| mapping = map.FindMapping(file_mapping1.addr_as<VMAddress>()); |
| ASSERT_TRUE(mapping); |
| possible_starts = map.FindFilePossibleMmapStarts(*mapping); |
| #if defined(OS_ANDROID) |
| EXPECT_EQ(possible_starts->Count(), 2u); |
| #else |
| EXPECT_EQ(possible_starts->Count(), 1u); |
| #endif |
| |
| mapping = map.FindMapping(file_mapping2.addr_as<VMAddress>()); |
| ASSERT_TRUE(mapping); |
| possible_starts = map.FindFilePossibleMmapStarts(*mapping); |
| #if defined(OS_ANDROID) |
| EXPECT_EQ(possible_starts->Count(), 3u); |
| #else |
| EXPECT_EQ(possible_starts->Count(), 2u); |
| #endif |
| |
| mapping = map.FindMapping(file_mapping3.addr_as<VMAddress>()); |
| ASSERT_TRUE(mapping); |
| possible_starts = map.FindFilePossibleMmapStarts(*mapping); |
| #if defined(OS_ANDROID) |
| EXPECT_EQ(possible_starts->Count(), 4u); |
| #else |
| EXPECT_EQ(possible_starts->Count(), 3u); |
| #endif |
| |
| mapping = map.FindMapping(file_mapping4.addr_as<VMAddress>()); |
| ASSERT_TRUE(mapping); |
| possible_starts = map.FindFilePossibleMmapStarts(*mapping); |
| #if defined(OS_ANDROID) |
| EXPECT_EQ(possible_starts->Count(), 5u); |
| #else |
| EXPECT_EQ(possible_starts->Count(), 4u); |
| #endif |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace crashpad |