| // Copyright 2015 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/win/process_info.h" |
| |
| #include <dbghelp.h> |
| #include <intrin.h> |
| #include <wchar.h> |
| |
| #include <memory> |
| |
| #include "base/files/file_path.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "gtest/gtest.h" |
| #include "test/errors.h" |
| #include "test/scoped_temp_dir.h" |
| #include "test/test_paths.h" |
| #include "test/win/child_launcher.h" |
| #include "util/file/file_io.h" |
| #include "util/misc/from_pointer_cast.h" |
| #include "util/misc/random_string.h" |
| #include "util/misc/uuid.h" |
| #include "util/win/command_line.h" |
| #include "util/win/get_function.h" |
| #include "util/win/handle.h" |
| #include "util/win/scoped_handle.h" |
| #include "util/win/scoped_registry_key.h" |
| |
| namespace crashpad { |
| namespace test { |
| namespace { |
| |
| constexpr wchar_t kNtdllName[] = L"\\ntdll.dll"; |
| |
| #if !defined(ARCH_CPU_64_BITS) |
| bool IsProcessWow64(HANDLE process_handle) { |
| static const auto is_wow64_process = |
| GET_FUNCTION(L"kernel32.dll", ::IsWow64Process); |
| if (!is_wow64_process) |
| return false; |
| BOOL is_wow64; |
| if (!is_wow64_process(process_handle, &is_wow64)) { |
| PLOG(ERROR) << "IsWow64Process"; |
| return false; |
| } |
| return !!is_wow64; |
| } |
| #endif |
| |
| void VerifyAddressInInCodePage(const ProcessInfo& process_info, |
| WinVMAddress code_address) { |
| // Make sure the child code address is an code page address with the right |
| // information. |
| const ProcessInfo::MemoryBasicInformation64Vector& memory_info = |
| process_info.MemoryInfo(); |
| bool found_region = false; |
| for (const auto& mi : memory_info) { |
| if (mi.BaseAddress <= code_address && |
| mi.BaseAddress + mi.RegionSize > code_address) { |
| EXPECT_EQ(mi.State, static_cast<DWORD>(MEM_COMMIT)); |
| EXPECT_EQ(mi.Protect, static_cast<DWORD>(PAGE_EXECUTE_READ)); |
| EXPECT_EQ(mi.Type, static_cast<DWORD>(MEM_IMAGE)); |
| EXPECT_FALSE(found_region); |
| found_region = true; |
| } |
| } |
| EXPECT_TRUE(found_region); |
| } |
| |
| TEST(ProcessInfo, Self) { |
| ProcessInfo process_info; |
| ASSERT_TRUE(process_info.Initialize(GetCurrentProcess())); |
| EXPECT_EQ(process_info.ProcessID(), GetCurrentProcessId()); |
| EXPECT_GT(process_info.ParentProcessID(), 0u); |
| |
| #if defined(ARCH_CPU_64_BITS) |
| EXPECT_TRUE(process_info.Is64Bit()); |
| EXPECT_FALSE(process_info.IsWow64()); |
| #else |
| EXPECT_FALSE(process_info.Is64Bit()); |
| if (IsProcessWow64(GetCurrentProcess())) |
| EXPECT_TRUE(process_info.IsWow64()); |
| else |
| EXPECT_FALSE(process_info.IsWow64()); |
| #endif |
| |
| std::wstring command_line; |
| EXPECT_TRUE(process_info.CommandLine(&command_line)); |
| EXPECT_EQ(command_line, std::wstring(GetCommandLine())); |
| |
| std::vector<ProcessInfo::Module> modules; |
| EXPECT_TRUE(process_info.Modules(&modules)); |
| ASSERT_GE(modules.size(), 2u); |
| std::wstring self_name = |
| std::wstring(1, '\\') + |
| TestPaths::ExpectedExecutableBasename(L"crashpad_util_test").value(); |
| ASSERT_GE(modules[0].name.size(), self_name.size()); |
| EXPECT_EQ(modules[0].name.substr(modules[0].name.size() - self_name.size()), |
| self_name); |
| ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName)); |
| EXPECT_EQ(modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName)), |
| kNtdllName); |
| |
| EXPECT_EQ(modules[0].dll_base, |
| reinterpret_cast<uintptr_t>(GetModuleHandle(nullptr))); |
| EXPECT_EQ(modules[1].dll_base, |
| reinterpret_cast<uintptr_t>(GetModuleHandle(L"ntdll.dll"))); |
| |
| EXPECT_GT(modules[0].size, 0u); |
| EXPECT_GT(modules[1].size, 0u); |
| |
| EXPECT_EQ(modules[0].timestamp, |
| GetTimestampForLoadedLibrary(GetModuleHandle(nullptr))); |
| // System modules are forced to particular stamps and the file header values |
| // don't match the on-disk times. Just make sure we got some data here. |
| EXPECT_GT(modules[1].timestamp, 0); |
| |
| // Find something we know is a code address and confirm expected memory |
| // information settings. |
| VerifyAddressInInCodePage(process_info, |
| FromPointerCast<WinVMAddress>(_ReturnAddress())); |
| } |
| |
| void TestOtherProcess(TestPaths::Architecture architecture) { |
| ProcessInfo process_info; |
| |
| UUID done_uuid; |
| done_uuid.InitializeWithNew(); |
| |
| ScopedKernelHANDLE done( |
| CreateEvent(nullptr, true, false, done_uuid.ToString16().c_str())); |
| ASSERT_TRUE(done.get()) << ErrorMessage("CreateEvent"); |
| |
| base::FilePath child_test_executable = |
| TestPaths::BuildArtifact(L"util", |
| L"process_info_test_child", |
| TestPaths::FileType::kExecutable, |
| architecture); |
| std::wstring args; |
| AppendCommandLineArgument(done_uuid.ToString16(), &args); |
| |
| ChildLauncher child(child_test_executable, args); |
| ASSERT_NO_FATAL_FAILURE(child.Start()); |
| |
| // The child sends us a code address we can look up in the memory map. |
| WinVMAddress code_address; |
| CheckedReadFileExactly( |
| child.stdout_read_handle(), &code_address, sizeof(code_address)); |
| |
| ASSERT_TRUE(process_info.Initialize(child.process_handle())); |
| |
| // Tell the test it's OK to shut down now that we've read our data. |
| EXPECT_TRUE(SetEvent(done.get())) << ErrorMessage("SetEvent"); |
| |
| EXPECT_EQ(child.WaitForExit(), 0u); |
| |
| std::vector<ProcessInfo::Module> modules; |
| EXPECT_TRUE(process_info.Modules(&modules)); |
| ASSERT_GE(modules.size(), 3u); |
| std::wstring child_name = L"\\crashpad_util_test_process_info_test_child.exe"; |
| ASSERT_GE(modules[0].name.size(), child_name.size()); |
| EXPECT_EQ(modules[0].name.substr(modules[0].name.size() - child_name.size()), |
| child_name); |
| ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName)); |
| EXPECT_EQ(modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName)), |
| kNtdllName); |
| // lz32.dll is an uncommonly-used-but-always-available module that the test |
| // binary manually loads. |
| static constexpr wchar_t kLz32dllName[] = L"\\lz32.dll"; |
| auto& lz32 = modules[modules.size() - 2]; |
| ASSERT_GE(lz32.name.size(), wcslen(kLz32dllName)); |
| EXPECT_EQ(lz32.name.substr(lz32.name.size() - wcslen(kLz32dllName)), |
| kLz32dllName); |
| |
| // Note that the test code corrupts the PEB MemoryOrder list, whereas |
| // ProcessInfo::Modules() retrieves the module names via the PEB LoadOrder |
| // list. These are expected to point to the same strings, but theoretically |
| // could be separate. |
| auto& corrupted = modules.back(); |
| EXPECT_EQ(corrupted.name, L"???"); |
| |
| VerifyAddressInInCodePage(process_info, code_address); |
| } |
| |
| TEST(ProcessInfo, OtherProcess) { |
| TestOtherProcess(TestPaths::Architecture::kDefault); |
| } |
| |
| #if defined(ARCH_CPU_64_BITS) |
| TEST(ProcessInfo, OtherProcessWOW64) { |
| if (!TestPaths::Has32BitBuildArtifacts()) { |
| GTEST_SKIP(); |
| } |
| |
| TestOtherProcess(TestPaths::Architecture::k32Bit); |
| } |
| #endif // ARCH_CPU_64_BITS |
| |
| TEST(ProcessInfo, AccessibleRangesNone) { |
| ProcessInfo::MemoryBasicInformation64Vector memory_info; |
| MEMORY_BASIC_INFORMATION64 mbi = {0}; |
| |
| mbi.BaseAddress = 0; |
| mbi.RegionSize = 10; |
| mbi.State = MEM_FREE; |
| memory_info.push_back(mbi); |
| |
| std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = |
| GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(2, 4), |
| memory_info); |
| |
| EXPECT_TRUE(result.empty()); |
| } |
| |
| TEST(ProcessInfo, AccessibleRangesOneInside) { |
| ProcessInfo::MemoryBasicInformation64Vector memory_info; |
| MEMORY_BASIC_INFORMATION64 mbi = {0}; |
| |
| mbi.BaseAddress = 0; |
| mbi.RegionSize = 10; |
| mbi.State = MEM_COMMIT; |
| memory_info.push_back(mbi); |
| |
| std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = |
| GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(2, 4), |
| memory_info); |
| |
| ASSERT_EQ(result.size(), 1u); |
| EXPECT_EQ(result[0].base(), 2u); |
| EXPECT_EQ(result[0].size(), 4u); |
| } |
| |
| TEST(ProcessInfo, AccessibleRangesOneTruncatedSize) { |
| ProcessInfo::MemoryBasicInformation64Vector memory_info; |
| MEMORY_BASIC_INFORMATION64 mbi = {0}; |
| |
| mbi.BaseAddress = 0; |
| mbi.RegionSize = 10; |
| mbi.State = MEM_COMMIT; |
| memory_info.push_back(mbi); |
| |
| mbi.BaseAddress = 10; |
| mbi.RegionSize = 20; |
| mbi.State = MEM_FREE; |
| memory_info.push_back(mbi); |
| |
| std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = |
| GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10), |
| memory_info); |
| |
| ASSERT_EQ(result.size(), 1u); |
| EXPECT_EQ(result[0].base(), 5u); |
| EXPECT_EQ(result[0].size(), 5u); |
| } |
| |
| TEST(ProcessInfo, AccessibleRangesOneMovedStart) { |
| ProcessInfo::MemoryBasicInformation64Vector memory_info; |
| MEMORY_BASIC_INFORMATION64 mbi = {0}; |
| |
| mbi.BaseAddress = 0; |
| mbi.RegionSize = 10; |
| mbi.State = MEM_FREE; |
| memory_info.push_back(mbi); |
| |
| mbi.BaseAddress = 10; |
| mbi.RegionSize = 20; |
| mbi.State = MEM_COMMIT; |
| memory_info.push_back(mbi); |
| |
| std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = |
| GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10), |
| memory_info); |
| |
| ASSERT_EQ(result.size(), 1u); |
| EXPECT_EQ(result[0].base(), 10u); |
| EXPECT_EQ(result[0].size(), 5u); |
| } |
| |
| TEST(ProcessInfo, ReserveIsInaccessible) { |
| ProcessInfo::MemoryBasicInformation64Vector memory_info; |
| MEMORY_BASIC_INFORMATION64 mbi = {0}; |
| |
| mbi.BaseAddress = 0; |
| mbi.RegionSize = 10; |
| mbi.State = MEM_RESERVE; |
| memory_info.push_back(mbi); |
| |
| mbi.BaseAddress = 10; |
| mbi.RegionSize = 20; |
| mbi.State = MEM_COMMIT; |
| memory_info.push_back(mbi); |
| |
| std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = |
| GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10), |
| memory_info); |
| |
| ASSERT_EQ(result.size(), 1u); |
| EXPECT_EQ(result[0].base(), 10u); |
| EXPECT_EQ(result[0].size(), 5u); |
| } |
| |
| TEST(ProcessInfo, PageGuardIsInaccessible) { |
| ProcessInfo::MemoryBasicInformation64Vector memory_info; |
| MEMORY_BASIC_INFORMATION64 mbi = {0}; |
| |
| mbi.BaseAddress = 0; |
| mbi.RegionSize = 10; |
| mbi.State = MEM_COMMIT; |
| mbi.Protect = PAGE_GUARD; |
| memory_info.push_back(mbi); |
| |
| mbi.BaseAddress = 10; |
| mbi.RegionSize = 20; |
| mbi.State = MEM_COMMIT; |
| mbi.Protect = 0; |
| memory_info.push_back(mbi); |
| |
| std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = |
| GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10), |
| memory_info); |
| |
| ASSERT_EQ(result.size(), 1u); |
| EXPECT_EQ(result[0].base(), 10u); |
| EXPECT_EQ(result[0].size(), 5u); |
| } |
| |
| TEST(ProcessInfo, PageNoAccessIsInaccessible) { |
| ProcessInfo::MemoryBasicInformation64Vector memory_info; |
| MEMORY_BASIC_INFORMATION64 mbi = {0}; |
| |
| mbi.BaseAddress = 0; |
| mbi.RegionSize = 10; |
| mbi.State = MEM_COMMIT; |
| mbi.Protect = PAGE_NOACCESS; |
| memory_info.push_back(mbi); |
| |
| mbi.BaseAddress = 10; |
| mbi.RegionSize = 20; |
| mbi.State = MEM_COMMIT; |
| mbi.Protect = 0; |
| memory_info.push_back(mbi); |
| |
| std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = |
| GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10), |
| memory_info); |
| |
| ASSERT_EQ(result.size(), 1u); |
| EXPECT_EQ(result[0].base(), 10u); |
| EXPECT_EQ(result[0].size(), 5u); |
| } |
| |
| TEST(ProcessInfo, AccessibleRangesCoalesced) { |
| ProcessInfo::MemoryBasicInformation64Vector memory_info; |
| MEMORY_BASIC_INFORMATION64 mbi = {0}; |
| |
| mbi.BaseAddress = 0; |
| mbi.RegionSize = 10; |
| mbi.State = MEM_FREE; |
| memory_info.push_back(mbi); |
| |
| mbi.BaseAddress = 10; |
| mbi.RegionSize = 2; |
| mbi.State = MEM_COMMIT; |
| memory_info.push_back(mbi); |
| |
| mbi.BaseAddress = 12; |
| mbi.RegionSize = 5; |
| mbi.State = MEM_COMMIT; |
| memory_info.push_back(mbi); |
| |
| std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = |
| GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(11, 4), |
| memory_info); |
| |
| ASSERT_EQ(result.size(), 1u); |
| EXPECT_EQ(result[0].base(), 11u); |
| EXPECT_EQ(result[0].size(), 4u); |
| } |
| |
| TEST(ProcessInfo, AccessibleRangesMiddleUnavailable) { |
| ProcessInfo::MemoryBasicInformation64Vector memory_info; |
| MEMORY_BASIC_INFORMATION64 mbi = {0}; |
| |
| mbi.BaseAddress = 0; |
| mbi.RegionSize = 10; |
| mbi.State = MEM_COMMIT; |
| memory_info.push_back(mbi); |
| |
| mbi.BaseAddress = 10; |
| mbi.RegionSize = 5; |
| mbi.State = MEM_FREE; |
| memory_info.push_back(mbi); |
| |
| mbi.BaseAddress = 15; |
| mbi.RegionSize = 100; |
| mbi.State = MEM_COMMIT; |
| memory_info.push_back(mbi); |
| |
| std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = |
| GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 45), |
| memory_info); |
| |
| ASSERT_EQ(result.size(), 2u); |
| EXPECT_EQ(result[0].base(), 5u); |
| EXPECT_EQ(result[0].size(), 5u); |
| EXPECT_EQ(result[1].base(), 15u); |
| EXPECT_EQ(result[1].size(), 35u); |
| } |
| |
| TEST(ProcessInfo, RequestedBeforeMap) { |
| ProcessInfo::MemoryBasicInformation64Vector memory_info; |
| MEMORY_BASIC_INFORMATION64 mbi = {0}; |
| |
| mbi.BaseAddress = 10; |
| mbi.RegionSize = 10; |
| mbi.State = MEM_COMMIT; |
| memory_info.push_back(mbi); |
| |
| std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = |
| GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10), |
| memory_info); |
| |
| ASSERT_EQ(result.size(), 1u); |
| EXPECT_EQ(result[0].base(), 10u); |
| EXPECT_EQ(result[0].size(), 5u); |
| } |
| |
| TEST(ProcessInfo, RequestedAfterMap) { |
| ProcessInfo::MemoryBasicInformation64Vector memory_info; |
| MEMORY_BASIC_INFORMATION64 mbi = {0}; |
| |
| mbi.BaseAddress = 10; |
| mbi.RegionSize = 10; |
| mbi.State = MEM_COMMIT; |
| memory_info.push_back(mbi); |
| |
| std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = |
| GetReadableRangesOfMemoryMap( |
| CheckedRange<WinVMAddress, WinVMSize>(15, 100), memory_info); |
| |
| ASSERT_EQ(result.size(), 1u); |
| EXPECT_EQ(result[0].base(), 15u); |
| EXPECT_EQ(result[0].size(), 5u); |
| } |
| |
| TEST(ProcessInfo, ReadableRanges) { |
| SYSTEM_INFO system_info; |
| GetSystemInfo(&system_info); |
| |
| const size_t kBlockSize = system_info.dwPageSize; |
| |
| // Allocate 6 pages, and then commit the second, fourth, and fifth, and mark |
| // two as committed, but PAGE_NOACCESS, so we have a setup like this: |
| // 0 1 2 3 4 5 |
| // +-----------------------------------------------+ |
| // | ????? | | xxxxx | | | ????? | |
| // +-----------------------------------------------+ |
| void* reserve_region = |
| VirtualAlloc(nullptr, kBlockSize * 6, MEM_RESERVE, PAGE_READWRITE); |
| ASSERT_TRUE(reserve_region); |
| uintptr_t reserved_as_int = reinterpret_cast<uintptr_t>(reserve_region); |
| void* readable1 = |
| VirtualAlloc(reinterpret_cast<void*>(reserved_as_int + kBlockSize), |
| kBlockSize, |
| MEM_COMMIT, |
| PAGE_READWRITE); |
| ASSERT_TRUE(readable1); |
| void* readable2 = |
| VirtualAlloc(reinterpret_cast<void*>(reserved_as_int + (kBlockSize * 3)), |
| kBlockSize * 2, |
| MEM_COMMIT, |
| PAGE_READWRITE); |
| ASSERT_TRUE(readable2); |
| |
| void* no_access = |
| VirtualAlloc(reinterpret_cast<void*>(reserved_as_int + (kBlockSize * 2)), |
| kBlockSize, |
| MEM_COMMIT, |
| PAGE_NOACCESS); |
| ASSERT_TRUE(no_access); |
| |
| HANDLE current_process = GetCurrentProcess(); |
| ProcessInfo info; |
| info.Initialize(current_process); |
| auto ranges = info.GetReadableRanges( |
| CheckedRange<WinVMAddress, WinVMSize>(reserved_as_int, kBlockSize * 6)); |
| |
| ASSERT_EQ(ranges.size(), 2u); |
| EXPECT_EQ(ranges[0].base(), reserved_as_int + kBlockSize); |
| EXPECT_EQ(ranges[0].size(), kBlockSize); |
| EXPECT_EQ(ranges[1].base(), reserved_as_int + (kBlockSize * 3)); |
| EXPECT_EQ(ranges[1].size(), kBlockSize * 2); |
| |
| // Also make sure what we think we can read corresponds with what we can |
| // actually read. |
| std::unique_ptr<unsigned char[]> into(new unsigned char[kBlockSize * 6]); |
| SIZE_T bytes_read; |
| |
| EXPECT_TRUE(ReadProcessMemory( |
| current_process, readable1, into.get(), kBlockSize, &bytes_read)); |
| EXPECT_EQ(bytes_read, kBlockSize); |
| |
| EXPECT_TRUE(ReadProcessMemory( |
| current_process, readable2, into.get(), kBlockSize * 2, &bytes_read)); |
| EXPECT_EQ(bytes_read, kBlockSize * 2); |
| |
| EXPECT_FALSE(ReadProcessMemory( |
| current_process, no_access, into.get(), kBlockSize, &bytes_read)); |
| EXPECT_FALSE(ReadProcessMemory( |
| current_process, reserve_region, into.get(), kBlockSize, &bytes_read)); |
| EXPECT_FALSE(ReadProcessMemory(current_process, |
| reserve_region, |
| into.get(), |
| kBlockSize * 6, |
| &bytes_read)); |
| } |
| |
| TEST(ProcessInfo, Handles) { |
| ScopedTempDir temp_dir; |
| |
| ScopedFileHandle file(LoggingOpenFileForWrite( |
| temp_dir.path().Append(FILE_PATH_LITERAL("test_file")), |
| FileWriteMode::kTruncateOrCreate, |
| FilePermissions::kWorldReadable)); |
| ASSERT_TRUE(file.is_valid()); |
| |
| SECURITY_ATTRIBUTES security_attributes = {0}; |
| security_attributes.nLength = sizeof(security_attributes); |
| security_attributes.bInheritHandle = true; |
| ScopedFileHandle inherited_file(CreateFile( |
| temp_dir.path().Append(FILE_PATH_LITERAL("inheritable")).value().c_str(), |
| GENERIC_WRITE, |
| 0, |
| &security_attributes, |
| CREATE_NEW, |
| FILE_ATTRIBUTE_NORMAL, |
| nullptr)); |
| ASSERT_TRUE(inherited_file.is_valid()); |
| |
| HKEY key; |
| ASSERT_EQ(RegOpenKeyEx( |
| HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft", 0, KEY_READ, &key), |
| ERROR_SUCCESS); |
| ScopedRegistryKey scoped_key(key); |
| ASSERT_TRUE(scoped_key.is_valid()); |
| |
| std::wstring mapping_name = |
| base::UTF8ToUTF16(base::StringPrintf("Local\\test_mapping_%lu_%s", |
| GetCurrentProcessId(), |
| RandomString().c_str())); |
| ScopedKernelHANDLE mapping(CreateFileMapping(INVALID_HANDLE_VALUE, |
| nullptr, |
| PAGE_READWRITE, |
| 0, |
| 1024, |
| mapping_name.c_str())); |
| ASSERT_TRUE(mapping.is_valid()) << ErrorMessage("CreateFileMapping"); |
| |
| ProcessInfo info; |
| info.Initialize(GetCurrentProcess()); |
| bool found_file_handle = false; |
| bool found_inherited_file_handle = false; |
| bool found_key_handle = false; |
| bool found_mapping_handle = false; |
| for (auto handle : info.Handles()) { |
| if (handle.handle == HandleToInt(file.get())) { |
| EXPECT_FALSE(found_file_handle); |
| found_file_handle = true; |
| EXPECT_EQ(handle.type_name, L"File"); |
| EXPECT_EQ(handle.handle_count, 1u); |
| EXPECT_NE(handle.pointer_count, 0u); |
| EXPECT_EQ(handle.granted_access & STANDARD_RIGHTS_ALL, |
| static_cast<uint32_t>(STANDARD_RIGHTS_READ | |
| STANDARD_RIGHTS_WRITE | SYNCHRONIZE)); |
| EXPECT_EQ(handle.attributes, 0u); |
| } |
| if (handle.handle == HandleToInt(inherited_file.get())) { |
| EXPECT_FALSE(found_inherited_file_handle); |
| found_inherited_file_handle = true; |
| EXPECT_EQ(handle.type_name, L"File"); |
| EXPECT_EQ(handle.handle_count, 1u); |
| EXPECT_NE(handle.pointer_count, 0u); |
| EXPECT_EQ(handle.granted_access & STANDARD_RIGHTS_ALL, |
| static_cast<uint32_t>(STANDARD_RIGHTS_READ | |
| STANDARD_RIGHTS_WRITE | SYNCHRONIZE)); |
| |
| // OBJ_INHERIT from ntdef.h, but including that conflicts with other |
| // headers. |
| constexpr uint32_t kObjInherit = 0x2; |
| EXPECT_EQ(handle.attributes, kObjInherit); |
| } |
| if (handle.handle == HandleToInt(scoped_key.get())) { |
| EXPECT_FALSE(found_key_handle); |
| found_key_handle = true; |
| EXPECT_EQ(handle.type_name, L"Key"); |
| EXPECT_EQ(handle.handle_count, 1u); |
| EXPECT_NE(handle.pointer_count, 0u); |
| EXPECT_EQ(handle.granted_access & STANDARD_RIGHTS_ALL, |
| static_cast<uint32_t>(STANDARD_RIGHTS_READ)); |
| EXPECT_EQ(handle.attributes, 0u); |
| } |
| if (handle.handle == HandleToInt(mapping.get())) { |
| EXPECT_FALSE(found_mapping_handle); |
| found_mapping_handle = true; |
| EXPECT_EQ(handle.type_name, L"Section"); |
| EXPECT_EQ(handle.handle_count, 1u); |
| EXPECT_NE(handle.pointer_count, 0u); |
| EXPECT_EQ(handle.granted_access & STANDARD_RIGHTS_ALL, |
| static_cast<uint32_t>(DELETE | READ_CONTROL | WRITE_DAC | |
| WRITE_OWNER | STANDARD_RIGHTS_READ | |
| STANDARD_RIGHTS_WRITE)); |
| EXPECT_EQ(handle.attributes, 0u); |
| } |
| } |
| EXPECT_TRUE(found_file_handle); |
| EXPECT_TRUE(found_inherited_file_handle); |
| EXPECT_TRUE(found_key_handle); |
| EXPECT_TRUE(found_mapping_handle); |
| } |
| |
| TEST(ProcessInfo, OutOfRangeCheck) { |
| constexpr size_t kAllocationSize = 12345; |
| std::unique_ptr<char[]> safe_memory(new char[kAllocationSize]); |
| |
| ProcessInfo info; |
| info.Initialize(GetCurrentProcess()); |
| |
| EXPECT_TRUE( |
| info.LoggingRangeIsFullyReadable(CheckedRange<WinVMAddress, WinVMSize>( |
| FromPointerCast<WinVMAddress>(safe_memory.get()), kAllocationSize))); |
| EXPECT_FALSE(info.LoggingRangeIsFullyReadable( |
| CheckedRange<WinVMAddress, WinVMSize>(0, 1024))); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace crashpad |