crashpad/snapshot/win/process_reader_win.cc

485 lines
16 KiB
C++
Raw Normal View History

// Copyright 2015 The Crashpad Authors
//
// 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 <string.h>
#include <winternl.h>
#include <memory>
#include "base/check_op.h"
Captures shadow stack registers for x64 Windows contexts Windows extended contexts must be allocated by InitializeContext2 and may not be aligned. This means we cannot simply store a struct in our thread snapshot object, but must instead store enough memory and alias our struct onto this backing memory. Note that shadow stack pointers are not yet recorded for the initial exception - this cannot be determined using LocateXStateFeature in the capturing process and will be added in a future CL by plumbing through client messages when a crashed process requests a dump. See crash/32bd2c53a252705c for an example dump with this baked into chrome, that has passed through breakpad without breaking it. Local testing shows this creates valid dumps when built into Chrome, but that the referenced memory limits may need to be increased to allow for ssp referenced memory to be included. See "MANAGING STATE USING THE XSAVE FEATURE SET" Chapter 13 in the Intel SDM[0]. Many of the offsets and sizes of the extended features are provided by cpu specific values. We can access these in Windows using the SDK, and transfer these to the saved extended context which in turn is understandable by windbg. Further information is available from AMD Ch. 18 "Shadow Stacks"[1]. [0] https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html. [1] https://www.amd.com/system/files/TechDocs/24593.pdf Bug: 1250098 Change-Id: I4b13bcb023e9d5fba257044abfd7e251d66a9329 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3300992 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Alex Gough <ajgo@chromium.org>
2022-05-16 15:38:37 -07:00
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/utf_string_conversions.h"
Captures shadow stack registers for x64 Windows contexts Windows extended contexts must be allocated by InitializeContext2 and may not be aligned. This means we cannot simply store a struct in our thread snapshot object, but must instead store enough memory and alias our struct onto this backing memory. Note that shadow stack pointers are not yet recorded for the initial exception - this cannot be determined using LocateXStateFeature in the capturing process and will be added in a future CL by plumbing through client messages when a crashed process requests a dump. See crash/32bd2c53a252705c for an example dump with this baked into chrome, that has passed through breakpad without breaking it. Local testing shows this creates valid dumps when built into Chrome, but that the referenced memory limits may need to be increased to allow for ssp referenced memory to be included. See "MANAGING STATE USING THE XSAVE FEATURE SET" Chapter 13 in the Intel SDM[0]. Many of the offsets and sizes of the extended features are provided by cpu specific values. We can access these in Windows using the SDK, and transfer these to the saved extended context which in turn is understandable by windbg. Further information is available from AMD Ch. 18 "Shadow Stacks"[1]. [0] https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html. [1] https://www.amd.com/system/files/TechDocs/24593.pdf Bug: 1250098 Change-Id: I4b13bcb023e9d5fba257044abfd7e251d66a9329 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3300992 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Alex Gough <ajgo@chromium.org>
2022-05-16 15:38:37 -07:00
#include "snapshot/win/cpu_context_win.h"
#include "util/misc/capture_context.h"
#include "util/misc/time.h"
#include "util/win/get_function.h"
#include "util/win/nt_internals.h"
#include "util/win/ntstatus_logging.h"
#include "util/win/process_structs.h"
#include "util/win/scoped_handle.h"
#include "util/win/scoped_local_alloc.h"
namespace crashpad {
namespace {
// Gets a pointer to the process information structure after a given one, or
// null when iteration is complete, assuming they've been retrieved in a block
// via NtQuerySystemInformation().
template <class Traits>
process_types::SYSTEM_PROCESS_INFORMATION<Traits>* NextProcess(
process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process) {
ULONG offset = process->NextEntryOffset;
if (offset == 0)
return nullptr;
return reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>(
reinterpret_cast<uint8_t*>(process) + offset);
}
//! \brief Retrieves the SYSTEM_PROCESS_INFORMATION for a given process.
//!
//! The returned pointer points into the memory block stored by \a buffer.
//! Ownership of \a buffer is transferred to the caller.
//!
//! \return Pointer to the process' data, or nullptr if it was not found or on
//! error. On error, a message will be logged.
template <class Traits>
process_types::SYSTEM_PROCESS_INFORMATION<Traits>* GetProcessInformation(
HANDLE process_handle,
std::unique_ptr<uint8_t[]>* buffer) {
ULONG buffer_size = 16384;
ULONG actual_size;
buffer->reset(new uint8_t[buffer_size]);
NTSTATUS status;
// This must be in retry loop, as we're racing with process creation on the
// system to find a buffer large enough to hold all process information.
for (int tries = 0; tries < 20; ++tries) {
status = crashpad::NtQuerySystemInformation(
SystemProcessInformation,
reinterpret_cast<void*>(buffer->get()),
buffer_size,
&actual_size);
if (status == STATUS_BUFFER_TOO_SMALL ||
status == STATUS_INFO_LENGTH_MISMATCH) {
DCHECK_GT(actual_size, buffer_size);
// Add a little extra to try to avoid an additional loop iteration. We're
// racing with system-wide process creation between here and the next call
// to NtQuerySystemInformation().
buffer_size = actual_size + 4096;
// Free the old buffer before attempting to allocate a new one.
buffer->reset();
buffer->reset(new uint8_t[buffer_size]);
} else {
break;
}
}
if (!NT_SUCCESS(status)) {
NTSTATUS_LOG(ERROR, status) << "NtQuerySystemInformation";
return nullptr;
}
DCHECK_LE(actual_size, buffer_size);
process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process =
reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>(
buffer->get());
DWORD process_id = GetProcessId(process_handle);
for (;;) {
if (process->UniqueProcessId == process_id)
return process;
process = NextProcess(process);
if (!process)
break;
}
LOG(ERROR) << "process " << process_id << " not found";
return nullptr;
}
template <class Traits>
HANDLE OpenThread(
const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info) {
HANDLE handle;
ACCESS_MASK query_access =
THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION;
OBJECT_ATTRIBUTES object_attributes;
InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr);
NTSTATUS status = crashpad::NtOpenThread(
&handle, query_access, &object_attributes, &thread_info.ClientId);
if (!NT_SUCCESS(status)) {
NTSTATUS_LOG(ERROR, status) << "NtOpenThread";
return nullptr;
}
return handle;
}
// It's necessary to suspend the thread to grab CONTEXT. SuspendThread has a
// side-effect of returning the SuspendCount of the thread on success, so we
// fill out these two pieces of semi-unrelated data in the same function.
template <class Traits>
bool FillThreadContextAndSuspendCount(HANDLE thread_handle,
ProcessReaderWin::Thread* thread,
ProcessSuspensionState suspension_state,
bool is_64_reading_32) {
// Don't suspend the thread if it's this thread. This is really only for test
// binaries, as we won't be walking ourselves, in general.
bool is_current_thread = thread->id ==
reinterpret_cast<process_types::TEB<Traits>*>(
NtCurrentTeb())->ClientId.UniqueThread;
if (is_current_thread) {
DCHECK(suspension_state == ProcessSuspensionState::kRunning);
thread->suspend_count = 0;
DCHECK(!is_64_reading_32);
thread->context.InitializeFromCurrentThread();
} else {
DWORD previous_suspend_count = SuspendThread(thread_handle);
if (previous_suspend_count == static_cast<DWORD>(-1)) {
PLOG(ERROR) << "SuspendThread";
return false;
}
if (previous_suspend_count <= 0 &&
suspension_state == ProcessSuspensionState::kSuspended) {
LOG(WARNING) << "Thread " << thread->id
<< " should be suspended, but previous_suspend_count is "
<< previous_suspend_count;
thread->suspend_count = 0;
} else {
thread->suspend_count =
previous_suspend_count -
(suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0);
}
#if defined(ARCH_CPU_32_BITS)
Captures shadow stack registers for x64 Windows contexts Windows extended contexts must be allocated by InitializeContext2 and may not be aligned. This means we cannot simply store a struct in our thread snapshot object, but must instead store enough memory and alias our struct onto this backing memory. Note that shadow stack pointers are not yet recorded for the initial exception - this cannot be determined using LocateXStateFeature in the capturing process and will be added in a future CL by plumbing through client messages when a crashed process requests a dump. See crash/32bd2c53a252705c for an example dump with this baked into chrome, that has passed through breakpad without breaking it. Local testing shows this creates valid dumps when built into Chrome, but that the referenced memory limits may need to be increased to allow for ssp referenced memory to be included. See "MANAGING STATE USING THE XSAVE FEATURE SET" Chapter 13 in the Intel SDM[0]. Many of the offsets and sizes of the extended features are provided by cpu specific values. We can access these in Windows using the SDK, and transfer these to the saved extended context which in turn is understandable by windbg. Further information is available from AMD Ch. 18 "Shadow Stacks"[1]. [0] https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html. [1] https://www.amd.com/system/files/TechDocs/24593.pdf Bug: 1250098 Change-Id: I4b13bcb023e9d5fba257044abfd7e251d66a9329 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3300992 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Alex Gough <ajgo@chromium.org>
2022-05-16 15:38:37 -07:00
if (!thread->context.InitializeNative(thread_handle))
return false;
#endif // ARCH_CPU_32_BITS
#if defined(ARCH_CPU_64_BITS)
if (is_64_reading_32) {
if (!thread->context.InitializeWow64(thread_handle))
return false;
#if defined(ARCH_CPU_X86_64)
Captures shadow stack registers for x64 Windows contexts Windows extended contexts must be allocated by InitializeContext2 and may not be aligned. This means we cannot simply store a struct in our thread snapshot object, but must instead store enough memory and alias our struct onto this backing memory. Note that shadow stack pointers are not yet recorded for the initial exception - this cannot be determined using LocateXStateFeature in the capturing process and will be added in a future CL by plumbing through client messages when a crashed process requests a dump. See crash/32bd2c53a252705c for an example dump with this baked into chrome, that has passed through breakpad without breaking it. Local testing shows this creates valid dumps when built into Chrome, but that the referenced memory limits may need to be increased to allow for ssp referenced memory to be included. See "MANAGING STATE USING THE XSAVE FEATURE SET" Chapter 13 in the Intel SDM[0]. Many of the offsets and sizes of the extended features are provided by cpu specific values. We can access these in Windows using the SDK, and transfer these to the saved extended context which in turn is understandable by windbg. Further information is available from AMD Ch. 18 "Shadow Stacks"[1]. [0] https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html. [1] https://www.amd.com/system/files/TechDocs/24593.pdf Bug: 1250098 Change-Id: I4b13bcb023e9d5fba257044abfd7e251d66a9329 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3300992 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Alex Gough <ajgo@chromium.org>
2022-05-16 15:38:37 -07:00
} else if (IsXStateFeatureEnabled(XSTATE_MASK_CET_U)) {
if (!thread->context.InitializeXState(thread_handle, XSTATE_MASK_CET_U))
return false;
#endif // ARCH_CPU_X86_64
Captures shadow stack registers for x64 Windows contexts Windows extended contexts must be allocated by InitializeContext2 and may not be aligned. This means we cannot simply store a struct in our thread snapshot object, but must instead store enough memory and alias our struct onto this backing memory. Note that shadow stack pointers are not yet recorded for the initial exception - this cannot be determined using LocateXStateFeature in the capturing process and will be added in a future CL by plumbing through client messages when a crashed process requests a dump. See crash/32bd2c53a252705c for an example dump with this baked into chrome, that has passed through breakpad without breaking it. Local testing shows this creates valid dumps when built into Chrome, but that the referenced memory limits may need to be increased to allow for ssp referenced memory to be included. See "MANAGING STATE USING THE XSAVE FEATURE SET" Chapter 13 in the Intel SDM[0]. Many of the offsets and sizes of the extended features are provided by cpu specific values. We can access these in Windows using the SDK, and transfer these to the saved extended context which in turn is understandable by windbg. Further information is available from AMD Ch. 18 "Shadow Stacks"[1]. [0] https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html. [1] https://www.amd.com/system/files/TechDocs/24593.pdf Bug: 1250098 Change-Id: I4b13bcb023e9d5fba257044abfd7e251d66a9329 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3300992 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Alex Gough <ajgo@chromium.org>
2022-05-16 15:38:37 -07:00
} else {
if (!thread->context.InitializeNative(thread_handle))
return false;
}
#endif // ARCH_CPU_64_BITS
if (!ResumeThread(thread_handle)) {
PLOG(ERROR) << "ResumeThread";
return false;
}
}
return true;
}
} // namespace
ProcessReaderWin::ThreadContext::ThreadContext()
: offset_(0), initialized_(false), data_() {}
void ProcessReaderWin::ThreadContext::InitializeFromCurrentThread() {
data_.resize(sizeof(CONTEXT));
initialized_ = true;
CaptureContext(context<CONTEXT>());
}
bool ProcessReaderWin::ThreadContext::InitializeNative(HANDLE thread_handle) {
data_.resize(sizeof(CONTEXT));
initialized_ = true;
context<CONTEXT>()->ContextFlags = CONTEXT_ALL;
if (!GetThreadContext(thread_handle, context<CONTEXT>())) {
PLOG(ERROR) << "GetThreadContext";
return false;
}
return true;
}
#if defined(ARCH_CPU_64_BITS)
bool ProcessReaderWin::ThreadContext::InitializeWow64(HANDLE thread_handle) {
data_.resize(sizeof(WOW64_CONTEXT));
initialized_ = true;
context<WOW64_CONTEXT>()->ContextFlags = CONTEXT_ALL;
if (!Wow64GetThreadContext(thread_handle, context<WOW64_CONTEXT>())) {
PLOG(ERROR) << "Wow64GetThreadContext";
return false;
}
return true;
}
#endif
#if defined(ARCH_CPU_X86_64)
Captures shadow stack registers for x64 Windows contexts Windows extended contexts must be allocated by InitializeContext2 and may not be aligned. This means we cannot simply store a struct in our thread snapshot object, but must instead store enough memory and alias our struct onto this backing memory. Note that shadow stack pointers are not yet recorded for the initial exception - this cannot be determined using LocateXStateFeature in the capturing process and will be added in a future CL by plumbing through client messages when a crashed process requests a dump. See crash/32bd2c53a252705c for an example dump with this baked into chrome, that has passed through breakpad without breaking it. Local testing shows this creates valid dumps when built into Chrome, but that the referenced memory limits may need to be increased to allow for ssp referenced memory to be included. See "MANAGING STATE USING THE XSAVE FEATURE SET" Chapter 13 in the Intel SDM[0]. Many of the offsets and sizes of the extended features are provided by cpu specific values. We can access these in Windows using the SDK, and transfer these to the saved extended context which in turn is understandable by windbg. Further information is available from AMD Ch. 18 "Shadow Stacks"[1]. [0] https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html. [1] https://www.amd.com/system/files/TechDocs/24593.pdf Bug: 1250098 Change-Id: I4b13bcb023e9d5fba257044abfd7e251d66a9329 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3300992 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Alex Gough <ajgo@chromium.org>
2022-05-16 15:38:37 -07:00
bool ProcessReaderWin::ThreadContext::InitializeXState(
HANDLE thread_handle,
ULONG64 XStateCompactionMask) {
// InitializeContext2 needs Windows 10 build 20348.
static const auto initialize_context_2 =
GET_FUNCTION(L"kernel32.dll", ::InitializeContext2);
Captures shadow stack registers for x64 Windows contexts Windows extended contexts must be allocated by InitializeContext2 and may not be aligned. This means we cannot simply store a struct in our thread snapshot object, but must instead store enough memory and alias our struct onto this backing memory. Note that shadow stack pointers are not yet recorded for the initial exception - this cannot be determined using LocateXStateFeature in the capturing process and will be added in a future CL by plumbing through client messages when a crashed process requests a dump. See crash/32bd2c53a252705c for an example dump with this baked into chrome, that has passed through breakpad without breaking it. Local testing shows this creates valid dumps when built into Chrome, but that the referenced memory limits may need to be increased to allow for ssp referenced memory to be included. See "MANAGING STATE USING THE XSAVE FEATURE SET" Chapter 13 in the Intel SDM[0]. Many of the offsets and sizes of the extended features are provided by cpu specific values. We can access these in Windows using the SDK, and transfer these to the saved extended context which in turn is understandable by windbg. Further information is available from AMD Ch. 18 "Shadow Stacks"[1]. [0] https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html. [1] https://www.amd.com/system/files/TechDocs/24593.pdf Bug: 1250098 Change-Id: I4b13bcb023e9d5fba257044abfd7e251d66a9329 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3300992 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Alex Gough <ajgo@chromium.org>
2022-05-16 15:38:37 -07:00
if (!initialize_context_2)
return false;
// We want CET_U xstate to get the ssp, only possible when supported.
PCONTEXT ret_context = nullptr;
DWORD context_size = 0;
if (!initialize_context_2(nullptr,
CONTEXT_ALL | CONTEXT_XSTATE,
&ret_context,
&context_size,
XStateCompactionMask) &&
GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
PLOG(ERROR) << "InitializeContext2 - getting required size";
return false;
}
// NB: ret_context may not be data.begin().
data_.resize(context_size);
if (!initialize_context_2(data_.data(),
CONTEXT_ALL | CONTEXT_XSTATE,
&ret_context,
&context_size,
XStateCompactionMask)) {
PLOG(ERROR) << "InitializeContext2 - initializing";
return false;
}
offset_ = reinterpret_cast<unsigned char*>(ret_context) - data_.data();
initialized_ = true;
if (!GetThreadContext(thread_handle, ret_context)) {
PLOG(ERROR) << "GetThreadContext";
return false;
}
return true;
}
#endif // defined(ARCH_CPU_X86_64)
Captures shadow stack registers for x64 Windows contexts Windows extended contexts must be allocated by InitializeContext2 and may not be aligned. This means we cannot simply store a struct in our thread snapshot object, but must instead store enough memory and alias our struct onto this backing memory. Note that shadow stack pointers are not yet recorded for the initial exception - this cannot be determined using LocateXStateFeature in the capturing process and will be added in a future CL by plumbing through client messages when a crashed process requests a dump. See crash/32bd2c53a252705c for an example dump with this baked into chrome, that has passed through breakpad without breaking it. Local testing shows this creates valid dumps when built into Chrome, but that the referenced memory limits may need to be increased to allow for ssp referenced memory to be included. See "MANAGING STATE USING THE XSAVE FEATURE SET" Chapter 13 in the Intel SDM[0]. Many of the offsets and sizes of the extended features are provided by cpu specific values. We can access these in Windows using the SDK, and transfer these to the saved extended context which in turn is understandable by windbg. Further information is available from AMD Ch. 18 "Shadow Stacks"[1]. [0] https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html. [1] https://www.amd.com/system/files/TechDocs/24593.pdf Bug: 1250098 Change-Id: I4b13bcb023e9d5fba257044abfd7e251d66a9329 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3300992 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Alex Gough <ajgo@chromium.org>
2022-05-16 15:38:37 -07:00
ProcessReaderWin::Thread::Thread()
: context(),
name(),
id(0),
teb_address(0),
teb_size(0),
stack_region_address(0),
stack_region_size(0),
suspend_count(0),
priority_class(0),
priority(0) {}
ProcessReaderWin::ProcessReaderWin()
: process_(INVALID_HANDLE_VALUE),
process_info_(),
process_memory_(),
threads_(),
modules_(),
suspension_state_(),
initialized_threads_(false),
initialized_() {
}
ProcessReaderWin::~ProcessReaderWin() {
}
bool ProcessReaderWin::Initialize(HANDLE process,
ProcessSuspensionState suspension_state) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
process_ = process;
suspension_state_ = suspension_state;
if (!process_info_.Initialize(process))
return false;
if (!process_memory_.Initialize(process))
return false;
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
bool ProcessReaderWin::StartTime(timeval* start_time) const {
FILETIME creation, exit, kernel, user;
if (!GetProcessTimes(process_, &creation, &exit, &kernel, &user)) {
PLOG(ERROR) << "GetProcessTimes";
return false;
}
*start_time = FiletimeToTimevalEpoch(creation);
return true;
}
bool ProcessReaderWin::CPUTimes(timeval* user_time,
timeval* system_time) const {
FILETIME creation, exit, kernel, user;
if (!GetProcessTimes(process_, &creation, &exit, &kernel, &user)) {
PLOG(ERROR) << "GetProcessTimes";
return false;
}
*user_time = FiletimeToTimevalInterval(user);
*system_time = FiletimeToTimevalInterval(kernel);
return true;
}
const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (initialized_threads_)
return threads_;
initialized_threads_ = true;
#if defined(ARCH_CPU_64_BITS)
ReadThreadData<process_types::internal::Traits64>(process_info_.IsWow64());
#else
ReadThreadData<process_types::internal::Traits32>(false);
#endif
return threads_;
}
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_;
}
win: Save contents of PEB to minidump to start making !peb work This makes the basics of !peb work in windbg, however, pointed-to things are not yet retrieved. For full functionality, a variety of pointers in the PEB also needs to be walked and captured. e.g. Previously: 0:000> .ecxr eax=00000007 ebx=7e383000 ecx=c3f9a943 edx=00000000 esi=006d62d0 edi=003c9280 eip=00384828 esp=005bf634 ebp=005bf638 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246 crashy_program!crashpad::`anonymous namespace'::SomeCrashyFunction+0x28: 00384828 c7002a000000 mov dword ptr [eax],2Ah ds:002b:00000007=???????? 0:000> !peb PEB at 7e383000 error 1 InitTypeRead( nt!_PEB at 7e383000)... Now: 0:000> .ecxr eax=00000007 ebx=7f958000 ecx=02102f4d edx=00000000 esi=00e162d0 edi=01389280 eip=01344828 esp=00c2fb64 ebp=00c2fb68 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246 crashy_program!crashpad::`anonymous namespace'::SomeCrashyFunction+0x28: 01344828 c7002a000000 mov dword ptr [eax],2Ah ds:002b:00000007=???????? 0:000> !peb PEB at 7f958000 InheritedAddressSpace: No ReadImageFileExecOptions: No BeingDebugged: No ImageBaseAddress: 01340000 Ldr 77ec8b40 *** unable to read Ldr table at 77ec8b40 SubSystemData: 00000000 ProcessHeap: 00e10000 ProcessParameters: 00e114e0 CurrentDirectory: '< Name not readable >' WindowTitle: '< Name not readable >' ImageFile: '< Name not readable >' CommandLine: '< Name not readable >' DllPath: '< Name not readable >' Environment: 00000000 Unable to read Environment string. R=mark@chromium.org BUG=crashpad:46 Review URL: https://codereview.chromium.org/1364053002 .
2015-09-25 10:31:02 -07:00
const ProcessInfo& ProcessReaderWin::GetProcessInfo() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return process_info_;
}
void ProcessReaderWin::DecrementThreadSuspendCounts(uint64_t except_thread_id) {
Threads();
for (auto& thread : threads_) {
if (thread.id != except_thread_id) {
DCHECK_GT(thread.suspend_count, 0u);
--thread.suspend_count;
}
}
}
template <class Traits>
void ProcessReaderWin::ReadThreadData(bool is_64_reading_32) {
DCHECK(threads_.empty());
std::unique_ptr<uint8_t[]> buffer;
process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process_information =
GetProcessInformation<Traits>(process_, &buffer);
if (!process_information)
return;
for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) {
const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info =
process_information->Threads[i];
ProcessReaderWin::Thread thread;
thread.id = thread_info.ClientId.UniqueThread;
ScopedKernelHANDLE thread_handle(OpenThread(thread_info));
if (!thread_handle.is_valid())
continue;
if (!FillThreadContextAndSuspendCount<Traits>(thread_handle.get(),
&thread,
suspension_state_,
is_64_reading_32)) {
continue;
}
// TODO(scottmg): I believe we could reverse engineer the PriorityClass from
// the Priority, BasePriority, and
// https://msdn.microsoft.com/library/ms685100.aspx. MinidumpThreadWriter
// doesn't handle it yet in any case, so investigate both of those at the
// same time if it's useful.
thread.priority_class = NORMAL_PRIORITY_CLASS;
thread.priority = thread_info.Priority;
process_types::THREAD_BASIC_INFORMATION<Traits> thread_basic_info;
NTSTATUS status = crashpad::NtQueryInformationThread(
thread_handle.get(),
static_cast<THREADINFOCLASS>(ThreadBasicInformation),
&thread_basic_info,
sizeof(thread_basic_info),
nullptr);
if (!NT_SUCCESS(status)) {
NTSTATUS_LOG(ERROR, status) << "NtQueryInformationThread";
continue;
}
// Read the TIB (Thread Information Block) which is the first element of the
// TEB, for its stack fields.
process_types::NT_TIB<Traits> tib;
thread.teb_address = thread_basic_info.TebBaseAddress;
thread.teb_size = sizeof(process_types::TEB<Traits>);
if (process_memory_.Read(thread.teb_address, sizeof(tib), &tib)) {
WinVMAddress base = 0;
WinVMAddress limit = 0;
// If we're reading a WOW64 process, then the TIB we just retrieved is the
// x64 one. The first word of the x64 TIB points at the x86 TIB. See
// https://msdn.microsoft.com/library/dn424783.aspx.
if (is_64_reading_32) {
process_types::NT_TIB<process_types::internal::Traits32> tib32;
thread.teb_address = tib.Wow64Teb;
thread.teb_size =
sizeof(process_types::TEB<process_types::internal::Traits32>);
if (process_memory_.Read(thread.teb_address, sizeof(tib32), &tib32)) {
base = tib32.StackBase;
limit = tib32.StackLimit;
}
} else {
base = tib.StackBase;
limit = tib.StackLimit;
}
// Note, "backwards" because of direction of stack growth.
thread.stack_region_address = limit;
if (limit > base) {
LOG(ERROR) << "invalid stack range: " << base << " - " << limit;
thread.stack_region_size = 0;
} else {
thread.stack_region_size = base - limit;
}
}
// On Windows 10 build 1607 and later, read the thread name.
static const auto get_thread_description =
GET_FUNCTION(L"kernel32.dll", ::GetThreadDescription);
if (get_thread_description) {
wchar_t* thread_description;
HRESULT hr =
get_thread_description(thread_handle.get(), &thread_description);
if (SUCCEEDED(hr)) {
ScopedLocalAlloc thread_description_owner(thread_description);
thread.name = base::WideToUTF8(thread_description);
} else {
LOG(WARNING) << "GetThreadDescription: "
<< logging::SystemErrorCodeToString(hr);
}
}
threads_.push_back(thread);
}
}
} // namespace crashpad