From feb3aa3923dd72b1ffb6d020d7c2636757f0c203 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 14 Jan 2016 12:50:22 -0800 Subject: [PATCH] win: Capture memory pointed to by the stack Change-Id: Ide75475aa9c42edf36c3a709bfc7dfbfed68b0d3 Reviewed-on: https://chromium-review.googlesource.com/322261 Reviewed-by: Mark Mentovai --- handler/win/crashy_test_program.cc | 13 ++ snapshot/capture_memory.cc | 128 ++++++++++++++++++ snapshot/capture_memory.h | 98 ++++++++++++++ snapshot/crashpad_info_client_options_test.cc | 43 +++--- snapshot/snapshot.gyp | 6 +- snapshot/win/capture_context_memory.cc | 103 -------------- snapshot/win/capture_context_memory.h | 43 ------ snapshot/win/capture_memory_delegate_win.cc | 56 ++++++++ snapshot/win/capture_memory_delegate_win.h | 58 ++++++++ snapshot/win/end_to_end_test.py | 12 ++ snapshot/win/exception_snapshot_win.cc | 8 +- snapshot/win/process_snapshot_win.cc | 76 ++++++----- snapshot/win/process_snapshot_win.h | 6 +- snapshot/win/thread_snapshot_win.cc | 13 +- snapshot/win/thread_snapshot_win.h | 5 +- 15 files changed, 454 insertions(+), 214 deletions(-) create mode 100644 snapshot/capture_memory.cc create mode 100644 snapshot/capture_memory.h delete mode 100644 snapshot/win/capture_context_memory.cc delete mode 100644 snapshot/win/capture_context_memory.h create mode 100644 snapshot/win/capture_memory_delegate_win.cc create mode 100644 snapshot/win/capture_memory_delegate_win.h diff --git a/handler/win/crashy_test_program.cc b/handler/win/crashy_test_program.cc index 6fbd514d..f9e8501f 100644 --- a/handler/win/crashy_test_program.cc +++ b/handler/win/crashy_test_program.cc @@ -28,6 +28,7 @@ #include "base/macros.h" #include "build/build_config.h" #include "client/crashpad_client.h" +#include "client/crashpad_info.h" #include "util/win/critical_section_with_debug_info.h" #include "util/win/get_function.h" @@ -164,6 +165,18 @@ int CrashyMain(int argc, wchar_t* argv[]) { return EXIT_FAILURE; } + // Make sure data pointed to by the stack is captured. + const int kDataSize = 512; + int* pointed_to_data = new int[kDataSize]; + for (int i = 0; i < kDataSize; ++i) + pointed_to_data[i] = i | ((i % 2 == 0) ? 0x80000000 : 0); + int* offset_pointer = &pointed_to_data[128]; + // Encourage the compiler to keep this variable around. + printf("%p, %p\n", offset_pointer, &offset_pointer); + + crashpad::CrashpadInfo::GetCrashpadInfo() + ->set_gather_indirectly_referenced_memory(TriState::kEnabled); + AllocateMemoryOfVariousProtections(); if (InitializeCriticalSectionWithDebugInfoIfPossible( diff --git a/snapshot/capture_memory.cc b/snapshot/capture_memory.cc new file mode 100644 index 00000000..c692798b --- /dev/null +++ b/snapshot/capture_memory.cc @@ -0,0 +1,128 @@ +// Copyright 2016 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/capture_memory.h" + +#include + +#include + +#include "base/memory/scoped_ptr.h" +#include "snapshot/memory_snapshot.h" + +namespace crashpad { +namespace internal { + +namespace { + +void MaybeCaptureMemoryAround(CaptureMemory::Delegate* delegate, + uint64_t address) { + const uint64_t non_address_offset = 0x10000; + if (address < non_address_offset) + return; + + const uint64_t max_address = delegate->Is64Bit() ? + std::numeric_limits::max() : + std::numeric_limits::max(); + if (address > max_address - non_address_offset) + return; + + const uint64_t kRegisterByteOffset = 256; + const uint64_t target = address - kRegisterByteOffset; + const uint64_t size = 1024; + static_assert(kRegisterByteOffset <= size / 2, + "negative offset too large"); + auto ranges = + delegate->GetReadableRanges(CheckedRange(target, size)); + for (const auto& range : ranges) { + delegate->AddNewMemorySnapshot(range); + } +} + +template +void CaptureAtPointersInRange(uint8_t* buffer, + uint64_t buffer_size, + CaptureMemory::Delegate* delegate) { + for (uint64_t address_offset = 0; address_offset < buffer_size; + address_offset += sizeof(T)) { + uint64_t target_address = *reinterpret_cast(&buffer[address_offset]); + MaybeCaptureMemoryAround(delegate, target_address); + } +} + +} // namespace + +// static +void CaptureMemory::PointedToByContext(const CPUContext& context, + Delegate* delegate) { +#if defined(ARCH_CPU_X86_FAMILY) + if (context.architecture == kCPUArchitectureX86_64) { + MaybeCaptureMemoryAround(delegate, context.x86_64->rax); + MaybeCaptureMemoryAround(delegate, context.x86_64->rbx); + MaybeCaptureMemoryAround(delegate, context.x86_64->rcx); + MaybeCaptureMemoryAround(delegate, context.x86_64->rdx); + MaybeCaptureMemoryAround(delegate, context.x86_64->rdi); + MaybeCaptureMemoryAround(delegate, context.x86_64->rsi); + MaybeCaptureMemoryAround(delegate, context.x86_64->rbp); + MaybeCaptureMemoryAround(delegate, context.x86_64->r8); + MaybeCaptureMemoryAround(delegate, context.x86_64->r9); + MaybeCaptureMemoryAround(delegate, context.x86_64->r10); + MaybeCaptureMemoryAround(delegate, context.x86_64->r11); + MaybeCaptureMemoryAround(delegate, context.x86_64->r12); + MaybeCaptureMemoryAround(delegate, context.x86_64->r13); + MaybeCaptureMemoryAround(delegate, context.x86_64->r14); + MaybeCaptureMemoryAround(delegate, context.x86_64->r15); + MaybeCaptureMemoryAround(delegate, context.x86_64->rip); + } else { + MaybeCaptureMemoryAround(delegate, context.x86->eax); + MaybeCaptureMemoryAround(delegate, context.x86->ebx); + MaybeCaptureMemoryAround(delegate, context.x86->ecx); + MaybeCaptureMemoryAround(delegate, context.x86->edx); + MaybeCaptureMemoryAround(delegate, context.x86->edi); + MaybeCaptureMemoryAround(delegate, context.x86->esi); + MaybeCaptureMemoryAround(delegate, context.x86->ebp); + MaybeCaptureMemoryAround(delegate, context.x86->eip); + } +#else +#error non-x86 +#endif +} + +// static +void CaptureMemory::PointedToByMemoryRange(const MemorySnapshot& memory, + Delegate* delegate) { + if (memory.Size() == 0) + return; + + const size_t alignment = + delegate->Is64Bit() ? sizeof(uint64_t) : sizeof(uint32_t); + if (memory.Address() % alignment != 0 || memory.Size() % alignment != 0) { + LOG(ERROR) << "unaligned range"; + return; + } + + scoped_ptr buffer(new uint8_t[memory.Size()]); + if (!delegate->ReadMemory(memory.Address(), memory.Size(), buffer.get())) { + LOG(ERROR) << "ReadMemory"; + return; + } + + if (delegate->Is64Bit()) + CaptureAtPointersInRange(buffer.get(), memory.Size(), delegate); + else + CaptureAtPointersInRange(buffer.get(), memory.Size(), delegate); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/capture_memory.h b/snapshot/capture_memory.h new file mode 100644 index 00000000..ef5f4ed9 --- /dev/null +++ b/snapshot/capture_memory.h @@ -0,0 +1,98 @@ +// Copyright 2016 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_CAPTURE_MEMORY_H_ +#define CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_ + +#include + +#include + +#include "snapshot/cpu_context.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { + +class MemorySnapshot; + +namespace internal { + +class CaptureMemory { + public: + //! \brief An interface to a platform-specific process reader. + class Delegate { + public: + virtual ~Delegate() {} + + //! \return `true` if the target process is a 64-bit process. + virtual bool Is64Bit() const = 0; + + //! \brief Attempts to read \a num_bytes bytes from the target process + //! starting at address \a at into \a into. + //! + //! \return `true` if the entire region could be read, or `false` with an + //! error logged. + virtual bool ReadMemory(uint64_t at, + uint64_t num_bytes, + void* into) const = 0; + + //! \brief Given a range to be read from the target process, returns a + //! vector + //! of ranges, representing the readable portions of the original range. + //! + //! \param[in] range The range being identified. + //! + //! \return A vector of ranges corresponding to the portion of \a range that + //! is readable. + virtual std::vector> GetReadableRanges( + const CheckedRange& range) const = 0; + + //! \brief Adds the given range representing a memory snapshot in the target + //! process to the result. + virtual void AddNewMemorySnapshot( + const CheckedRange& range) = 0; + }; + + //! \brief For all registers that appear to be pointer-like in \a context, + //! captures a small amount of memory near their pointed to location. + //! + //! "Pointer-like" in this context means not too close to zero (signed or + //! unsigned) so that there's a reasonable chance that the value is a pointer. + //! + //! \param[in] context The context to inspect. + //! \param[in] process_reader A MemoryCaptureProcessReader to read from the + //! target process, and that handles adding new ranges. + static void PointedToByContext(const CPUContext& context, Delegate* delegate); + + //! \brief For all pointer-like values in a memory range of the target + //! process, + //! captures a small amount of memory near the pointed to location. + //! + //! \param[in] memory An existing MemorySnapshot of the range to search. The + //! base address and size must be pointer-aligned and an integral number + //! of + //! pointers long. + //! \param[in] process_reader A MemoryCaptureProcessReader to read from the + //! target process, and that handles adding new ranges. + static void PointedToByMemoryRange(const MemorySnapshot& memory, + Delegate* delegate); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureMemory); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_ diff --git a/snapshot/crashpad_info_client_options_test.cc b/snapshot/crashpad_info_client_options_test.cc index b6d27301..50ba6310 100644 --- a/snapshot/crashpad_info_client_options_test.cc +++ b/snapshot/crashpad_info_client_options_test.cc @@ -70,14 +70,13 @@ class ScopedUnsetCrashpadInfoOptions { DISALLOW_COPY_AND_ASSIGN(ScopedUnsetCrashpadInfoOptions); }; -TEST(CrashpadInfoClientOptions, OneModule) { - // Make sure that the initial state has all values unset. +CrashpadInfoClientOptions SelfProcessSnapshotAndGetCrashpadOptions() { #if defined(OS_MACOSX) ProcessSnapshotMac process_snapshot; - ASSERT_TRUE(process_snapshot.Initialize(mach_task_self())); + EXPECT_TRUE(process_snapshot.Initialize(mach_task_self())); #elif defined(OS_WIN) ProcessSnapshotWin process_snapshot; - ASSERT_TRUE(process_snapshot.Initialize( + EXPECT_TRUE(process_snapshot.Initialize( GetCurrentProcess(), ProcessSuspensionState::kRunning, 0)); #else #error Port. @@ -85,6 +84,12 @@ TEST(CrashpadInfoClientOptions, OneModule) { CrashpadInfoClientOptions options; process_snapshot.GetCrashpadOptions(&options); + return options; +} + +TEST(CrashpadInfoClientOptions, OneModule) { + // Make sure that the initial state has all values unset. + auto options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); @@ -98,7 +103,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); @@ -109,7 +114,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { crashpad_info->set_system_crash_reporter_forwarding(TriState::kDisabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); @@ -120,7 +125,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { crashpad_info->set_gather_indirectly_referenced_memory(TriState::kEnabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kEnabled, options.gather_indirectly_referenced_memory); @@ -199,21 +204,9 @@ TEST(CrashpadInfoClientOptions, TwoModules) { dl_handle.LookUpSymbol("TestModule_GetCrashpadInfo"); ASSERT_TRUE(TestModule_GetCrashpadInfo); + auto options = SelfProcessSnapshotAndGetCrashpadOptions(); + // 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(), ProcessSuspensionState::kRunning, 0)); -#else -#error Port. -#endif // OS_MACOSX - - CrashpadInfoClientOptions options; - process_snapshot.GetCrashpadOptions(&options); - EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); @@ -232,7 +225,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { // When only one module sets a value, it applies to the entire process. remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); @@ -242,7 +235,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { // module, because the local module loaded the remote module. local_crashpad_info->set_crashpad_handler_behavior(TriState::kDisabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kDisabled, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); @@ -256,7 +249,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { remote_crashpad_info->set_system_crash_reporter_forwarding( TriState::kDisabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); @@ -267,7 +260,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { local_crashpad_info->set_system_crash_reporter_forwarding( TriState::kEnabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kEnabled, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index 0154495d..d0011528 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -30,6 +30,8 @@ '..', ], 'sources': [ + 'capture_memory.cc', + 'capture_memory.h', 'cpu_architecture.h', 'cpu_context.cc', 'cpu_context.h', @@ -89,12 +91,12 @@ 'process_snapshot.h', 'system_snapshot.h', 'thread_snapshot.h', - 'win/capture_context_memory.cc', - 'win/capture_context_memory.h', 'win/cpu_context_win.cc', 'win/cpu_context_win.h', 'win/exception_snapshot_win.cc', 'win/exception_snapshot_win.h', + 'win/capture_memory_delegate_win.cc', + 'win/capture_memory_delegate_win.h', 'win/memory_map_region_snapshot_win.cc', 'win/memory_map_region_snapshot_win.h', 'win/memory_snapshot_win.cc', diff --git a/snapshot/win/capture_context_memory.cc b/snapshot/win/capture_context_memory.cc deleted file mode 100644 index e4469008..00000000 --- a/snapshot/win/capture_context_memory.cc +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2016 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/capture_context_memory.h" - -#include - -#include - -#include "snapshot/win/memory_snapshot_win.h" -#include "snapshot/win/process_reader_win.h" - -namespace crashpad { -namespace internal { - -namespace { - -void MaybeCaptureMemoryAround(ProcessReaderWin* process_reader, - WinVMAddress address, - PointerVector* into) { - const WinVMAddress non_address_offset = 0x10000; - if (address < non_address_offset) - return; - if (process_reader->Is64Bit()) { - if (address >= std::numeric_limits::max() - non_address_offset) - return; - } else { - if (address >= std::numeric_limits::max() - non_address_offset) - return; - } - - const WinVMSize kRegisterByteOffset = 32; - const WinVMAddress target = address - kRegisterByteOffset; - const WinVMSize size = 128; - auto ranges = process_reader->GetProcessInfo().GetReadableRanges( - CheckedRange(target, size)); - for (const auto& range : ranges) { - internal::MemorySnapshotWin* snapshot = new internal::MemorySnapshotWin(); - snapshot->Initialize(process_reader, range.base(), range.size()); - into->push_back(snapshot); - } -} - -} // namespace - -void CaptureMemoryPointedToByContext(const CPUContext& context, - ProcessReaderWin* process_reader, - const ProcessReaderWin::Thread& thread, - PointerVector* into) { -#if defined(ARCH_CPU_X86_64) - if (context.architecture == kCPUArchitectureX86_64) { - MaybeCaptureMemoryAround(process_reader, context.x86_64->rax, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->rbx, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->rcx, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->rdx, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->rdi, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->rsi, into); - if (context.x86_64->rbp < thread.stack_region_address || - context.x86_64->rbp >= - thread.stack_region_address + thread.stack_region_size) { - MaybeCaptureMemoryAround(process_reader, context.x86_64->rbp, into); - } - MaybeCaptureMemoryAround(process_reader, context.x86_64->r8, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r9, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r10, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r11, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r12, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r13, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r14, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r15, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->rip, into); - } else { -#endif - MaybeCaptureMemoryAround(process_reader, context.x86->eax, into); - MaybeCaptureMemoryAround(process_reader, context.x86->ebx, into); - MaybeCaptureMemoryAround(process_reader, context.x86->ecx, into); - MaybeCaptureMemoryAround(process_reader, context.x86->edx, into); - MaybeCaptureMemoryAround(process_reader, context.x86->edi, into); - MaybeCaptureMemoryAround(process_reader, context.x86->esi, into); - if (context.x86->ebp < thread.stack_region_address || - context.x86->ebp >= - thread.stack_region_address + thread.stack_region_size) { - MaybeCaptureMemoryAround(process_reader, context.x86->ebp, into); - } - MaybeCaptureMemoryAround(process_reader, context.x86->eip, into); -#if defined(ARCH_CPU_X86_64) - } -#endif -} - -} // namespace internal -} // namespace crashpad diff --git a/snapshot/win/capture_context_memory.h b/snapshot/win/capture_context_memory.h deleted file mode 100644 index d4f4e038..00000000 --- a/snapshot/win/capture_context_memory.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 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_CAPTURE_CONTEXT_MEMORY_H_ -#define CRASHPAD_SNAPSHOT_WIN_CAPTURE_CONTEXT_MEMORY_H_ - -#include "snapshot/cpu_context.h" -#include "snapshot/win/process_reader_win.h" -#include "util/stdlib/pointer_container.h" - -namespace crashpad { -namespace internal { - -class MemorySnapshotWin; - -//! \brief For all registers that appear to be pointer-like in \a context, -//! captures a small amount of memory near their pointed to location. -//! -//! \param[in] context The context to inspect. -//! \param[in] process_reader A ProcessReaderWin to read from the target -//! process. -//! \param[in] thread The thread to which the context belongs. -//! \param[out] into A vector of pointers to append new ranges to. -void CaptureMemoryPointedToByContext(const CPUContext& context, - ProcessReaderWin* process_reader, - const ProcessReaderWin::Thread& thread, - PointerVector* into); - -} // namespace internal -} // namespace crashpad - -#endif // CRASHPAD_SNAPSHOT_WIN_CAPTURE_CONTEXT_MEMORY_H_ diff --git a/snapshot/win/capture_memory_delegate_win.cc b/snapshot/win/capture_memory_delegate_win.cc new file mode 100644 index 00000000..1eaa4258 --- /dev/null +++ b/snapshot/win/capture_memory_delegate_win.cc @@ -0,0 +1,56 @@ +// Copyright 2016 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/capture_memory_delegate_win.h" + +#include "snapshot/win/memory_snapshot_win.h" + +namespace crashpad { +namespace internal { + +CaptureMemoryDelegateWin::CaptureMemoryDelegateWin( + ProcessReaderWin* process_reader, + const ProcessReaderWin::Thread& thread, + PointerVector* snapshots) + : stack_(thread.stack_region_address, thread.stack_region_size), + process_reader_(process_reader), + snapshots_(snapshots) {} + +bool CaptureMemoryDelegateWin::Is64Bit() const { + return process_reader_->Is64Bit(); +} + +bool CaptureMemoryDelegateWin::ReadMemory(uint64_t at, + uint64_t num_bytes, + void* into) const { + return process_reader_->ReadMemory(at, num_bytes, into); +} + +std::vector> CaptureMemoryDelegateWin::GetReadableRanges( + const CheckedRange& range) const { + return process_reader_->GetProcessInfo().GetReadableRanges(range); +} + +void CaptureMemoryDelegateWin::AddNewMemorySnapshot( + const CheckedRange& range) { + // Don't bother storing this memory if it points back into the stack. + if (stack_.ContainsRange(range)) + return; + internal::MemorySnapshotWin* snapshot = new internal::MemorySnapshotWin(); + snapshot->Initialize(process_reader_, range.base(), range.size()); + snapshots_->push_back(snapshot); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/win/capture_memory_delegate_win.h b/snapshot/win/capture_memory_delegate_win.h new file mode 100644 index 00000000..e408e549 --- /dev/null +++ b/snapshot/win/capture_memory_delegate_win.h @@ -0,0 +1,58 @@ + // Copyright 2016 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_CAPTURE_MEMORY_DELEGATE_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_ + +#include "snapshot/capture_memory.h" + +#include "snapshot/win/process_reader_win.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { +namespace internal { + +class MemorySnapshotWin; + +class CaptureMemoryDelegateWin : public CaptureMemory::Delegate { + public: + //! \brief A MemoryCaptureDelegate for Windows. + //! + //! \param[in] process_reader A ProcessReaderWin for the target process. + //! \param[in] thread The thread being inspected. Memory ranges overlapping + //! this thread's stack will be ignored on the assumption that they're + //! already captured elsewhere. + //! \param[in] snapshots A vector of MemorySnapshotWin to which the captured + //! memory will be added. + CaptureMemoryDelegateWin(ProcessReaderWin* process_reader, + const ProcessReaderWin::Thread& thread, + PointerVector* snapshots); + + // MemoryCaptureDelegate: + bool Is64Bit() const override; + bool ReadMemory(uint64_t at, uint64_t num_bytes, void* into) const override; + std::vector> GetReadableRanges( + const CheckedRange& range) const override; + void AddNewMemorySnapshot(const CheckedRange& range); + + private: + CheckedRange stack_; + ProcessReaderWin* process_reader_; + PointerVector* snapshots_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_ diff --git a/snapshot/win/end_to_end_test.py b/snapshot/win/end_to_end_test.py index 34343ba6..bf4f3034 100755 --- a/snapshot/win/end_to_end_test.py +++ b/snapshot/win/end_to_end_test.py @@ -247,10 +247,22 @@ def RunTests(cdb_path, r'FreeOwnStackAndBreak.*\nquit:', 'at correct location, no additional stack entries') + # Switch to the other thread after jumping to the exception, and examine + # memory. out = CdbRun(cdb_path, dump_path, '.ecxr; ~1s; db /c14 edi') out.Check(r'63 62 61 60 5f 5e 5d 5c-5b 5a 59 58 57 56 55 54 53 52 51 50', 'data pointed to by registers captured') + # Move up one stack frame after jumping to the exception, and examine memory. + out = CdbRun(cdb_path, dump_path, + '.ecxr; .f+; dd /c100 poi(offset_pointer)-20') + out.Check(r'80000078 00000079 8000007a 0000007b 8000007c 0000007d 8000007e ' + r'0000007f 80000080 00000081 80000082 00000083 80000084 00000085 ' + r'80000086 00000087 80000088 00000089 8000008a 0000008b 8000008c ' + r'0000008d 8000008e 0000008f 80000090 00000091 80000092 00000093 ' + r'80000094 00000095 80000096 00000097', + 'data pointed to by stack captured') + if z7_dump_path: out = CdbRun(cdb_path, z7_dump_path, '.ecxr;lm') out.Check('This dump file has an exception of interest stored in it', diff --git a/snapshot/win/exception_snapshot_win.cc b/snapshot/win/exception_snapshot_win.cc index 9ec16f69..ea44f0fc 100644 --- a/snapshot/win/exception_snapshot_win.cc +++ b/snapshot/win/exception_snapshot_win.cc @@ -14,9 +14,10 @@ #include "snapshot/win/exception_snapshot_win.h" +#include "snapshot/capture_memory.h" #include "snapshot/memory_snapshot.h" -#include "snapshot/win/capture_context_memory.h" #include "snapshot/win/cpu_context_win.h" +#include "snapshot/win/capture_memory_delegate_win.h" #include "snapshot/win/memory_snapshot_win.h" #include "snapshot/win/process_reader_win.h" #include "util/win/nt_internals.h" @@ -89,8 +90,9 @@ bool ExceptionSnapshotWin::Initialize(ProcessReaderWin* process_reader, InitializeX86Context(context_record, context_.x86); } - CaptureMemoryPointedToByContext( - context_, process_reader, *thread, &extra_memory_); + CaptureMemoryDelegateWin capture_memory_delegate( + process_reader, *thread, &extra_memory_); + CaptureMemory::PointedToByContext(context_, &capture_memory_delegate); INITIALIZATION_STATE_SET_VALID(initialized_); return true; diff --git a/snapshot/win/process_snapshot_win.cc b/snapshot/win/process_snapshot_win.cc index 99e52937..535c9a73 100644 --- a/snapshot/win/process_snapshot_win.cc +++ b/snapshot/win/process_snapshot_win.cc @@ -38,6 +38,7 @@ ProcessSnapshotWin::ProcessSnapshotWin() client_id_(), annotations_simple_map_(), snapshot_time_(), + options_(), initialized_() { } @@ -65,9 +66,13 @@ bool ProcessSnapshotWin::Initialize( debug_critical_section_address); } - InitializeThreads(); InitializeModules(); + GetCrashpadOptionsInternal(&options_); + + InitializeThreads(options_.gather_indirectly_referenced_memory == + TriState::kEnabled); + for (const MEMORY_BASIC_INFORMATION64& mbi : process_reader_.GetProcessInfo().MemoryInfo()) { memory_map_.push_back(new internal::MemoryMapRegionSnapshotWin(mbi)); @@ -104,35 +109,7 @@ bool ProcessSnapshotWin::InitializeException( 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 (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { - local_options.gather_indirectly_referenced_memory = - module_options.gather_indirectly_referenced_memory; - } - - // 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; + *options = options_; } pid_t ProcessSnapshotWin::ProcessID() const { @@ -238,13 +215,16 @@ std::vector ProcessSnapshotWin::ExtraMemory() const { return extra_memory; } -void ProcessSnapshotWin::InitializeThreads() { +void ProcessSnapshotWin::InitializeThreads( + bool gather_indirectly_referenced_memory) { const std::vector& process_reader_threads = process_reader_.Threads(); for (const ProcessReaderWin::Thread& process_reader_thread : process_reader_threads) { auto thread = make_scoped_ptr(new internal::ThreadSnapshotWin()); - if (thread->Initialize(&process_reader_, process_reader_thread)) { + if (thread->Initialize(&process_reader_, + process_reader_thread, + gather_indirectly_referenced_memory)) { threads_.push_back(thread.release()); } } @@ -262,6 +242,38 @@ void ProcessSnapshotWin::InitializeModules() { } } +void ProcessSnapshotWin::GetCrashpadOptionsInternal( + CrashpadInfoClientOptions* options) { + 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 (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { + local_options.gather_indirectly_referenced_memory = + module_options.gather_indirectly_referenced_memory; + } + + // 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; +} + template void ProcessSnapshotWin::InitializePebData( WinVMAddress debug_critical_section_address) { diff --git a/snapshot/win/process_snapshot_win.h b/snapshot/win/process_snapshot_win.h index 64e73da1..c2307bee 100644 --- a/snapshot/win/process_snapshot_win.h +++ b/snapshot/win/process_snapshot_win.h @@ -141,11 +141,14 @@ class ProcessSnapshotWin final : public ProcessSnapshot { private: // Initializes threads_ on behalf of Initialize(). - void InitializeThreads(); + void InitializeThreads(bool gather_indirectly_referenced_memory); // Initializes modules_ on behalf of Initialize(). void InitializeModules(); + // Initializes options_ on behalf of Initialize(). + void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options); + // Initializes various memory blocks reachable from the PEB on behalf of // Initialize(). template @@ -186,6 +189,7 @@ class ProcessSnapshotWin final : public ProcessSnapshot { UUID client_id_; std::map annotations_simple_map_; timeval snapshot_time_; + CrashpadInfoClientOptions options_; InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotWin); diff --git a/snapshot/win/thread_snapshot_win.cc b/snapshot/win/thread_snapshot_win.cc index f3446e98..a240eb2a 100644 --- a/snapshot/win/thread_snapshot_win.cc +++ b/snapshot/win/thread_snapshot_win.cc @@ -17,8 +17,9 @@ #include #include "base/logging.h" -#include "snapshot/win/capture_context_memory.h" +#include "snapshot/capture_memory.h" #include "snapshot/win/cpu_context_win.h" +#include "snapshot/win/capture_memory_delegate_win.h" #include "snapshot/win/process_reader_win.h" namespace crashpad { @@ -38,7 +39,8 @@ ThreadSnapshotWin::~ThreadSnapshotWin() { bool ThreadSnapshotWin::Initialize( ProcessReaderWin* process_reader, - const ProcessReaderWin::Thread& process_reader_thread) { + const ProcessReaderWin::Thread& process_reader_thread, + bool gather_indirectly_referenced_memory) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); thread_ = process_reader_thread; @@ -76,8 +78,11 @@ bool ThreadSnapshotWin::Initialize( InitializeX86Context(process_reader_thread.context.native, context_.x86); #endif // ARCH_CPU_X86_64 - CaptureMemoryPointedToByContext( - context_, process_reader, thread_, &pointed_to_memory_); + CaptureMemoryDelegateWin capture_memory_delegate( + process_reader, thread_, &pointed_to_memory_); + CaptureMemory::PointedToByContext(context_, &capture_memory_delegate); + if (gather_indirectly_referenced_memory) + CaptureMemory::PointedToByMemoryRange(stack_, &capture_memory_delegate); INITIALIZATION_STATE_SET_VALID(initialized_); return true; diff --git a/snapshot/win/thread_snapshot_win.h b/snapshot/win/thread_snapshot_win.h index 74e6bc6e..d746ddb4 100644 --- a/snapshot/win/thread_snapshot_win.h +++ b/snapshot/win/thread_snapshot_win.h @@ -48,11 +48,14 @@ class ThreadSnapshotWin final : public ThreadSnapshot { //! the thread. //! \param[in] process_reader_thread The thread within the ProcessReaderWin //! for which the snapshot should be created. + //! \param[in] gather_indirectly_referenced_memory If `true`, adds extra + //! memory regions to the snapshot pointed to by the thread's stack. //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. bool Initialize(ProcessReaderWin* process_reader, - const ProcessReaderWin::Thread& process_reader_thread); + const ProcessReaderWin::Thread& process_reader_thread, + bool gather_indirectly_referenced_memory); // ThreadSnapshot: