crashpad/snapshot/win/pe_image_reader_test.cc
Mark Mentovai 0e3c38a4ca win: Make ProcessSnapshotTest.CrashpadInfoChild use a loaded module
When this test examines a module that doesn’t have a CodeView PDB link,
it will fail. Such a link may be missing when linking with Lexan
ld-link.exe without /DEBUG. The test had been examining the executable
as its module. Since it’s easier to provide a single small module linked
with /DEBUG than it is to require that the test executable always be
linked with /DEBUG, the test is revised to always load a module and
operate on it. The module used is the existing
crashpad_snapshot_test_image_reader_module.dll. It was chosen because
it’s also used by PEImageReader.DebugDirectory, which also requires a
CodeView PDB link.

It’s the build system’s responsibility to ensure that
crashpad_snapshot_test_image_reader_module.dll is linked appropriately.
Crashpad’s own GYP-based build always links with /DEBUG. Chrome’s
GN-based Crashpad build will require additional attention at
symbol_level = 0.

Bug: chromium:782781
Change-Id: I0dda8cd13278b82842263e76bcc46362bd3998df
Reviewed-on: https://chromium-review.googlesource.com/761501
Reviewed-by: Leonard Mosescu <mosescu@chromium.org>
2017-11-09 23:18:51 +00:00

