win: make CrashpadInfo retrievable

The main goal was to get the beginnings of module iteration and retrieval
of CrashpadInfo in snapshot. The main change for that is to move
crashpad_info_client_options[_test] down out of mac/.

This also requires adding some of the supporting code of snapshot in
ProcessReaderWin, ProcessSnapshotWin, and ModuleSnapshotWin. These are
partially copied from Mac or stubbed out with lots of TODO annotations.
This is a bit unfortunate, but seemed like the most productive way to
make progress incrementally. That is, it's mostly placeholder at the
moment, but hopefully has the right shape for things to come.

R=mark@chromium.org
BUG=crashpad:1

Review URL: https://codereview.chromium.org/1052813002
This commit is contained in:
Scott Graham 2015-05-01 13:48:23 -07:00
parent 3d216f5516
commit 69d135acda
31 changed files with 1718 additions and 259 deletions

View File

@ -71,8 +71,10 @@ __attribute__((section(SEG_DATA ",__crashpad_info"),
#elif defined(OS_WIN)
// TODO(scottmg): Tag in a way that makes it easy to locate on Windows.
CrashpadInfo g_crashpad_info;
// Put the struct in a section name CPADinfo where it can be found without the
// symbol table.
#pragma section("CPADinfo", read, write)
__declspec(allocate("CPADinfo")) CrashpadInfo g_crashpad_info;
#endif

View File

@ -23,7 +23,7 @@
#include "base/strings/stringprintf.h"
#include "client/settings.h"
#include "minidump/minidump_file_writer.h"
#include "snapshot/mac/crashpad_info_client_options.h"
#include "snapshot/crashpad_info_client_options.h"
#include "snapshot/mac/process_snapshot_mac.h"
#include "util/file/file_writer.h"
#include "util/mach/exc_client_variants.h"

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/mac/crashpad_info_client_options.h"
#include "snapshot/crashpad_info_client_options.h"
#include "base/logging.h"
#include "client/crashpad_info.h"

View File

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_MAC_CRASHPAD_INFO_CLIENT_OPTIONS_H_
#define CRASHPAD_SNAPSHOT_MAC_CRASHPAD_INFO_CLIENT_OPTIONS_H_
#ifndef CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_
#define CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_
#include "util/misc/tri_state.h"
@ -61,4 +61,4 @@ struct CrashpadInfoClientOptions {
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_MAC_CRASHPAD_INFO_CLIENT_OPTIONS_H_
#endif // CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_

View File

@ -12,16 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/mac/crashpad_info_client_options.h"
#include <dlfcn.h>
#include "snapshot/crashpad_info_client_options.h"
#include "base/files/file_path.h"
#include "build/build_config.h"
#include "client/crashpad_info.h"
#include "gtest/gtest.h"
#include "snapshot/mac/process_snapshot_mac.h"
#include "test/errors.h"
#include "test/paths.h"
#if defined(OS_MACOSX)
#include <dlfcn.h>
#include "snapshot/mac/process_snapshot_mac.h"
#elif defined(OS_WIN)
#include <windows.h>
#include "snapshot/win/process_snapshot_win.h"
#endif
namespace crashpad {
namespace test {
namespace {
@ -62,8 +69,15 @@ class ScopedUnsetCrashpadInfoOptions {
TEST(CrashpadInfoClientOptions, OneModule) {
// Make sure that the initial state has all values unset.
#if defined(OS_MACOSX)
ProcessSnapshotMac process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
#elif defined(OS_WIN)
ProcessSnapshotWin process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(GetCurrentProcess()));
#else
#error Port.
#endif // OS_MACOSX
CrashpadInfoClientOptions options;
process_snapshot.GetCrashpadOptions(&options);
@ -95,17 +109,28 @@ TEST(CrashpadInfoClientOptions, OneModule) {
}
}
#if defined(OS_POSIX)
using DlHandle = void*;
#elif defined(OS_WIN)
using DlHandle = HMODULE;
#endif // OS_POSIX
class ScopedDlHandle {
public:
explicit ScopedDlHandle(void* dl_handle)
explicit ScopedDlHandle(DlHandle dl_handle)
: dl_handle_(dl_handle) {
}
~ScopedDlHandle() {
if (dl_handle_) {
#if defined(OS_POSIX)
if (dlclose(dl_handle_) != 0) {
LOG(ERROR) << "dlclose: " << dlerror();
}
#elif defined(OS_WIN)
if (!FreeLibrary(dl_handle_))
PLOG(ERROR) << "FreeLibrary";
#endif // OS_POSIX
}
}
@ -113,23 +138,41 @@ class ScopedDlHandle {
template <typename T>
T LookUpSymbol(const char* symbol_name) {
#if defined(OS_POSIX)
return reinterpret_cast<T>(dlsym(dl_handle_, symbol_name));
#elif defined(OS_WIN)
return reinterpret_cast<T>(GetProcAddress(dl_handle_, symbol_name));
#endif // OS_POSIX
}
private:
void* dl_handle_;
DlHandle dl_handle_;
DISALLOW_COPY_AND_ASSIGN(ScopedDlHandle);
};
TEST(CrashpadInfoClientOptions, TwoModules) {
// Open the module, which has its own CrashpadInfo structure.
base::FilePath module_path =
Paths::Executable().DirName().Append("crashpad_snapshot_test_module.so");
#if defined(OS_MACOSX)
const base::FilePath::StringType kDlExtension = FILE_PATH_LITERAL(".so");
#elif defined(OS_WIN)
const base::FilePath::StringType kDlExtension = FILE_PATH_LITERAL(".dll");
#endif
base::FilePath module_path = Paths::Executable().DirName().Append(
FILE_PATH_LITERAL("crashpad_snapshot_test_module") + kDlExtension);
#if defined(OS_MACOSX)
ScopedDlHandle dl_handle(
dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL));
ASSERT_TRUE(dl_handle.valid()) << "dlopen " << module_path.value() << ": "
<< dlerror();
#elif defined(OS_WIN)
ScopedDlHandle dl_handle(LoadLibrary(module_path.value().c_str()));
ASSERT_TRUE(dl_handle.valid()) << "LoadLibrary "
<< module_path.value().c_str() << ": "
<< ErrorMessage();
#else
#error Port.
#endif // OS_MACOSX
// Get the function pointer from the module. This wraps GetCrashpadInfo(), but
// because it runs in the module, it returns the remote modules CrashpadInfo
@ -139,8 +182,15 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
ASSERT_TRUE(TestModule_GetCrashpadInfo);
// Make sure that the initial state has all values unset.
#if defined(OS_MACOSX)
ProcessSnapshotMac process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
#elif defined(OS_WIN)
ProcessSnapshotWin process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(GetCurrentProcess()));
#else
#error Port.
#endif // OS_MACOSX
CrashpadInfoClientOptions options;
process_snapshot.GetCrashpadOptions(&options);

View File

@ -12,9 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "build/build_config.h"
#include "client/crashpad_info.h"
#if defined(OS_POSIX)
#define EXPORT __attribute__((visibility("default")))
#elif defined(OS_WIN)
#include <windows.h>
#define EXPORT __declspec(dllexport)
#endif // OS_POSIX
extern "C" {
@ -33,3 +39,9 @@ EXPORT crashpad::CrashpadInfo* TestModule_GetCrashpadInfo() {
}
} // extern "C"
#if defined(OS_WIN)
BOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID reserved) {
return TRUE;
}
#endif // OS_WIN

View File

@ -24,7 +24,7 @@
#include "base/basictypes.h"
#include "client/crashpad_info.h"
#include "snapshot/mac/crashpad_info_client_options.h"
#include "snapshot/crashpad_info_client_options.h"
#include "snapshot/mac/process_reader.h"
#include "snapshot/module_snapshot.h"
#include "util/misc/initialization_state_dcheck.h"

View File

@ -26,8 +26,8 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "client/crashpad_info.h"
#include "snapshot/crashpad_info_client_options.h"
#include "snapshot/exception_snapshot.h"
#include "snapshot/mac/crashpad_info_client_options.h"
#include "snapshot/mac/exception_snapshot_mac.h"
#include "snapshot/mac/module_snapshot_mac.h"
#include "snapshot/mac/process_reader.h"

View File

@ -33,11 +33,11 @@
'cpu_architecture.h',
'cpu_context.cc',
'cpu_context.h',
'crashpad_info_client_options.cc',
'crashpad_info_client_options.h',
'exception_snapshot.h',
'mac/cpu_context_mac.cc',
'mac/cpu_context_mac.h',
'mac/crashpad_info_client_options.cc',
'mac/crashpad_info_client_options.h',
'mac/exception_snapshot_mac.cc',
'mac/exception_snapshot_mac.h',
'mac/mach_o_image_annotations_reader.cc',
@ -87,8 +87,14 @@
'process_snapshot.h',
'system_snapshot.h',
'thread_snapshot.h',
'win/module_snapshot_win.cc',
'win/module_snapshot_win.h',
'win/pe_image_reader.cc',
'win/pe_image_reader.h',
'win/process_reader_win.cc',
'win/process_reader_win.h',
'win/process_snapshot_win.cc',
'win/process_snapshot_win.h',
'win/system_snapshot_win.cc',
'win/system_snapshot_win.h',
],

View File

@ -50,6 +50,7 @@
'target_name': 'crashpad_snapshot_test',
'type': 'executable',
'dependencies': [
'crashpad_snapshot_test_module',
'snapshot.gyp:crashpad_snapshot',
'../client/client.gyp:crashpad_client',
'../compat/compat.gyp:crashpad_compat',
@ -64,8 +65,8 @@
],
'sources': [
'cpu_context_test.cc',
'crashpad_info_client_options_test.cc',
'mac/cpu_context_mac_test.cc',
'mac/crashpad_info_client_options_test.cc',
'mac/mach_o_image_annotations_reader_test.cc',
'mac/mach_o_image_reader_test.cc',
'mac/mach_o_image_segment_reader_test.cc',
@ -73,13 +74,11 @@
'mac/process_types_test.cc',
'mac/system_snapshot_mac_test.cc',
'minidump/process_snapshot_minidump_test.cc',
'win/process_reader_win_test.cc',
'win/system_snapshot_win_test.cc',
],
'conditions': [
['OS=="mac"', {
'dependencies': [
'crashpad_snapshot_test_module',
],
'link_settings': {
'libraries': [
'$(SDKROOT)/System/Library/Frameworks/OpenCL.framework',
@ -88,25 +87,19 @@
}],
],
},
],
'conditions': [
['OS=="mac"', {
'targets': [
{
'target_name': 'crashpad_snapshot_test_module',
'type': 'loadable_module',
'dependencies': [
'../client/client.gyp:crashpad_client',
'../third_party/mini_chromium/mini_chromium.gyp:base',
],
'include_dirs': [
'..',
],
'sources': [
'mac/crashpad_info_client_options_test_module.cc',
],
},
{
'target_name': 'crashpad_snapshot_test_module',
'type': 'loadable_module',
'dependencies': [
'../client/client.gyp:crashpad_client',
'../third_party/mini_chromium/mini_chromium.gyp:base',
],
}],
'include_dirs': [
'..',
],
'sources': [
'crashpad_info_client_options_test_module.cc',
],
},
],
}

View File

@ -0,0 +1,136 @@
// 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/module_snapshot_win.h"
#include "base/strings/utf_string_conversions.h"
#include "snapshot/win/pe_image_reader.h"
#include "util/misc/tri_state.h"
#include "util/misc/uuid.h"
namespace crashpad {
namespace internal {
ModuleSnapshotWin::ModuleSnapshotWin()
: ModuleSnapshot(),
name_(),
timestamp_(0),
process_reader_(nullptr),
initialized_() {
}
ModuleSnapshotWin::~ModuleSnapshotWin() {
}
bool ModuleSnapshotWin::Initialize(
ProcessReaderWin* process_reader,
const ProcessInfo::Module& process_reader_module) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
process_reader_ = process_reader;
name_ = base::UTF16ToUTF8(process_reader_module.name);
timestamp_ = process_reader_module.timestamp;
pe_image_reader_.reset(new PEImageReader());
if (!pe_image_reader_->Initialize(process_reader_,
process_reader_module.dll_base,
process_reader_module.size,
name_)) {
return false;
}
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
void ModuleSnapshotWin::GetCrashpadOptions(CrashpadInfoClientOptions* options) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
process_types::CrashpadInfo crashpad_info;
if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) {
options->crashpad_handler_behavior = TriState::kUnset;
options->system_crash_reporter_forwarding = TriState::kUnset;
return;
}
options->crashpad_handler_behavior =
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
crashpad_info.crashpad_handler_behavior);
options->system_crash_reporter_forwarding =
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
crashpad_info.system_crash_reporter_forwarding);
}
std::string ModuleSnapshotWin::Name() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return name_;
}
uint64_t ModuleSnapshotWin::Address() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return pe_image_reader_->Address();
}
uint64_t ModuleSnapshotWin::Size() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return pe_image_reader_->Size();
}
time_t ModuleSnapshotWin::Timestamp() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return timestamp_;
}
void ModuleSnapshotWin::FileVersion(uint16_t* version_0,
uint16_t* version_1,
uint16_t* version_2,
uint16_t* version_3) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
CHECK(false) << "TODO(scottmg)";
}
void ModuleSnapshotWin::SourceVersion(uint16_t* version_0,
uint16_t* version_1,
uint16_t* version_2,
uint16_t* version_3) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
CHECK(false) << "TODO(scottmg)";
}
ModuleSnapshot::ModuleType ModuleSnapshotWin::GetModuleType() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
CHECK(false) << "TODO(scottmg)";
return ModuleSnapshot::ModuleType();
}
void ModuleSnapshotWin::UUID(crashpad::UUID* uuid) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
CHECK(false) << "TODO(scottmg)";
}
std::vector<std::string> ModuleSnapshotWin::AnnotationsVector() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
CHECK(false) << "TODO(scottmg)";
return std::vector<std::string>();
}
std::map<std::string, std::string> ModuleSnapshotWin::AnnotationsSimpleMap()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
CHECK(false) << "TODO(scottmg)";
return std::map<std::string, std::string>();
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,96 @@
// 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.
#ifndef CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_
#define CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_
#include <stdint.h>
#include <sys/types.h>
#include <map>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "snapshot/crashpad_info_client_options.h"
#include "snapshot/module_snapshot.h"
#include "snapshot/win/process_reader_win.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/win/process_info.h"
namespace crashpad {
class PEImageReader;
struct UUID;
namespace internal {
//! \brief A ModuleSnapshot of a code module (binary image) loaded into a
//! running (or crashed) process on a Windows system.
class ModuleSnapshotWin final : public ModuleSnapshot {
public:
ModuleSnapshotWin();
~ModuleSnapshotWin() override;
//! \brief Initializes the object.
//!
//! \param[in] process_reader A ProcessReader for the task containing the
//! module.
//! \param[in] process_reader_module The module within the ProcessReader for
//! which the snapshot should be created.
//!
//! \return `true` if the snapshot could be created, `false` otherwise with
//! an appropriate message logged.
bool Initialize(ProcessReaderWin* process_reader,
const ProcessInfo::Module& process_reader_module);
//! \brief Returns options from the module's CrashpadInfo structure.
//!
//! \param[out] options Options set in the module's CrashpadInfo structure.
void GetCrashpadOptions(CrashpadInfoClientOptions* options);
// ModuleSnapshot:
std::string Name() const override;
uint64_t Address() const override;
uint64_t Size() const override;
time_t Timestamp() const override;
void FileVersion(uint16_t* version_0,
uint16_t* version_1,
uint16_t* version_2,
uint16_t* version_3) const override;
void SourceVersion(uint16_t* version_0,
uint16_t* version_1,
uint16_t* version_2,
uint16_t* version_3) const override;
ModuleType GetModuleType() const override;
void UUID(crashpad::UUID* uuid) const override;
std::vector<std::string> AnnotationsVector() const override;
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
private:
std::string name_;
time_t timestamp_;
scoped_ptr<PEImageReader> pe_image_reader_;
ProcessReaderWin* process_reader_; // weak
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotWin);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_

View File

@ -0,0 +1,182 @@
// 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"
#include <string.h>
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "client/crashpad_info.h"
#include "snapshot/win/process_reader_win.h"
namespace crashpad {
namespace {
std::string RangeToString(const CheckedWinAddressRange& range) {
return base::StringPrintf("[0x%llx + 0x%llx (%s)]",
range.Base(),
range.Size(),
range.Is64Bit() ? "64" : "32");
}
} // namespace
PEImageReader::PEImageReader()
: process_reader_(nullptr),
module_range_(),
module_name_(),
initialized_() {
}
PEImageReader::~PEImageReader() {
}
bool PEImageReader::Initialize(ProcessReaderWin* process_reader,
WinVMAddress address,
WinVMSize size,
const std::string& module_name) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
process_reader_ = process_reader;
module_range_.SetRange(process_reader_->Is64Bit(), address, size);
if (!module_range_.IsValid()) {
LOG(WARNING) << "invalid module range for " << module_name << ": "
<< RangeToString(module_range_);
return false;
}
module_name_ = module_name;
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
bool PEImageReader::GetCrashpadInfo(
process_types::CrashpadInfo* crashpad_info) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
IMAGE_SECTION_HEADER section;
if (!GetSectionByName("CPADinfo", &section))
return false;
if (section.Misc.VirtualSize < sizeof(process_types::CrashpadInfo)) {
LOG(WARNING) << "small crashpad info section size "
<< section.Misc.VirtualSize << ", " << module_name_;
return false;
}
WinVMAddress crashpad_info_address = Address() + section.VirtualAddress;
CheckedWinAddressRange crashpad_info_range(process_reader_->Is64Bit(),
crashpad_info_address,
section.Misc.VirtualSize);
if (!crashpad_info_range.IsValid()) {
LOG(WARNING) << "invalid range for crashpad info: "
<< RangeToString(crashpad_info_range);
return false;
}
if (!module_range_.ContainsRange(crashpad_info_range)) {
LOG(WARNING) << "crashpad info does not fall inside module "
<< module_name_;
return false;
}
// TODO(scottmg): process_types for cross-bitness.
if (!process_reader_->ReadMemory(crashpad_info_address,
sizeof(process_types::CrashpadInfo),
crashpad_info)) {
LOG(WARNING) << "could not read crashpad info " << module_name_;
return false;
}
if (crashpad_info->signature != CrashpadInfo::kSignature ||
crashpad_info->version < 1) {
LOG(WARNING) << "unexpected crashpad info data " << module_name_;
return false;
}
return true;
}
bool PEImageReader::GetSectionByName(const std::string& name,
IMAGE_SECTION_HEADER* section) const {
if (name.size() > sizeof(section->Name)) {
LOG(WARNING) << "supplied section name too long " << name;
return false;
}
IMAGE_DOS_HEADER dos_header;
if (!CheckedReadMemory(Address(), sizeof(IMAGE_DOS_HEADER), &dos_header)) {
LOG(WARNING) << "could not read dos header of " << module_name_;
return false;
}
if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) {
LOG(WARNING) << "invalid e_magic in dos header of " << module_name_;
return false;
}
// TODO(scottmg): This is reading a same-bitness sized structure.
IMAGE_NT_HEADERS nt_headers;
WinVMAddress nt_headers_address = Address() + dos_header.e_lfanew;
if (!CheckedReadMemory(
nt_headers_address, sizeof(IMAGE_NT_HEADERS), &nt_headers)) {
LOG(WARNING) << "could not read nt headers of " << module_name_;
return false;
}
if (nt_headers.Signature != IMAGE_NT_SIGNATURE) {
LOG(WARNING) << "invalid signature in nt headers of " << module_name_;
return false;
}
WinVMAddress first_section_address =
nt_headers_address + offsetof(IMAGE_NT_HEADERS, OptionalHeader) +
nt_headers.FileHeader.SizeOfOptionalHeader;
for (DWORD i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) {
WinVMAddress section_address =
first_section_address + sizeof(IMAGE_SECTION_HEADER) * i;
if (!CheckedReadMemory(
section_address, sizeof(IMAGE_SECTION_HEADER), section)) {
LOG(WARNING) << "could not read section " << i << " of " << module_name_;
return false;
}
if (strncmp(reinterpret_cast<const char*>(section->Name),
name.c_str(),
sizeof(section->Name)) == 0) {
return true;
}
}
return false;
}
bool PEImageReader::CheckedReadMemory(WinVMAddress address,
WinVMSize size,
void* into) const {
CheckedWinAddressRange read_range(process_reader_->Is64Bit(), address, size);
if (!read_range.IsValid()) {
LOG(WARNING) << "invalid read range: " << RangeToString(read_range);
return false;
}
if (!module_range_.ContainsRange(read_range)) {
LOG(WARNING) << "attempt to read outside of module " << module_name_
<< " at range: " << RangeToString(read_range);
return false;
}
return process_reader_->ReadMemory(address, size, into);
}
} // namespace crashpad

