2015-03-05 22:07:38 -08:00
|
|
|
// 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"
|
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
#include <dbghelp.h>
|
2015-09-25 21:11:04 -07:00
|
|
|
#include <intrin.h>
|
2015-03-05 22:07:38 -08:00
|
|
|
#include <wchar.h>
|
|
|
|
|
|
|
|
#include "base/files/file_path.h"
|
2015-10-01 15:28:40 -07:00
|
|
|
#include "base/memory/scoped_ptr.h"
|
2015-10-16 15:31:32 -07:00
|
|
|
#include "base/rand_util.h"
|
|
|
|
#include "base/strings/stringprintf.h"
|
|
|
|
#include "base/strings/utf_string_conversions.h"
|
2015-03-05 22:07:38 -08:00
|
|
|
#include "build/build_config.h"
|
|
|
|
#include "gtest/gtest.h"
|
2015-10-16 15:31:32 -07:00
|
|
|
#include "test/scoped_temp_dir.h"
|
test: Move util/test to its own top-level directory, test.
After 9e79ea1da719, it no longer makes sense for crashpad_util_test_lib
to “hide” in util/util_test.gyp. All of util/test is moved to its own
top-level directory, test, which all other test code is allowed to
depend on. test, too, is allowed to depend on all other non-test code.
In a future change, when crashpad_util_test_lib gains a dependency on
crashpad_client, it won’t look so weird for something in util (even
though it’s in util/test) to depend on something in client, because the
thing that needs to depend on client will live in test, not util.
BUG=crashpad:33
R=scottmg@chromium.org
Review URL: https://codereview.chromium.org/1051533002
2015-03-31 17:44:14 -04:00
|
|
|
#include "test/paths.h"
|
2015-09-20 11:16:31 -07:00
|
|
|
#include "test/win/child_launcher.h"
|
2015-05-01 13:48:23 -07:00
|
|
|
#include "util/file/file_io.h"
|
2015-03-05 22:07:38 -08:00
|
|
|
#include "util/misc/uuid.h"
|
|
|
|
#include "util/win/scoped_handle.h"
|
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
namespace test {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
const wchar_t kNtdllName[] = L"\\ntdll.dll";
|
|
|
|
|
2015-09-14 11:07:59 -07:00
|
|
|
bool IsProcessWow64(HANDLE process_handle) {
|
|
|
|
static decltype(IsWow64Process)* is_wow64_process =
|
|
|
|
reinterpret_cast<decltype(IsWow64Process)*>(
|
|
|
|
GetProcAddress(LoadLibrary(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;
|
|
|
|
}
|
|
|
|
|
2015-09-25 21:11:04 -07:00
|
|
|
void VerifyAddressInInCodePage(const ProcessInfo& process_info,
|
|
|
|
WinVMAddress code_address) {
|
|
|
|
// Make sure the child code address is an code page address with the right
|
|
|
|
// information.
|
2015-10-07 12:23:08 -07:00
|
|
|
const std::vector<MEMORY_BASIC_INFORMATION64>& memory_info =
|
|
|
|
process_info.MemoryInfo();
|
2015-09-25 21:11:04 -07:00
|
|
|
bool found_region = false;
|
|
|
|
for (const auto& mi : memory_info) {
|
2015-10-07 12:23:08 -07:00
|
|
|
if (mi.BaseAddress <= code_address &&
|
|
|
|
mi.BaseAddress + mi.RegionSize > code_address) {
|
|
|
|
EXPECT_EQ(MEM_COMMIT, mi.State);
|
|
|
|
EXPECT_EQ(PAGE_EXECUTE_READ, mi.Protect);
|
|
|
|
EXPECT_EQ(MEM_IMAGE, mi.Type);
|
2015-09-25 21:11:04 -07:00
|
|
|
EXPECT_FALSE(found_region);
|
|
|
|
found_region = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPECT_TRUE(found_region);
|
|
|
|
}
|
|
|
|
|
2015-03-05 22:07:38 -08:00
|
|
|
TEST(ProcessInfo, Self) {
|
|
|
|
ProcessInfo process_info;
|
|
|
|
ASSERT_TRUE(process_info.Initialize(GetCurrentProcess()));
|
|
|
|
EXPECT_EQ(GetCurrentProcessId(), process_info.ProcessID());
|
|
|
|
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());
|
2015-09-14 11:07:59 -07:00
|
|
|
if (IsProcessWow64(GetCurrentProcess()))
|
|
|
|
EXPECT_TRUE(process_info.IsWow64());
|
|
|
|
else
|
|
|
|
EXPECT_FALSE(process_info.IsWow64());
|
2015-03-05 22:07:38 -08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
std::wstring command_line;
|
|
|
|
EXPECT_TRUE(process_info.CommandLine(&command_line));
|
|
|
|
EXPECT_EQ(std::wstring(GetCommandLine()), command_line);
|
|
|
|
|
2015-05-01 13:48:23 -07:00
|
|
|
std::vector<ProcessInfo::Module> modules;
|
2015-03-05 22:07:38 -08:00
|
|
|
EXPECT_TRUE(process_info.Modules(&modules));
|
|
|
|
ASSERT_GE(modules.size(), 2u);
|
2015-03-08 16:25:34 -04:00
|
|
|
const wchar_t kSelfName[] = L"\\crashpad_util_test.exe";
|
2015-05-01 13:48:23 -07:00
|
|
|
ASSERT_GE(modules[0].name.size(), wcslen(kSelfName));
|
2015-03-05 22:07:38 -08:00
|
|
|
EXPECT_EQ(kSelfName,
|
2015-05-01 13:48:23 -07:00
|
|
|
modules[0].name.substr(modules[0].name.size() - wcslen(kSelfName)));
|
|
|
|
ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName));
|
|
|
|
EXPECT_EQ(
|
|
|
|
kNtdllName,
|
|
|
|
modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName)));
|
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
EXPECT_EQ(reinterpret_cast<uintptr_t>(GetModuleHandle(nullptr)),
|
|
|
|
modules[0].dll_base);
|
|
|
|
EXPECT_EQ(reinterpret_cast<uintptr_t>(GetModuleHandle(L"ntdll.dll")),
|
|
|
|
modules[1].dll_base);
|
2015-05-01 13:48:23 -07:00
|
|
|
|
|
|
|
EXPECT_GT(modules[0].size, 0);
|
|
|
|
EXPECT_GT(modules[1].size, 0);
|
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
EXPECT_EQ(GetTimestampForLoadedLibrary(GetModuleHandle(nullptr)),
|
|
|
|
modules[0].timestamp);
|
2015-05-01 13:48:23 -07:00
|
|
|
// 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);
|
2015-09-25 21:11:04 -07:00
|
|
|
|
|
|
|
// Find something we know is a code address and confirm expected memory
|
|
|
|
// information settings.
|
|
|
|
VerifyAddressInInCodePage(process_info,
|
|
|
|
reinterpret_cast<WinVMAddress>(_ReturnAddress()));
|
2015-03-05 22:07:38 -08:00
|
|
|
}
|
|
|
|
|
2015-09-18 16:06:05 -07:00
|
|
|
void TestOtherProcess(const base::string16& directory_modification) {
|
2015-03-05 22:07:38 -08:00
|
|
|
ProcessInfo process_info;
|
|
|
|
|
2015-09-20 11:16:31 -07:00
|
|
|
UUID done_uuid(UUID::InitializeWithNewTag{});
|
2015-03-05 22:07:38 -08:00
|
|
|
|
|
|
|
ScopedKernelHANDLE done(
|
|
|
|
CreateEvent(nullptr, true, false, done_uuid.ToString16().c_str()));
|
|
|
|
ASSERT_TRUE(done.get());
|
|
|
|
|
2015-03-09 15:13:13 -04:00
|
|
|
base::FilePath test_executable = Paths::Executable();
|
2015-09-18 16:06:05 -07:00
|
|
|
|
2015-03-05 22:07:38 -08:00
|
|
|
std::wstring child_test_executable =
|
2015-09-18 16:06:05 -07:00
|
|
|
test_executable.DirName()
|
|
|
|
.Append(directory_modification)
|
|
|
|
.Append(test_executable.BaseName().RemoveFinalExtension().value() +
|
|
|
|
L"_process_info_test_child.exe")
|
|
|
|
.value();
|
2015-09-22 09:20:23 -07:00
|
|
|
|
|
|
|
std::wstring args;
|
|
|
|
AppendCommandLineArgument(done_uuid.ToString16(), &args);
|
|
|
|
|
|
|
|
ChildLauncher child(child_test_executable, args);
|
2015-09-20 11:16:31 -07:00
|
|
|
child.Start();
|
2015-03-05 22:07:38 -08:00
|
|
|
|
2015-09-25 21:11:04 -07:00
|
|
|
// The child sends us a code address we can look up in the memory map.
|
|
|
|
WinVMAddress code_address;
|
|
|
|
CheckedReadFile(
|
|
|
|
child.stdout_read_handle(), &code_address, sizeof(code_address));
|
2015-03-05 22:07:38 -08:00
|
|
|
|
2015-09-20 11:16:31 -07:00
|
|
|
ASSERT_TRUE(process_info.Initialize(child.process_handle()));
|
2015-03-05 22:07:38 -08:00
|
|
|
|
|
|
|
// Tell the test it's OK to shut down now that we've read our data.
|
2015-09-25 21:11:04 -07:00
|
|
|
EXPECT_TRUE(SetEvent(done.get()));
|
2015-03-05 22:07:38 -08:00
|
|
|
|
2015-05-01 13:48:23 -07:00
|
|
|
std::vector<ProcessInfo::Module> modules;
|
2015-03-05 22:07:38 -08:00
|
|
|
EXPECT_TRUE(process_info.Modules(&modules));
|
|
|
|
ASSERT_GE(modules.size(), 3u);
|
2015-09-18 16:06:05 -07:00
|
|
|
std::wstring child_name = L"\\crashpad_util_test_process_info_test_child.exe";
|
2015-05-01 13:48:23 -07:00
|
|
|
ASSERT_GE(modules[0].name.size(), child_name.size());
|
2015-03-09 16:37:43 -07:00
|
|
|
EXPECT_EQ(child_name,
|
2015-05-01 13:48:23 -07:00
|
|
|
modules[0].name.substr(modules[0].name.size() - child_name.size()));
|
|
|
|
ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName));
|
|
|
|
EXPECT_EQ(
|
|
|
|
kNtdllName,
|
|
|
|
modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName)));
|
2015-03-05 22:07:38 -08:00
|
|
|
// lz32.dll is an uncommonly-used-but-always-available module that the test
|
|
|
|
// binary manually loads.
|
|
|
|
const wchar_t kLz32dllName[] = L"\\lz32.dll";
|
2015-05-01 13:48:23 -07:00
|
|
|
ASSERT_GE(modules.back().name.size(), wcslen(kLz32dllName));
|
|
|
|
EXPECT_EQ(kLz32dllName,
|
|
|
|
modules.back().name.substr(modules.back().name.size() -
|
|
|
|
wcslen(kLz32dllName)));
|
2015-09-25 21:11:04 -07:00
|
|
|
|
|
|
|
VerifyAddressInInCodePage(process_info, code_address);
|
2015-03-05 22:07:38 -08:00
|
|
|
}
|
|
|
|
|
2015-09-18 16:06:05 -07:00
|
|
|
TEST(ProcessInfo, OtherProcess) {
|
|
|
|
TestOtherProcess(FILE_PATH_LITERAL("."));
|
2015-03-09 16:37:43 -07:00
|
|
|
}
|
|
|
|
|
2015-09-18 16:06:05 -07:00
|
|
|
#if defined(ARCH_CPU_64_BITS)
|
|
|
|
TEST(ProcessInfo, OtherProcessWOW64) {
|
|
|
|
#ifndef NDEBUG
|
|
|
|
TestOtherProcess(FILE_PATH_LITERAL("..\\..\\out\\Debug"));
|
|
|
|
#else
|
|
|
|
TestOtherProcess(FILE_PATH_LITERAL("..\\..\\out\\Release"));
|
|
|
|
#endif
|
2015-03-09 16:37:43 -07:00
|
|
|
}
|
2015-09-18 16:06:05 -07:00
|
|
|
#endif // ARCH_CPU_64_BITS
|
2015-03-09 16:37:43 -07:00
|
|
|
|
2015-10-01 11:47:32 -07:00
|
|
|
TEST(ProcessInfo, AccessibleRangesNone) {
|
2015-10-07 12:23:08 -07:00
|
|
|
std::vector<MEMORY_BASIC_INFORMATION64> memory_info;
|
|
|
|
MEMORY_BASIC_INFORMATION64 mbi = {0};
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
mbi.BaseAddress = 0;
|
|
|
|
mbi.RegionSize = 10;
|
|
|
|
mbi.State = MEM_FREE;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
|
|
|
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(2, 4),
|
|
|
|
memory_info);
|
|
|
|
|
|
|
|
EXPECT_TRUE(result.empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ProcessInfo, AccessibleRangesOneInside) {
|
2015-10-07 12:23:08 -07:00
|
|
|
std::vector<MEMORY_BASIC_INFORMATION64> memory_info;
|
|
|
|
MEMORY_BASIC_INFORMATION64 mbi = {0};
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
mbi.BaseAddress = 0;
|
|
|
|
mbi.RegionSize = 10;
|
|
|
|
mbi.State = MEM_COMMIT;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
|
|
|
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(2, 4),
|
|
|
|
memory_info);
|
|
|
|
|
2015-10-01 15:28:40 -07:00
|
|
|
ASSERT_EQ(1u, result.size());
|
2015-10-01 11:47:32 -07:00
|
|
|
EXPECT_EQ(2, result[0].base());
|
|
|
|
EXPECT_EQ(4, result[0].size());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ProcessInfo, AccessibleRangesOneTruncatedSize) {
|
2015-10-07 12:23:08 -07:00
|
|
|
std::vector<MEMORY_BASIC_INFORMATION64> memory_info;
|
|
|
|
MEMORY_BASIC_INFORMATION64 mbi = {0};
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
mbi.BaseAddress = 0;
|
|
|
|
mbi.RegionSize = 10;
|
|
|
|
mbi.State = MEM_COMMIT;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
mbi.BaseAddress = 10;
|
2015-10-01 11:47:32 -07:00
|
|
|
mbi.RegionSize = 20;
|
|
|
|
mbi.State = MEM_FREE;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
|
|
|
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
|
|
|
|
memory_info);
|
|
|
|
|
2015-10-01 15:28:40 -07:00
|
|
|
ASSERT_EQ(1u, result.size());
|
2015-10-01 11:47:32 -07:00
|
|
|
EXPECT_EQ(5, result[0].base());
|
|
|
|
EXPECT_EQ(5, result[0].size());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ProcessInfo, AccessibleRangesOneMovedStart) {
|
2015-10-07 12:23:08 -07:00
|
|
|
std::vector<MEMORY_BASIC_INFORMATION64> memory_info;
|
|
|
|
MEMORY_BASIC_INFORMATION64 mbi = {0};
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
mbi.BaseAddress = 0;
|
|
|
|
mbi.RegionSize = 10;
|
|
|
|
mbi.State = MEM_FREE;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
mbi.BaseAddress = 10;
|
2015-10-01 11:47:32 -07:00
|
|
|
mbi.RegionSize = 20;
|
|
|
|
mbi.State = MEM_COMMIT;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
|
|
|
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
|
|
|
|
memory_info);
|
|
|
|
|
2015-10-01 15:28:40 -07:00
|
|
|
ASSERT_EQ(1u, result.size());
|
|
|
|
EXPECT_EQ(10, result[0].base());
|
|
|
|
EXPECT_EQ(5, result[0].size());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ProcessInfo, ReserveIsInaccessible) {
|
2015-10-07 12:23:08 -07:00
|
|
|
std::vector<MEMORY_BASIC_INFORMATION64> memory_info;
|
|
|
|
MEMORY_BASIC_INFORMATION64 mbi = {0};
|
2015-10-01 15:28:40 -07:00
|
|
|
|
|
|
|
mbi.BaseAddress = 0;
|
|
|
|
mbi.RegionSize = 10;
|
|
|
|
mbi.State = MEM_RESERVE;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 15:28:40 -07:00
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
mbi.BaseAddress = 10;
|
2015-10-01 15:28:40 -07:00
|
|
|
mbi.RegionSize = 20;
|
|
|
|
mbi.State = MEM_COMMIT;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 15:28:40 -07:00
|
|
|
|
|
|
|
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
|
|
|
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
|
|
|
|
memory_info);
|
|
|
|
|
|
|
|
ASSERT_EQ(1u, result.size());
|
|
|
|
EXPECT_EQ(10, result[0].base());
|
|
|
|
EXPECT_EQ(5, result[0].size());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ProcessInfo, PageGuardIsInaccessible) {
|
2015-10-07 12:23:08 -07:00
|
|
|
std::vector<MEMORY_BASIC_INFORMATION64> memory_info;
|
|
|
|
MEMORY_BASIC_INFORMATION64 mbi = {0};
|
2015-10-01 15:28:40 -07:00
|
|
|
|
|
|
|
mbi.BaseAddress = 0;
|
|
|
|
mbi.RegionSize = 10;
|
|
|
|
mbi.State = MEM_COMMIT;
|
|
|
|
mbi.Protect = PAGE_GUARD;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 15:28:40 -07:00
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
mbi.BaseAddress = 10;
|
2015-10-01 15:28:40 -07:00
|
|
|
mbi.RegionSize = 20;
|
|
|
|
mbi.State = MEM_COMMIT;
|
|
|
|
mbi.Protect = 0;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 15:28:40 -07:00
|
|
|
|
|
|
|
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
|
|
|
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
|
|
|
|
memory_info);
|
|
|
|
|
|
|
|
ASSERT_EQ(1u, result.size());
|
|
|
|
EXPECT_EQ(10, result[0].base());
|
|
|
|
EXPECT_EQ(5, result[0].size());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ProcessInfo, PageNoAccessIsInaccessible) {
|
2015-10-07 12:23:08 -07:00
|
|
|
std::vector<MEMORY_BASIC_INFORMATION64> memory_info;
|
|
|
|
MEMORY_BASIC_INFORMATION64 mbi = {0};
|
2015-10-01 15:28:40 -07:00
|
|
|
|
|
|
|
mbi.BaseAddress = 0;
|
|
|
|
mbi.RegionSize = 10;
|
|
|
|
mbi.State = MEM_COMMIT;
|
|
|
|
mbi.Protect = PAGE_NOACCESS;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 15:28:40 -07:00
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
mbi.BaseAddress = 10;
|
2015-10-01 15:28:40 -07:00
|
|
|
mbi.RegionSize = 20;
|
|
|
|
mbi.State = MEM_COMMIT;
|
|
|
|
mbi.Protect = 0;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 15:28:40 -07:00
|
|
|
|
|
|
|
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
|
|
|
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
|
|
|
|
memory_info);
|
|
|
|
|
|
|
|
ASSERT_EQ(1u, result.size());
|
2015-10-01 11:47:32 -07:00
|
|
|
EXPECT_EQ(10, result[0].base());
|
|
|
|
EXPECT_EQ(5, result[0].size());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ProcessInfo, AccessibleRangesCoalesced) {
|
2015-10-07 12:23:08 -07:00
|
|
|
std::vector<MEMORY_BASIC_INFORMATION64> memory_info;
|
|
|
|
MEMORY_BASIC_INFORMATION64 mbi = {0};
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
mbi.BaseAddress = 0;
|
|
|
|
mbi.RegionSize = 10;
|
|
|
|
mbi.State = MEM_FREE;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
mbi.BaseAddress = 10;
|
2015-10-01 11:47:32 -07:00
|
|
|
mbi.RegionSize = 2;
|
|
|
|
mbi.State = MEM_COMMIT;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
mbi.BaseAddress = 12;
|
2015-10-01 11:47:32 -07:00
|
|
|
mbi.RegionSize = 5;
|
2015-10-01 15:28:40 -07:00
|
|
|
mbi.State = MEM_COMMIT;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
|
|
|
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(11, 4),
|
|
|
|
memory_info);
|
|
|
|
|
2015-10-01 15:28:40 -07:00
|
|
|
ASSERT_EQ(1u, result.size());
|
2015-10-01 11:47:32 -07:00
|
|
|
EXPECT_EQ(11, result[0].base());
|
|
|
|
EXPECT_EQ(4, result[0].size());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ProcessInfo, AccessibleRangesMiddleUnavailable) {
|
2015-10-07 12:23:08 -07:00
|
|
|
std::vector<MEMORY_BASIC_INFORMATION64> memory_info;
|
|
|
|
MEMORY_BASIC_INFORMATION64 mbi = {0};
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
mbi.BaseAddress = 0;
|
|
|
|
mbi.RegionSize = 10;
|
|
|
|
mbi.State = MEM_COMMIT;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
mbi.BaseAddress = 10;
|
2015-10-01 11:47:32 -07:00
|
|
|
mbi.RegionSize = 5;
|
|
|
|
mbi.State = MEM_FREE;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
mbi.BaseAddress = 15;
|
2015-10-01 11:47:32 -07:00
|
|
|
mbi.RegionSize = 100;
|
|
|
|
mbi.State = MEM_COMMIT;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
|
|
|
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 45),
|
|
|
|
memory_info);
|
|
|
|
|
2015-10-01 15:28:40 -07:00
|
|
|
ASSERT_EQ(2u, result.size());
|
2015-10-01 11:47:32 -07:00
|
|
|
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) {
|
2015-10-07 12:23:08 -07:00
|
|
|
std::vector<MEMORY_BASIC_INFORMATION64> memory_info;
|
|
|
|
MEMORY_BASIC_INFORMATION64 mbi = {0};
|
2015-10-01 11:47:32 -07:00
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
mbi.BaseAddress = 10;
|
2015-10-01 11:47:32 -07:00
|
|
|
mbi.RegionSize = 10;
|
|
|
|
mbi.State = MEM_COMMIT;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
|
|
|
GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),
|
|
|
|
memory_info);
|
|
|
|
|
2015-10-01 15:28:40 -07:00
|
|
|
ASSERT_EQ(1u, result.size());
|
2015-10-01 11:47:32 -07:00
|
|
|
EXPECT_EQ(10, result[0].base());
|
|
|
|
EXPECT_EQ(5, result[0].size());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ProcessInfo, RequestedAfterMap) {
|
2015-10-07 12:23:08 -07:00
|
|
|
std::vector<MEMORY_BASIC_INFORMATION64> memory_info;
|
|
|
|
MEMORY_BASIC_INFORMATION64 mbi = {0};
|
2015-10-01 11:47:32 -07:00
|
|
|
|
2015-10-07 12:23:08 -07:00
|
|
|
mbi.BaseAddress = 10;
|
2015-10-01 11:47:32 -07:00
|
|
|
mbi.RegionSize = 10;
|
|
|
|
mbi.State = MEM_COMMIT;
|
2015-10-07 12:23:08 -07:00
|
|
|
memory_info.push_back(mbi);
|
2015-10-01 11:47:32 -07:00
|
|
|
|
|
|
|
std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =
|
|
|
|
GetReadableRangesOfMemoryMap(
|
|
|
|
CheckedRange<WinVMAddress, WinVMSize>(15, 100), memory_info);
|
|
|
|
|
2015-10-01 15:28:40 -07:00
|
|
|
ASSERT_EQ(1u, result.size());
|
2015-10-01 11:47:32 -07:00
|
|
|
EXPECT_EQ(15, result[0].base());
|
|
|
|
EXPECT_EQ(5, result[0].size());
|
|
|
|
}
|
|
|
|
|
2015-10-01 15:28:40 -07:00
|
|
|
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(2u, ranges.size());
|
|
|
|
EXPECT_EQ(reserved_as_int + kBlockSize, ranges[0].base());
|
|
|
|
EXPECT_EQ(kBlockSize, ranges[0].size());
|
|
|
|
EXPECT_EQ(reserved_as_int + (kBlockSize * 3), ranges[1].base());
|
|
|
|
EXPECT_EQ(kBlockSize * 2, ranges[1].size());
|
|
|
|
|
|
|
|
// Also make sure what we think we can read corresponds with what we can
|
|
|
|
// actually read.
|
|
|
|
scoped_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(kBlockSize, bytes_read);
|
|
|
|
|
|
|
|
EXPECT_TRUE(ReadProcessMemory(
|
|
|
|
current_process, readable2, into.get(), kBlockSize * 2, &bytes_read));
|
|
|
|
EXPECT_EQ(kBlockSize * 2, bytes_read);
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2015-10-16 15:31:32 -07:00
|
|
|
struct ScopedRegistryKeyCloseTraits {
|
|
|
|
static HKEY InvalidValue() {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
static void Free(HKEY key) {
|
|
|
|
RegCloseKey(key);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
using ScopedRegistryKey =
|
|
|
|
base::ScopedGeneric<HKEY, ScopedRegistryKeyCloseTraits>;
|
|
|
|
|
|
|
|
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.bInheritHandle = true;
|
|
|
|
ScopedFileHandle inherited_file(CreateFile(L"CONOUT$",
|
|
|
|
GENERIC_WRITE,
|
|
|
|
0,
|
|
|
|
&security_attributes,
|
|
|
|
OPEN_EXISTING,
|
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
|
nullptr));
|
|
|
|
ASSERT_TRUE(inherited_file.is_valid());
|
|
|
|
|
|
|
|
HKEY key;
|
|
|
|
ASSERT_EQ(ERROR_SUCCESS,
|
|
|
|
RegOpenKeyEx(
|
|
|
|
HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft", 0, KEY_READ, &key));
|
|
|
|
ScopedRegistryKey scoped_key(key);
|
|
|
|
ASSERT_TRUE(scoped_key.is_valid());
|
|
|
|
|
|
|
|
std::wstring mapping_name =
|
|
|
|
base::UTF8ToUTF16(base::StringPrintf("Global\\test_mapping_%d_%I64x",
|
|
|
|
GetCurrentProcessId(),
|
|
|
|
base::RandUint64()));
|
|
|
|
ScopedKernelHANDLE mapping(CreateFileMapping(INVALID_HANDLE_VALUE,
|
|
|
|
nullptr,
|
|
|
|
PAGE_READWRITE,
|
|
|
|
0,
|
|
|
|
1024,
|
|
|
|
mapping_name.c_str()));
|
|
|
|
ASSERT_TRUE(mapping.is_valid());
|
|
|
|
|
|
|
|
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 (reinterpret_cast<uint32_t>(file.get()) == handle.handle) {
|
|
|
|
EXPECT_FALSE(found_file_handle);
|
|
|
|
found_file_handle = true;
|
|
|
|
EXPECT_EQ(L"File", handle.type_name);
|
|
|
|
EXPECT_EQ(1, handle.handle_count);
|
|
|
|
EXPECT_NE(0u, handle.pointer_count);
|
|
|
|
EXPECT_EQ(STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | SYNCHRONIZE,
|
|
|
|
handle.granted_access & STANDARD_RIGHTS_ALL);
|
|
|
|
EXPECT_EQ(0, handle.attributes);
|
|
|
|
}
|
|
|
|
if (reinterpret_cast<uint32_t>(inherited_file.get()) == handle.handle) {
|
|
|
|
EXPECT_FALSE(found_inherited_file_handle);
|
|
|
|
found_inherited_file_handle = true;
|
|
|
|
EXPECT_EQ(L"File", handle.type_name);
|
|
|
|
EXPECT_EQ(1, handle.handle_count);
|
|
|
|
EXPECT_NE(0u, handle.pointer_count);
|
|
|
|
EXPECT_EQ(STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | SYNCHRONIZE,
|
|
|
|
handle.granted_access & STANDARD_RIGHTS_ALL);
|
|
|
|
|
|
|
|
// OBJ_INHERIT from ntdef.h, but including that conflicts with other
|
|
|
|
// headers.
|
|
|
|
const int kObjInherit = 0x2;
|
|
|
|
EXPECT_EQ(kObjInherit, handle.attributes);
|
|
|
|
}
|
|
|
|
if (reinterpret_cast<uint32_t>(scoped_key.get()) == handle.handle) {
|
|
|
|
EXPECT_FALSE(found_key_handle);
|
|
|
|
found_key_handle = true;
|
|
|
|
EXPECT_EQ(L"Key", handle.type_name);
|
|
|
|
EXPECT_EQ(1, handle.handle_count);
|
|
|
|
EXPECT_NE(0u, handle.pointer_count);
|
|
|
|
EXPECT_EQ(STANDARD_RIGHTS_READ,
|
|
|
|
handle.granted_access & STANDARD_RIGHTS_ALL);
|
|
|
|
EXPECT_EQ(0, handle.attributes);
|
|
|
|
}
|
|
|
|
if (reinterpret_cast<uint32_t>(mapping.get()) == handle.handle) {
|
|
|
|
EXPECT_FALSE(found_mapping_handle);
|
|
|
|
found_mapping_handle = true;
|
|
|
|
EXPECT_EQ(L"Section", handle.type_name);
|
|
|
|
EXPECT_EQ(1, handle.handle_count);
|
|
|
|
EXPECT_NE(0u, handle.pointer_count);
|
|
|
|
EXPECT_EQ(DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER |
|
|
|
|
STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE,
|
|
|
|
handle.granted_access & STANDARD_RIGHTS_ALL);
|
|
|
|
EXPECT_EQ(0, handle.attributes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPECT_TRUE(found_file_handle);
|
|
|
|
EXPECT_TRUE(found_key_handle);
|
|
|
|
EXPECT_TRUE(found_mapping_handle);
|
|
|
|
}
|
|
|
|
|
2015-03-05 22:07:38 -08:00
|
|
|
} // namespace
|
|
|
|
} // namespace test
|
|
|
|
} // namespace crashpad
|