192 lines
7.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 "snapshot/win/pe_image_reader.h"
#define PSAPI_VERSION 1
#include <psapi.h>
#include "base/files/file_path.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "gtest/gtest.h"
#include "snapshot/win/process_reader_win.h"
#include "test/errors.h"
#include "test/scoped_module_handle.h"
#include "test/test_paths.h"
#include "util/misc/from_pointer_cast.h"
#include "util/win/get_module_information.h"
#include "util/win/module_version.h"
#include "util/win/process_info.h"
namespace crashpad {
namespace test {
namespace {
TEST(PEImageReader, DebugDirectory) {
base::FilePath module_path =
TestPaths::BuildArtifact(L"snapshot",
L"image_reader_module",
TestPaths::FileType::kLoadableModule);
ScopedModuleHandle module_handle(LoadLibrary(module_path.value().c_str()));
ASSERT_TRUE(module_handle.valid()) << ErrorMessage("LoadLibrary");
PEImageReader pe_image_reader;
ProcessReaderWin process_reader;
ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),
ProcessSuspensionState::kRunning));
MODULEINFO module_info;
ASSERT_TRUE(CrashpadGetModuleInformation(GetCurrentProcess(),
module_handle.get(),
&module_info,
sizeof(module_info)))
<< ErrorMessage("GetModuleInformation");
EXPECT_EQ(module_info.lpBaseOfDll, module_handle.get());
base::FilePath module_basename = module_path.BaseName();
ASSERT_TRUE(pe_image_reader.Initialize(
&process_reader,
FromPointerCast<WinVMAddress>(module_handle.get()),
module_info.SizeOfImage,
base::UTF16ToUTF8(module_basename.value())));
UUID uuid;
DWORD age;
std::string pdbname;
ASSERT_TRUE(pe_image_reader.DebugDirectoryInformation(&uuid, &age, &pdbname));
std::string module_name =
base::UTF16ToUTF8(module_basename.RemoveFinalExtension().value());
EXPECT_NE(pdbname.find(module_name), std::string::npos);
const std::string suffix(".pdb");
EXPECT_EQ(
pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix),
0);
}
void TestVSFixedFileInfo(ProcessReaderWin* process_reader,
const ProcessInfo::Module& module,
bool known_dll) {
PEImageReader pe_image_reader;
ASSERT_TRUE(pe_image_reader.Initialize(process_reader,
module.dll_base,
module.size,
base::UTF16ToUTF8(module.name)));
VS_FIXEDFILEINFO observed;
const bool observed_rv = pe_image_reader.VSFixedFileInfo(&observed);
ASSERT_TRUE(observed_rv || !known_dll);
if (observed_rv) {
EXPECT_EQ(observed.dwSignature, static_cast<DWORD>(VS_FFI_SIGNATURE));
EXPECT_EQ(observed.dwStrucVersion, static_cast<DWORD>(VS_FFI_STRUCVERSION));
EXPECT_EQ(observed.dwFileFlags & ~observed.dwFileFlagsMask, 0u);
if (known_dll) {
EXPECT_EQ(observed.dwFileOS, static_cast<DWORD>(VOS_NT_WINDOWS32));
EXPECT_EQ(observed.dwFileType, static_cast<DWORD>(VFT_DLL));
} else {
EXPECT_NE(observed.dwFileOS & VOS_NT_WINDOWS32, 0u);
// VFT_DRV/VFT2_DRV_NETWORK is for nsi.dll, “network service interface.”
// Its not normally loaded, but has been observed to be loaded in some
// cases.
EXPECT_TRUE(observed.dwFileType == VFT_APP ||
observed.dwFileType == VFT_DLL ||
(observed.dwFileType == VFT_DRV &&
observed.dwFileSubtype == VFT2_DRV_NETWORK))
<< base::StringPrintf("type 0x%lx, subtype 0x%lx",
observed.dwFileType,
observed.dwFileSubtype);
}
}
base::FilePath module_path(module.name);
const DWORD version = GetVersion();
const int major_version = LOBYTE(LOWORD(version));
const int minor_version = HIBYTE(LOWORD(version));
if (major_version > 6 || (major_version == 6 && minor_version >= 2)) {
// Windows 8 or later.
//
// Use BaseName() to ensure that GetModuleVersionAndType() finds the
// already-loaded module with the specified name. Otherwise, dwFileVersionMS
// may not match. This appears to be related to the changes made in Windows
// 8.1 to GetVersion() and GetVersionEx() for non-manifested applications
module_path = module_path.BaseName();
}
VS_FIXEDFILEINFO expected;
const bool expected_rv = GetModuleVersionAndType(module_path, &expected);
ASSERT_TRUE(expected_rv || !known_dll);
EXPECT_EQ(observed_rv, expected_rv);
if (observed_rv && expected_rv) {
EXPECT_EQ(observed.dwSignature, expected.dwSignature);
EXPECT_EQ(observed.dwStrucVersion, expected.dwStrucVersion);
EXPECT_EQ(observed.dwFileVersionMS, expected.dwFileVersionMS);
EXPECT_EQ(observed.dwFileVersionLS, expected.dwFileVersionLS);
EXPECT_EQ(observed.dwProductVersionMS, expected.dwProductVersionMS);
EXPECT_EQ(observed.dwProductVersionLS, expected.dwProductVersionLS);
EXPECT_EQ(observed.dwFileFlagsMask, expected.dwFileFlagsMask);
EXPECT_EQ(observed.dwFileFlags, expected.dwFileFlags);
EXPECT_EQ(observed.dwFileOS, expected.dwFileOS);
EXPECT_EQ(observed.dwFileType, expected.dwFileType);
EXPECT_EQ(observed.dwFileSubtype, expected.dwFileSubtype);
EXPECT_EQ(observed.dwFileDateMS, expected.dwFileDateMS);
EXPECT_EQ(observed.dwFileDateLS, expected.dwFileDateLS);
}
}
TEST(PEImageReader, VSFixedFileInfo_OneModule) {
ProcessReaderWin process_reader;
ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),
ProcessSuspensionState::kRunning));
static constexpr wchar_t kModuleName[] = L"kernel32.dll";
const HMODULE module_handle = GetModuleHandle(kModuleName);
ASSERT_TRUE(module_handle) << ErrorMessage("GetModuleHandle");
MODULEINFO module_info;
ASSERT_TRUE(CrashpadGetModuleInformation(
GetCurrentProcess(), module_handle, &module_info, sizeof(module_info)))
<< ErrorMessage("GetModuleInformation");
EXPECT_EQ(module_info.lpBaseOfDll, module_handle);
ProcessInfo::Module module;
module.name = kModuleName;
module.dll_base = FromPointerCast<WinVMAddress>(module_info.lpBaseOfDll);
module.size = module_info.SizeOfImage;
TestVSFixedFileInfo(&process_reader, module, true);
}
TEST(PEImageReader, VSFixedFileInfo_AllModules) {
ProcessReaderWin process_reader;
ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),
ProcessSuspensionState::kRunning));
const std::vector<ProcessInfo::Module>& modules = process_reader.Modules();
EXPECT_GT(modules.size(), 2u);
for (const auto& module : modules) {
SCOPED_TRACE(base::UTF16ToUTF8(module.name));
TestVSFixedFileInfo(&process_reader, module, false);
}
}
} // namespace
} // namespace test
} // namespace crashpad