View File

@ -0,0 +1,120 @@
// 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.
#ifndef CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_
#define CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_
#include <windows.h>
#include <string>
#include "base/basictypes.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/win/address_types.h"
#include "util/win/checked_win_address_range.h"
namespace crashpad {
class ProcessReaderWin;
namespace process_types {
// TODO(scottmg): Genericize and/or? move process_types out of mac/.
struct CrashpadInfo {
uint32_t signature;
uint32_t size;
uint32_t version;
uint8_t crashpad_handler_behavior; // TriState.
uint8_t system_crash_reporter_forwarding; // TriState.
uint16_t padding_0;
uint64_t simple_annotations; // TODO(scottmg): x86/64.
};
struct Section {
};
} // namespace process_types
//! \brief A reader for PE images mapped into another process.
//!
//! This class is capable of reading both 32-bit and 64-bit images based on the
//! bitness of the remote process.
//!
//! \sa PEImageAnnotationsReader
class PEImageReader {
public:
PEImageReader();
~PEImageReader();
//! \brief Initializes the reader.
//!
//! This method must be called only once on an object. This method must be
//! called successfully before any other method in this class may be called.
//!
//! \param[in] process_reader The reader for the remote process.
//! \param[in] address The address, in the remote process' address space,
//! where the `IMAGE_DOS_HEADER` is located.
//! \param[in] size The size of the image.
//! \param[in] name The module's name, a string to be used in logged messages.
//! This string is for diagnostic purposes.
//!
//! \return `true` if the image was read successfully, `false` otherwise, with
//! an appropriate message logged.
bool Initialize(ProcessReaderWin* process_reader,
WinVMAddress address,
WinVMSize size,
const std::string& module_name);
//! \brief Returns the image's load address.
//!
//! This is the value passed as \a address to Initialize().
WinVMAddress Address() const { return module_range_.Base(); }
//! \brief Returns the image's size.
//!
//! This is the value passed as \a size to Initialize().
WinVMSize Size() const { return module_range_.Size(); }
//! \brief Obtains the module's CrashpadInfo structure.
//!
//! \return `true` on success, `false` on failure. If the module does not have
//! a `CPADinfo` section, this will return `false` without logging any
//! messages. Other failures will result in messages being logged.
bool GetCrashpadInfo(process_types::CrashpadInfo* crashpad_info) const;
private:
//! \brief Finds a given section by name in the image.
bool GetSectionByName(const std::string& name,
IMAGE_SECTION_HEADER* section) const;
//! \brief Reads memory from target process, first checking whether the range
//! requested falls inside module_range_.
//!
//! \return `true` on success, with \a into filled out, otherwise `false` and
//! a message will be logged.
bool CheckedReadMemory(WinVMAddress address,
WinVMSize size,
void* into) const;
ProcessReaderWin* process_reader_; // weak
CheckedWinAddressRange module_range_;
std::string module_name_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(PEImageReader);
};
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_

View File

@ -14,9 +14,15 @@
#include "snapshot/win/process_reader_win.h"
#include "base/numerics/safe_conversions.h"
namespace crashpad {
ProcessReaderWin::ProcessReaderWin() : process_info_(), initialized_() {
ProcessReaderWin::ProcessReaderWin()
: process_(INVALID_HANDLE_VALUE),
process_info_(),
modules_(),
initialized_() {
}
ProcessReaderWin::~ProcessReaderWin() {
@ -25,10 +31,37 @@ ProcessReaderWin::~ProcessReaderWin() {
bool ProcessReaderWin::Initialize(HANDLE process) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
process_ = process;
process_info_.Initialize(process);
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
bool ProcessReaderWin::ReadMemory(WinVMAddress at,
WinVMSize num_bytes,
void* into) {
SIZE_T bytes_read;
if (!ReadProcessMemory(process_,
reinterpret_cast<void*>(at),
into,
base::checked_cast<SIZE_T>(num_bytes),
&bytes_read) ||
num_bytes != bytes_read) {
PLOG(ERROR) << "ReadMemory at " << at << " of " << num_bytes << " failed";
return false;
}
return true;
}
const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (!process_info_.Modules(&modules_)) {
LOG(ERROR) << "couldn't retrieve modules";
}
return modules_;
}
} // namespace crashpad

View File

@ -18,6 +18,7 @@
#include <windows.h>
#include "util/misc/initialization_state_dcheck.h"
#include "util/win/address_types.h"
#include "util/win/process_info.h"
namespace crashpad {
@ -42,8 +43,19 @@ class ProcessReaderWin {
//! \return `true` if the target task is a 64-bit process.
bool Is64Bit() const { return process_info_.Is64Bit(); }
pid_t ProcessID() const { return process_info_.ProcessID(); }
pid_t ParentProcessID() const { return process_info_.ParentProcessID(); }
bool ReadMemory(WinVMAddress at, WinVMSize num_bytes, void* into);
//! \return The modules loaded in the process. The first element (at index
//! `0`) corresponds to the main executable.
const std::vector<ProcessInfo::Module>& Modules();
private:
HANDLE process_;
ProcessInfo process_info_;
std::vector<ProcessInfo::Module> modules_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ProcessReaderWin);

View File

@ -0,0 +1,48 @@
// 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/process_reader_win.h"
#include <windows.h>
#include "gtest/gtest.h"
namespace crashpad {
namespace test {
namespace {
TEST(ProcessReaderWin, SelfBasic) {
ProcessReaderWin process_reader;
ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess()));
#if !defined(ARCH_CPU_64_BITS)
EXPECT_FALSE(process_reader.Is64Bit());
#else
EXPECT_TRUE(process_reader.Is64Bit());
#endif
EXPECT_EQ(GetCurrentProcessId(), process_reader.ProcessID());
const char kTestMemory[] = "Some test memory";
char buffer[arraysize(kTestMemory)];
ASSERT_TRUE(
process_reader.ReadMemory(reinterpret_cast<uintptr_t>(kTestMemory),
sizeof(kTestMemory),
&buffer));
EXPECT_STREQ(kTestMemory, buffer);
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -0,0 +1,162 @@
// 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/process_snapshot_win.h"
#include "snapshot/win/module_snapshot_win.h"
#include "util/win/time.h"
namespace crashpad {
ProcessSnapshotWin::ProcessSnapshotWin()
: ProcessSnapshot(),
system_(),
// TODO(scottmg): threads_(),
modules_(),
// TODO(scottmg): exception_(),
process_reader_(),
report_id_(),
client_id_(),
annotations_simple_map_(),
snapshot_time_(),
initialized_() {
}
ProcessSnapshotWin::~ProcessSnapshotWin() {
}
bool ProcessSnapshotWin::Initialize(HANDLE process) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
GetTimeOfDay(&snapshot_time_);
if (!process_reader_.Initialize(process))
return false;
system_.Initialize(&process_reader_);
// TODO(scottmg): InitializeThreads();
InitializeModules();
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
void ProcessSnapshotWin::GetCrashpadOptions(
CrashpadInfoClientOptions* options) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
CrashpadInfoClientOptions local_options;
for (internal::ModuleSnapshotWin* module : modules_) {
CrashpadInfoClientOptions module_options;
module->GetCrashpadOptions(&module_options);
if (local_options.crashpad_handler_behavior == TriState::kUnset) {
local_options.crashpad_handler_behavior =
module_options.crashpad_handler_behavior;
}
if (local_options.system_crash_reporter_forwarding == TriState::kUnset) {
local_options.system_crash_reporter_forwarding =
module_options.system_crash_reporter_forwarding;
}
// If non-default values have been found for all options, the loop can end
// early.
if (local_options.crashpad_handler_behavior != TriState::kUnset &&
local_options.system_crash_reporter_forwarding != TriState::kUnset) {
break;
}
}
*options = local_options;
}
pid_t ProcessSnapshotWin::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return process_reader_.ProcessID();
}
pid_t ProcessSnapshotWin::ParentProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return process_reader_.ParentProcessID();
}
void ProcessSnapshotWin::SnapshotTime(timeval* snapshot_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
*snapshot_time = snapshot_time_;
}
void ProcessSnapshotWin::ProcessStartTime(timeval* start_time) const {
CHECK(false) << "TODO(scottmg)";
}
void ProcessSnapshotWin::ProcessCPUTimes(timeval* user_time,
timeval* system_time) const {
CHECK(false) << "TODO(scottmg)";
}
void ProcessSnapshotWin::ReportID(UUID* report_id) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
*report_id = report_id_;
}
void ProcessSnapshotWin::ClientID(UUID* client_id) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
*client_id = client_id_;
}
const std::map<std::string, std::string>&
ProcessSnapshotWin::AnnotationsSimpleMap() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return annotations_simple_map_;
}
const SystemSnapshot* ProcessSnapshotWin::System() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return &system_;
}
std::vector<const ThreadSnapshot*> ProcessSnapshotWin::Threads() const {
CHECK(false) << "TODO(scottmg)";
return std::vector<const ThreadSnapshot*>();
}
std::vector<const ModuleSnapshot*> ProcessSnapshotWin::Modules() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
std::vector<const ModuleSnapshot*> modules;
for (internal::ModuleSnapshotWin* module : modules_) {
modules.push_back(module);
}
return modules;
}
const ExceptionSnapshot* ProcessSnapshotWin::Exception() const {
CHECK(false) << "TODO(scottmg)";
return nullptr;
}
void ProcessSnapshotWin::InitializeModules() {
const std::vector<ProcessInfo::Module>& process_reader_modules =
process_reader_.Modules();
for (const ProcessInfo::Module& process_reader_module :
process_reader_modules) {
auto module = make_scoped_ptr(new internal::ModuleSnapshotWin());
if (module->Initialize(&process_reader_, process_reader_module)) {
modules_.push_back(module.release());
}
}
}
} // namespace crashpad

View File

@ -0,0 +1,128 @@
// 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.
#ifndef CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_
#define CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_
#include <windows.h>
#include <sys/time.h>
#include <map>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "client/crashpad_info.h"
#include "snapshot/crashpad_info_client_options.h"
#include "snapshot/exception_snapshot.h"
#include "snapshot/module_snapshot.h"
#include "snapshot/process_snapshot.h"
#include "snapshot/system_snapshot.h"
#include "snapshot/thread_snapshot.h"
#include "snapshot/win/module_snapshot_win.h"
#include "snapshot/win/system_snapshot_win.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/misc/uuid.h"
#include "util/stdlib/pointer_container.h"
namespace crashpad {
//! \brief A ProcessSnapshot of a running (or crashed) process running on a
//! Windows system.
class ProcessSnapshotWin final : public ProcessSnapshot {
public:
ProcessSnapshotWin();
~ProcessSnapshotWin() override;
//! \brief Initializes the object.
//!
//! \param[in] process The handle to create a snapshot from.
//!
//! \return `true` if the snapshot could be created, `false` otherwise with
//! an appropriate message logged.
bool Initialize(HANDLE process);
//! \brief Sets the value to be returned by ReportID().
//!
//! The crash report ID is under the control of the snapshot producer, which
//! may call this method to set the report ID. If this is not done, ReportID()
//! will return an identifier consisting entirely of zeroes.
void SetReportID(const UUID& report_id) { report_id_ = report_id; }
//! \brief Sets the value to be returned by ClientID().
//!
//! The client ID is under the control of the snapshot producer, which may
//! call this method to set the client ID. If this is not done, ClientID()
//! will return an identifier consisting entirely of zeroes.
void SetClientID(const UUID& client_id) { client_id_ = client_id; }
//! \brief Sets the value to be returned by AnnotationsSimpleMap().
//!
//! All process annotations are under the control of the snapshot producer,
//! which may call this method to establish these annotations. Contrast this
//! with module annotations, which are under the control of the process being
//! snapshotted.
void SetAnnotationsSimpleMap(
const std::map<std::string, std::string>& annotations_simple_map) {
annotations_simple_map_ = annotations_simple_map;
}
//! \brief Returns options from CrashpadInfo structures found in modules in
//! the process.
//!
//! \param[out] options Options set in CrashpadInfo structures in modules in
//! the process.
void GetCrashpadOptions(CrashpadInfoClientOptions* options);
// ProcessSnapshot:
pid_t ProcessID() const override;
pid_t ParentProcessID() const override;
void SnapshotTime(timeval* snapshot_time) const override;
void ProcessStartTime(timeval* start_time) const override;
void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
void ReportID(UUID* report_id) const override;
void ClientID(UUID* client_id) const override;
const std::map<std::string, std::string>& AnnotationsSimpleMap()
const override;
const SystemSnapshot* System() const override;
std::vector<const ThreadSnapshot*> Threads() const override;
std::vector<const ModuleSnapshot*> Modules() const override;
const ExceptionSnapshot* Exception() const override;
private:
// Initializes threads_ on behalf of Initialize().
// TODO(scottmg): void InitializeThreads();
// Initializes modules_ on behalf of Initialize().
void InitializeModules();
internal::SystemSnapshotWin system_;
// TODO(scottmg): PointerVector<internal::ThreadSnapshotWin> threads_;
PointerVector<internal::ModuleSnapshotWin> modules_;
// TODO(scottmg): scoped_ptr<internal::ExceptionSnapshotWin> exception_;
ProcessReaderWin process_reader_;
UUID report_id_;
UUID client_id_;
std::map<std::string, std::string> annotations_simple_map_;
timeval snapshot_time_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotWin);
};
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_

View File

@ -1,87 +0,0 @@
// Copyright 2014 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/mac/checked_mach_address_range.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
namespace crashpad {
CheckedMachAddressRange::CheckedMachAddressRange()
: range_32_(0, 0), is_64_bit_(false), range_ok_(true) {
}
CheckedMachAddressRange::CheckedMachAddressRange(
bool is_64_bit,
mach_vm_address_t base,
mach_vm_size_t size) {
SetRange(is_64_bit, base, size);
}
void CheckedMachAddressRange::SetRange(bool is_64_bit,
mach_vm_address_t base,
mach_vm_size_t size) {
is_64_bit_ = is_64_bit;
if (is_64_bit_) {
range_64_.SetRange(base, size);
range_ok_ = true;
} else {
range_32_.SetRange(base, size);
range_ok_ = base::IsValueInRangeForNumericType<uint32_t>(base) &&
base::IsValueInRangeForNumericType<uint32_t>(size);
}
}
mach_vm_address_t CheckedMachAddressRange::Base() const {
return is_64_bit_ ? range_64_.base() : range_32_.base();
}
mach_vm_size_t CheckedMachAddressRange::Size() const {
return is_64_bit_ ? range_64_.size() : range_32_.size();
}
mach_vm_address_t CheckedMachAddressRange::End() const {
return is_64_bit_ ? range_64_.end() : range_32_.end();
}
bool CheckedMachAddressRange::IsValid() const {
return range_ok_ && (is_64_bit_ ? range_64_.IsValid() : range_32_.IsValid());
}
bool CheckedMachAddressRange::ContainsValue(mach_vm_address_t value) const {
DCHECK(range_ok_);
if (is_64_bit_) {
return range_64_.ContainsValue(value);
}
if (!base::IsValueInRangeForNumericType<uint32_t>(value)) {
return false;
}
return range_32_.ContainsValue(value);
}
bool CheckedMachAddressRange::ContainsRange(
const CheckedMachAddressRange& that) const {
DCHECK_EQ(is_64_bit_, that.is_64_bit_);
DCHECK(range_ok_);
DCHECK(that.range_ok_);
return is_64_bit_ ? range_64_.ContainsRange(that.range_64_)
: range_32_.ContainsRange(that.range_32_);
}
} // namespace crashpad

View File

@ -17,7 +17,7 @@
#include <mach/mach.h>
#include "util/numeric/checked_range.h"
#include "util/numeric/checked_address_range.h"
namespace crashpad {
@ -30,101 +30,8 @@ namespace crashpad {
//!
//! Aside from varying the overall range on the basis of a process pointer type
//! width, this class functions very similarly to CheckedRange.
class CheckedMachAddressRange {
public:
//! \brief Initializes a default range.
//!
//! The default range has base 0, size 0, and appears to be from a 32-bit
//! process.
CheckedMachAddressRange();
//! \brief Initializes a range.
//!
//! See SetRange().
CheckedMachAddressRange(bool is_64_bit,
mach_vm_address_t base,
mach_vm_size_t size);
//! \brief Sets a ranges fields.
//!
//! \param[in] is_64_bit `true` if \a base and \a size refer to addresses in a
//! 64-bit process; `false` if they refer to addresses in a 32-bit
//! process.
//! \param[in] base The ranges base address.
//! \param[in] size The ranges size.
void SetRange(bool is_64_bit, mach_vm_address_t base, mach_vm_size_t size);
//! \brief The ranges base address.
mach_vm_address_t Base() const;
//! \brief The ranges size.
mach_vm_size_t Size() const;
//! \brief The ranges end address (its base address plus its size).
mach_vm_address_t End() const;
//! \brief Returns the validity of the address range.
//!
//! \return `true` if the address range is valid, `false` otherwise.
//!
//! An address range is valid if its size can be converted to the address
//! ranges data type without data loss, and if its end (base plus size) can
//! be computed without overflowing its data type.
bool IsValid() const;
//! \brief Returns whether the address range contains another address.
//!
//! \param[in] value The (possibly) contained address.
//!
//! \return `true` if the address range contains \a value, `false` otherwise.
//!
//! An address range contains a value if the value is greater than or equal to
//! its base address, and less than its end address (base address plus size).
//!
//! This method must only be called if IsValid() would return `true`.
bool ContainsValue(const mach_vm_address_t value) const;
//! \brief Returns whether the address range contains another address range.
//!
//! \param[in] that The (possibly) contained address range.
//!
//! \return `true` if `this` address range, the containing address range,
//! contains \a that, the contained address range. `false` otherwise.
//!
//! An address range contains another address range when the contained address
//! ranges base is greater than or equal to the containing address ranges
//! base, and the contained address ranges end is less than or equal to the
//! containing address ranges end.
//!
//! This method should only be called on two CheckedMachAddressRange objects
//! representing address ranges in the same process.
//!
//! This method must only be called if IsValid() would return `true` for both
//! CheckedMachAddressRange objects involved.
bool ContainsRange(const CheckedMachAddressRange& that) const;
private:
// The field of the union that is expressed is determined by is_64_bit_.
union {
CheckedRange<uint32_t> range_32_;
CheckedRange<uint64_t> range_64_;
};
// Determines which field of the union is expressed.
bool is_64_bit_;
// Whether the base and size were valid for their data type when set. This is
// always true when is_64_bit_ is true because the underlying data types are
// 64 bits wide and there is no possibility for range and size to overflow.
// When is_64_bit_ is false, range_ok_ will be false if SetRange() was passed
// a base or size that overflowed the underlying 32-bit data type. This field
// is necessary because the interface exposes mach_vm_address_t and
// mach_vm_size_t uniformly, but these types are too wide for the underlying
// pointer and size types in 32-bit processes.
bool range_ok_;
DISALLOW_COPY_AND_ASSIGN(CheckedMachAddressRange);
};
using CheckedMachAddressRange =
internal::CheckedAddressRangeGeneric<mach_vm_address_t, mach_vm_size_t>;
} // namespace crashpad

View File

@ -0,0 +1,120 @@
// 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/numeric/checked_address_range.h"
#if defined(OS_MACOSX)
#include <mach/mach.h>
#elif defined(OS_WIN)
#include "util/win/address_types.h"
#endif // OS_MACOSX
namespace crashpad {
namespace internal {
template <class ValueType, class SizeType>
CheckedAddressRangeGeneric<ValueType, SizeType>::CheckedAddressRangeGeneric()
: range_32_(0, 0),
#if defined(COMPILER_MSVC)
range_64_(0, 0),
#endif // COMPILER_MSVC
is_64_bit_(false),
range_ok_(true) {
}
template <class ValueType, class SizeType>
CheckedAddressRangeGeneric<ValueType, SizeType>::CheckedAddressRangeGeneric(
bool is_64_bit,
ValueType base,
SizeType size)
#if defined(COMPILER_MSVC)
: range_32_(0, 0),
range_64_(0, 0)
#endif // COMPILER_MSVC
{
SetRange(is_64_bit, base, size);
}
template <class ValueType, class SizeType>
ValueType CheckedAddressRangeGeneric<ValueType, SizeType>::Base() const {
return is_64_bit_ ? range_64_.base() : range_32_.base();
}
template <class ValueType, class SizeType>
SizeType CheckedAddressRangeGeneric<ValueType, SizeType>::Size() const {
return is_64_bit_ ? range_64_.size() : range_32_.size();
}
template <class ValueType, class SizeType>
ValueType CheckedAddressRangeGeneric<ValueType, SizeType>::End() const {
return is_64_bit_ ? range_64_.end() : range_32_.end();
}
template <class ValueType, class SizeType>
bool CheckedAddressRangeGeneric<ValueType, SizeType>::IsValid() const {
return range_ok_ && (is_64_bit_ ? range_64_.IsValid() : range_32_.IsValid());
}
template <class ValueType, class SizeType>
void CheckedAddressRangeGeneric<ValueType, SizeType>::SetRange(bool is_64_bit,
ValueType base,
SizeType size) {
is_64_bit_ = is_64_bit;
if (is_64_bit_) {
range_64_.SetRange(base, size);
range_ok_ = true;
} else {
range_32_.SetRange(static_cast<uint32_t>(base),
static_cast<uint32_t>(size));
range_ok_ = base::IsValueInRangeForNumericType<uint32_t>(base) &&
base::IsValueInRangeForNumericType<uint32_t>(size);
}
}
template <class ValueType, class SizeType>
bool CheckedAddressRangeGeneric<ValueType, SizeType>::ContainsValue(
ValueType value) const {
DCHECK(range_ok_);
if (is_64_bit_) {
return range_64_.ContainsValue(value);
}
if (!base::IsValueInRangeForNumericType<uint32_t>(value)) {
return false;
}
return range_32_.ContainsValue(static_cast<uint32_t>(value));
}
template <class ValueType, class SizeType>
bool CheckedAddressRangeGeneric<ValueType, SizeType>::ContainsRange(
const CheckedAddressRangeGeneric& that) const {
DCHECK_EQ(is_64_bit_, that.is_64_bit_);
DCHECK(range_ok_);
DCHECK(that.range_ok_);
return is_64_bit_ ? range_64_.ContainsRange(that.range_64_)
: range_32_.ContainsRange(that.range_32_);
}
// Explicit instantiations for the cases we use.
#if defined(OS_MACOSX)
template class CheckedAddressRangeGeneric<mach_vm_address_t, mach_vm_size_t>;
#elif defined(OS_WIN)
template class CheckedAddressRangeGeneric<WinVMAddress, WinVMSize>;
#endif // OS_MACOSX
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,144 @@
// 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.
#ifndef CRASHPAD_UTIL_NUMERIC_CHECKED_ADDRESS_RANGE_H_
#define CRASHPAD_UTIL_NUMERIC_CHECKED_ADDRESS_RANGE_H_
#include <stdint.h>
#include "build/build_config.h"
#include "util/numeric/checked_range.h"
namespace crashpad {
namespace internal {
//! \brief Ensures that a range, composed of a base and a size, does not
//! overflow the pointer type of the process it describes a range in.
//!
//! This class checks bases of type `ValueType` and sizes of type `SizeType`
//! against a process whose pointer type is either 32 or 64 bits wide.
//!
//! Aside from varying the overall range on the basis of a process pointer type
//! width, this class functions very similarly to CheckedRange.
//!
//! \sa CheckedMachAddressRange
template <class ValueType, class SizeType>
class CheckedAddressRangeGeneric {
public:
//! \brief Initializes a default range.
//!
//! The default range has base 0, size 0, and appears to be from a 32-bit
//! process.
CheckedAddressRangeGeneric();
//! \brief Initializes a range.
//!
//! See SetRange().
CheckedAddressRangeGeneric(bool is_64_bit, ValueType base, SizeType size);
//! \brief Sets a ranges fields.
//!
//! \param[in] is_64_bit `true` if \a base and \a size refer to addresses in a
//! 64-bit process; `false` if they refer to addresses in a 32-bit
//! process.
//! \param[in] base The ranges base address.
//! \param[in] size The ranges size.
void SetRange(bool is_64_bit, ValueType base, SizeType size);
//! \brief The ranges base address.
ValueType Base() const;
//! \brief The ranges size.
SizeType Size() const;
//! \brief The ranges end address (its base address plus its size).
ValueType End() const;
//! \brief Returns the validity of the address range.
//!
//! \return `true` if the address range is valid, `false` otherwise.
//!
//! An address range is valid if its size can be converted to the address
//! ranges data type without data loss, and if its end (base plus size) can
//! be computed without overflowing its data type.
bool IsValid() const;
//! \brief Returns whether this range refers to a 64-bit process.
bool Is64Bit() const { return is_64_bit_; }
//! \brief Returns whether the address range contains another address.
//!
//! \param[in] value The (possibly) contained address.
//!
//! \return `true` if the address range contains \a value, `false` otherwise.
//!
//! An address range contains a value if the value is greater than or equal to
//! its base address, and less than its end address (base address plus size).
//!
//! This method must only be called if IsValid() would return `true`.
bool ContainsValue(const ValueType value) const;
//! \brief Returns whether the address range contains another address range.
//!
//! \param[in] that The (possibly) contained address range.
//!
//! \return `true` if `this` address range, the containing address range,
//! contains \a that, the contained address range. `false` otherwise.
//!
//! An address range contains another address range when the contained address
//! ranges base is greater than or equal to the containing address ranges
//! base, and the contained address ranges end is less than or equal to the
//! containing address ranges end.
//!
//! This method should only be called on two CheckedAddressRangeGeneric
//! objects representing address ranges in the same process.
//!
//! This method must only be called if IsValid() would return `true` for both
//! CheckedAddressRangeGeneric objects involved.
bool ContainsRange(const CheckedAddressRangeGeneric& that) const;
private:
#if defined(COMPILER_MSVC)
// MSVC cannot handle a union containing CheckedRange (with constructor, etc.)
// currently.
CheckedRange<uint32_t> range_32_;
CheckedRange<uint64_t> range_64_;
#else
// The field of the union that is expressed is determined by is_64_bit_.
union {
CheckedRange<uint32_t> range_32_;
CheckedRange<uint64_t> range_64_;
};
#endif
// Determines which field of the union is expressed.
bool is_64_bit_;
// Whether the base and size were valid for their data type when set. This is
// always true when is_64_bit_ is true because the underlying data types are
// 64 bits wide and there is no possibility for range and size to overflow.
// When is_64_bit_ is false, range_ok_ will be false if SetRange() was passed
// a base or size that overflowed the underlying 32-bit data type. This field
// is necessary because the interface exposes the address and size types
// uniformly, but these types are too wide for the underlying pointer and size
// types in 32-bit processes.
bool range_ok_;
DISALLOW_COPY_AND_ASSIGN(CheckedAddressRangeGeneric);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_UTIL_NUMERIC_CHECKED_ADDRESS_RANGE_H_

View File

@ -0,0 +1,260 @@
// Copyright 2014 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/numeric/checked_address_range.h"
#include <limits>
#include "base/basictypes.h"
#include "base/format_macros.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
namespace crashpad {
namespace test {
namespace {
using CheckedAddressRange =
internal::CheckedAddressRangeGeneric<uint64_t, uint64_t>;
enum Validity {
kInvalid = false,
kValid,
kValid64Invalid32,
};
bool ExpectationForValidity32(Validity validity) {
return validity == kValid;
}
bool ExpectationForValidity64(Validity validity) {
return validity == kValid || validity == kValid64Invalid32;
}
TEST(CheckedAddressRange, IsValid) {
const struct TestData {
uint64_t base;
uint64_t size;
Validity validity;
} kTestData[] = {
{0, 0, kValid},
{0, 1, kValid},
{0, 2, kValid},
{0, 0x7fffffff, kValid},
{0, 0x80000000, kValid},
{0, 0xfffffffe, kValid},
{0, 0xffffffff, kValid},
{0, 0xffffffffffffffff, kValid64Invalid32},
{1, 0, kValid},
{1, 1, kValid},
{1, 2, kValid},
{1, 0x7fffffff, kValid},
{1, 0x80000000, kValid},
{1, 0xfffffffe, kValid},
{1, 0xffffffff, kValid64Invalid32},
{1, 0xfffffffffffffffe, kValid64Invalid32},
{1, 0xffffffffffffffff, kInvalid},
{0x7fffffff, 0, kValid},
{0x7fffffff, 1, kValid},
{0x7fffffff, 2, kValid},
{0x7fffffff, 0x7fffffff, kValid},
{0x7fffffff, 0x80000000, kValid},
{0x7fffffff, 0xfffffffe, kValid64Invalid32},
{0x7fffffff, 0xffffffff, kValid64Invalid32},
{0x80000000, 0, kValid},
{0x80000000, 1, kValid},
{0x80000000, 2, kValid},
{0x80000000, 0x7fffffff, kValid},
{0x80000000, 0x80000000, kValid64Invalid32},
{0x80000000, 0xfffffffe, kValid64Invalid32},
{0x80000000, 0xffffffff, kValid64Invalid32},
{0xfffffffe, 0, kValid},
{0xfffffffe, 1, kValid},
{0xfffffffe, 2, kValid64Invalid32},
{0xfffffffe, 0x7fffffff, kValid64Invalid32},
{0xfffffffe, 0x80000000, kValid64Invalid32},
{0xfffffffe, 0xfffffffe, kValid64Invalid32},
{0xfffffffe, 0xffffffff, kValid64Invalid32},
{0xffffffff, 0, kValid},
{0xffffffff, 1, kValid64Invalid32},
{0xffffffff, 2, kValid64Invalid32},
{0xffffffff, 0x7fffffff, kValid64Invalid32},
{0xffffffff, 0x80000000, kValid64Invalid32},
{0xffffffff, 0xfffffffe, kValid64Invalid32},
{0xffffffff, 0xffffffff, kValid64Invalid32},
{0x7fffffffffffffff, 0, kValid64Invalid32},
{0x7fffffffffffffff, 1, kValid64Invalid32},
{0x7fffffffffffffff, 2, kValid64Invalid32},
{0x7fffffffffffffff, 0x7fffffffffffffff, kValid64Invalid32},
{0x7fffffffffffffff, 0x8000000000000000, kValid64Invalid32},
{0x7fffffffffffffff, 0x8000000000000001, kInvalid},
{0x7fffffffffffffff, 0xfffffffffffffffe, kInvalid},
{0x7fffffffffffffff, 0xffffffffffffffff, kInvalid},
{0x8000000000000000, 0, kValid64Invalid32},
{0x8000000000000000, 1, kValid64Invalid32},
{0x8000000000000000, 2, kValid64Invalid32},
{0x8000000000000000, 0x7fffffffffffffff, kValid64Invalid32},
{0x8000000000000000, 0x8000000000000000, kInvalid},
{0x8000000000000000, 0x8000000000000001, kInvalid},
{0x8000000000000000, 0xfffffffffffffffe, kInvalid},
{0x8000000000000000, 0xffffffffffffffff, kInvalid},
{0xfffffffffffffffe, 0, kValid64Invalid32},
{0xfffffffffffffffe, 1, kValid64Invalid32},
{0xfffffffffffffffe, 2, kInvalid},
{0xffffffffffffffff, 0, kValid64Invalid32},
{0xffffffffffffffff, 1, kInvalid},
};
for (size_t index = 0; index < arraysize(kTestData); ++index) {
const TestData& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf("index %" PRIuS
", base 0x%llx, size 0x%llx",
index,
testcase.base,
testcase.size));
CheckedAddressRange range_32(false, testcase.base, testcase.size);
EXPECT_EQ(ExpectationForValidity32(testcase.validity), range_32.IsValid());
CheckedAddressRange range_64(true, testcase.base, testcase.size);
EXPECT_EQ(ExpectationForValidity64(testcase.validity), range_64.IsValid());
}
}
TEST(CheckedAddressRange, ContainsValue) {
const struct TestData {
uint64_t value;
bool expectation;
} kTestData[] = {
{0, false},
{1, false},
{0x1fff, false},
{0x2000, true},
{0x2001, true},
{0x2ffe, true},
{0x2fff, true},
{0x3000, false},
{0x3001, false},
{0x7fffffff, false},
{0x80000000, false},
{0x80000001, false},
{0x80001fff, false},
{0x80002000, false},
{0x80002001, false},
{0x80002ffe, false},
{0x80002fff, false},
{0x80003000, false},
{0x80003001, false},
{0xffffcfff, false},
{0xffffdfff, false},
{0xffffefff, false},
{0xffffffff, false},
{0x100000000, false},
{0xffffffffffffffff, false},
};
CheckedAddressRange parent_range_32(false, 0x2000, 0x1000);
ASSERT_TRUE(parent_range_32.IsValid());
for (size_t index = 0; index < arraysize(kTestData); ++index) {
const TestData& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf(
"index %" PRIuS ", value 0x%llx", index, testcase.value));
EXPECT_EQ(testcase.expectation,
parent_range_32.ContainsValue(testcase.value));
}
CheckedAddressRange parent_range_64(true, 0x100000000, 0x1000);
ASSERT_TRUE(parent_range_64.IsValid());
EXPECT_FALSE(parent_range_64.ContainsValue(0xffffffff));
EXPECT_TRUE(parent_range_64.ContainsValue(0x100000000));
EXPECT_TRUE(parent_range_64.ContainsValue(0x100000001));
EXPECT_TRUE(parent_range_64.ContainsValue(0x100000fff));
EXPECT_FALSE(parent_range_64.ContainsValue(0x100001000));
}
TEST(CheckedAddressRange, ContainsRange) {
const struct TestData {
uint64_t base;
uint64_t size;
bool expectation;
} kTestData[] = {
{0, 0, false},
{0, 1, false},
{0x2000, 0x1000, true},
{0, 0x2000, false},
{0x3000, 0x1000, false},
{0x1800, 0x1000, false},
{0x2800, 0x1000, false},
{0x2000, 0x800, true},
{0x2800, 0x800, true},
{0x2400, 0x800, true},
{0x2800, 0, true},
{0x2000, 0xffffdfff, false},
{0x2800, 0xffffd7ff, false},
{0x3000, 0xffffcfff, false},
{0xfffffffe, 1, false},
{0xffffffff, 0, false},
{0x1fff, 0, false},
{0x2000, 0, true},
{0x2001, 0, true},
{0x2fff, 0, true},
{0x3000, 0, true},
{0x3001, 0, false},
{0x1fff, 1, false},
{0x2000, 1, true},
{0x2001, 1, true},
{0x2fff, 1, true},
{0x3000, 1, false},
{0x3001, 1, false},
};
CheckedAddressRange parent_range_32(false, 0x2000, 0x1000);
ASSERT_TRUE(parent_range_32.IsValid());
for (size_t index = 0; index < arraysize(kTestData); ++index) {
const TestData& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf("index %" PRIuS
", base 0x%llx, size 0x%llx",
index,
testcase.base,
testcase.size));
CheckedAddressRange child_range_32(false, testcase.base, testcase.size);
ASSERT_TRUE(child_range_32.IsValid());
EXPECT_EQ(testcase.expectation,
parent_range_32.ContainsRange(child_range_32));
}
CheckedAddressRange parent_range_64(true, 0x100000000, 0x1000);
ASSERT_TRUE(parent_range_64.IsValid());
CheckedAddressRange child_range_64(true, 0xffffffff, 2);
EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64));
child_range_64.SetRange(true, 0x100000000, 2);
EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64));
child_range_64.SetRange(true, 0x100000ffe, 2);
EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64));
child_range_64.SetRange(true, 0x100000fff, 2);
EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64));
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -41,7 +41,6 @@
'file/file_writer.h',
'file/string_file.cc',
'file/string_file.h',
'mac/checked_mach_address_range.cc',
'mac/checked_mach_address_range.h',
'mac/launchd.h',
'mac/launchd.mm',
@ -108,6 +107,8 @@
'net/http_transport.h',
'net/http_transport_mac.mm',
'net/http_transport_win.cc',
'numeric/checked_address_range.cc',
'numeric/checked_address_range.h',
'numeric/checked_range.h',
'numeric/in_range_cast.h',
'numeric/int128.h',
@ -140,6 +141,8 @@
'synchronization/semaphore.h',
'thread/thread_log_messages.cc',
'thread/thread_log_messages.h',
'win/address_types.h',
'win/checked_win_address_range.h',
'win/process_info.cc',
'win/process_info.h',
'win/process_structs.h',

View File

@ -35,7 +35,6 @@
'sources': [
'file/file_io_test.cc',
'file/string_file_test.cc',
'mac/checked_mach_address_range_test.cc',
'mac/launchd_test.mm',
'mac/mac_util_test.mm',
'mac/service_management_test.mm',
@ -65,6 +64,7 @@
'net/http_body_test_util.h',
'net/http_multipart_builder_test.cc',
'net/http_transport_test.cc',
'numeric/checked_address_range_test.cc',
'numeric/checked_range_test.cc',
'numeric/in_range_cast_test.cc',
'numeric/int128_test.cc',

32
util/win/address_types.h Normal file
View File

@ -0,0 +1,32 @@
// 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.
#ifndef CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_
#define CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_
#include <stdint.h>
namespace crashpad {
//! \brief Type used to represent an address in a process, potentially across
//! bitness.
using WinVMAddress = uint64_t;
//! \brief Type used to represent the size of a memory range (with a
//! WinVMAddress), potentially across bitness.
using WinVMSize = uint64_t;
} // namespace crashpad
#endif // CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_

View File

@ -0,0 +1,36 @@
// 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.
#ifndef CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_
#define CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_
#include "util/numeric/checked_address_range.h"
#include "util/win/address_types.h"
namespace crashpad {
//! \brief Ensures that a range, composed of a base and a size, does not
//! overflow the pointer type of the process it describes a range in.
//!
//! This class checks bases of type WinVMAddress and sizes of type WinVMSize
//! against a process whose pointer type is either 32 or 64 bits wide.
//!
//! Aside from varying the overall range on the basis of a process' pointer type
//! width, this class functions very similarly to CheckedRange.
using CheckedWinAddressRange =
internal::CheckedAddressRangeGeneric<WinVMAddress, WinVMSize>;
} // namespace crashpad
#endif // CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_

View File

@ -80,7 +80,7 @@ bool ReadUnicodeString(HANDLE process,
return true;
}
template <class T> bool ReadStruct(HANDLE process, uintptr_t at, T* into) {
template <class T> bool ReadStruct(HANDLE process, WinVMAddress at, T* into) {
SIZE_T bytes_read;
if (!ReadProcessMemory(process,
reinterpret_cast<const void*>(at),
@ -103,11 +103,11 @@ template <class T> bool ReadStruct(HANDLE process, uintptr_t at, T* into) {
template <class Traits>
bool ReadProcessData(HANDLE process,
uintptr_t peb_address_uintptr,
WinVMAddress peb_address_vmaddr,
ProcessInfo* process_info) {
Traits::Pointer peb_address;
if (!AssignIfInRange(&peb_address, peb_address_uintptr)) {
LOG(ERROR) << "peb_address_uintptr " << peb_address_uintptr
if (!AssignIfInRange(&peb_address, peb_address_vmaddr)) {
LOG(ERROR) << "peb_address_vmaddr " << peb_address_vmaddr
<< " out of range";
return false;
}
@ -131,13 +131,12 @@ bool ReadProcessData(HANDLE process,
if (!ReadStruct(process, peb.Ldr, &peb_ldr_data))
return false;
std::wstring module;
process_types::LDR_DATA_TABLE_ENTRY<Traits> ldr_data_table_entry;
// Include the first module in the memory order list to get our the main
// executable's name, as it's not included in initialization order below.
if (!ReadStruct(process,
reinterpret_cast<uintptr_t>(
reinterpret_cast<WinVMAddress>(
reinterpret_cast<const char*>(
peb_ldr_data.InMemoryOrderModuleList.Flink) -
offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
@ -145,8 +144,14 @@ bool ReadProcessData(HANDLE process,
&ldr_data_table_entry)) {
return false;
}
if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module))
ProcessInfo::Module module;
if (!ReadUnicodeString(
process, ldr_data_table_entry.FullDllName, &module.name)) {
return false;
}
module.dll_base = ldr_data_table_entry.DllBase;
module.size = ldr_data_table_entry.SizeOfImage;
module.timestamp = ldr_data_table_entry.TimeDateStamp;
process_info->modules_.push_back(module);
// Walk the PEB LDR structure (doubly-linked list) to get the list of loaded
@ -161,16 +166,21 @@ bool ReadProcessData(HANDLE process,
// to read from the target, and also offset back to the beginning of the
// structure.
if (!ReadStruct(process,
reinterpret_cast<uintptr_t>(
reinterpret_cast<WinVMAddress>(
reinterpret_cast<const char*>(cur) -
offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
InInitializationOrderLinks)),
&ldr_data_table_entry)) {
break;
}
// TODO(scottmg): Capture TimeDateStamp, Checksum, etc. too?
if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module))
// TODO(scottmg): Capture Checksum, etc. too?
if (!ReadUnicodeString(
process, ldr_data_table_entry.FullDllName, &module.name)) {
break;
}
module.dll_base = ldr_data_table_entry.DllBase;
module.size = ldr_data_table_entry.SizeOfImage;
module.timestamp = ldr_data_table_entry.TimeDateStamp;
process_info->modules_.push_back(module);
if (cur == last)
break;
@ -179,6 +189,12 @@ bool ReadProcessData(HANDLE process,
return true;
}
ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() {
}
ProcessInfo::Module::~Module() {
}
ProcessInfo::ProcessInfo()
: process_id_(),
inherited_from_process_id_(),
@ -251,7 +267,7 @@ bool ProcessInfo::Initialize(HANDLE process) {
// PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32,
// but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both).
// The address of this is found by a second call to NtQueryInformationProcess.
uintptr_t peb_address = process_basic_information.PebBaseAddress;
WinVMAddress peb_address = process_basic_information.PebBaseAddress;
if (is_wow64_) {
ULONG_PTR wow64_peb_address;
status =
@ -309,7 +325,7 @@ bool ProcessInfo::CommandLine(std::wstring* command_line) const {
return true;
}
bool ProcessInfo::Modules(std::vector<std::wstring>* modules) const {
bool ProcessInfo::Modules(std::vector<Module>* modules) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
*modules = modules_;
return true;

View File

@ -23,6 +23,7 @@
#include "base/basictypes.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/win/address_types.h"
namespace crashpad {
@ -30,6 +31,24 @@ namespace crashpad {
//! primarily of information stored in the Process Environment Block.
class ProcessInfo {
public:
//! \brief Contains information about a module loaded into a process.
struct Module {
Module();
~Module();
//! \brief The pathname used to load the module from disk.
std::wstring name;
//! \brief The base address of the loaded DLL.
WinVMAddress dll_base;
//! \brief The size of the module.
WinVMSize size;
//! \brief The module's timestamp.
time_t timestamp;
};
ProcessInfo();
~ProcessInfo();
@ -64,18 +83,18 @@ class ProcessInfo {
//! The modules are enumerated in initialization order as detailed in the
//! Process Environment Block. The main executable will always be the
//! first element.
bool Modules(std::vector<std::wstring>* modules) const;
bool Modules(std::vector<Module>* modules) const;
private:
template <class T>
friend bool ReadProcessData(HANDLE process,
uintptr_t peb_address_uintptr,
WinVMAddress peb_address_vmaddr,
ProcessInfo* process_info);
pid_t process_id_;
pid_t inherited_from_process_id_;
std::wstring command_line_;
std::vector<std::wstring> modules_;
std::vector<Module> modules_;
bool is_64_bit_;
bool is_wow64_;
InitializationStateDcheck initialized_;

View File

@ -21,6 +21,7 @@
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "test/paths.h"
#include "util/file/file_io.h"
#include "util/misc/uuid.h"
#include "util/win/scoped_handle.h"
@ -30,6 +31,18 @@ namespace {
const wchar_t kNtdllName[] = L"\\ntdll.dll";
time_t GetTimestampForModule(HMODULE module) {
wchar_t filename[MAX_PATH];
if (!GetModuleFileName(module, filename, arraysize(filename)))
return 0;
struct _stat stat_buf;
int rv = _wstat(filename, &stat_buf);
EXPECT_EQ(0, rv);
if (rv != 0)
return 0;
return stat_buf.st_mtime;
}
TEST(ProcessInfo, Self) {
ProcessInfo process_info;
ASSERT_TRUE(process_info.Initialize(GetCurrentProcess()));
@ -49,16 +62,31 @@ TEST(ProcessInfo, Self) {
EXPECT_TRUE(process_info.CommandLine(&command_line));
EXPECT_EQ(std::wstring(GetCommandLine()), command_line);
std::vector<std::wstring> modules;
std::vector<ProcessInfo::Module> modules;
EXPECT_TRUE(process_info.Modules(&modules));
ASSERT_GE(modules.size(), 2u);
const wchar_t kSelfName[] = L"\\crashpad_util_test.exe";
ASSERT_GE(modules[0].size(), wcslen(kSelfName));
ASSERT_GE(modules[0].name.size(), wcslen(kSelfName));
EXPECT_EQ(kSelfName,
modules[0].substr(modules[0].size() - wcslen(kSelfName)));
ASSERT_GE(modules[1].size(), wcslen(kNtdllName));
EXPECT_EQ(kNtdllName,
modules[1].substr(modules[1].size() - wcslen(kNtdllName)));
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)));
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, 0);
EXPECT_GT(modules[1].size, 0);
EXPECT_EQ(modules[0].timestamp,
GetTimestampForModule(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);
}
void TestOtherProcess(const std::wstring& child_name_suffix) {
@ -110,24 +138,25 @@ void TestOtherProcess(const std::wstring& child_name_suffix) {
// Tell the test it's OK to shut down now that we've read our data.
SetEvent(done.get());
std::vector<std::wstring> modules;
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_" +
child_name_suffix + L".exe";
ASSERT_GE(modules[0].size(), child_name.size());
ASSERT_GE(modules[0].name.size(), child_name.size());
EXPECT_EQ(child_name,
modules[0].substr(modules[0].size() - child_name.size()));
ASSERT_GE(modules[1].size(), wcslen(kNtdllName));
EXPECT_EQ(kNtdllName,
modules[1].substr(modules[1].size() - wcslen(kNtdllName)));
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)));
// lz32.dll is an uncommonly-used-but-always-available module that the test
// binary manually loads.
const wchar_t kLz32dllName[] = L"\\lz32.dll";
ASSERT_GE(modules.back().size(), wcslen(kLz32dllName));
EXPECT_EQ(
kLz32dllName,
modules.back().substr(modules.back().size() - wcslen(kLz32dllName)));
ASSERT_GE(modules.back().name.size(), wcslen(kLz32dllName));
EXPECT_EQ(kLz32dllName,
modules.back().name.substr(modules.back().name.size() -
wcslen(kLz32dllName)));
}
TEST(ProcessInfo, OtherProcessX64) {