// 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/thread_snapshot_win.h" #include #include #include "base/check_op.h" #include "base/memory/page_size.h" #include "base/numerics/safe_conversions.h" #include "snapshot/capture_memory.h" #include "snapshot/win/capture_memory_delegate_win.h" #include "snapshot/win/cpu_context_win.h" #include "snapshot/win/process_reader_win.h" namespace crashpad { namespace internal { namespace { #if defined(ARCH_CPU_X86_64) XSAVE_CET_U_FORMAT* LocateXStateCetU(CONTEXT* context) { // GetEnabledXStateFeatures needs Windows 7 SP1. static auto locate_xstate_feature = []() { HINSTANCE kernel32 = GetModuleHandle(L"Kernel32.dll"); return reinterpret_cast( GetProcAddress(kernel32, "LocateXStateFeature")); }(); if (!locate_xstate_feature) return nullptr; DWORD cet_u_size = 0; return reinterpret_cast( locate_xstate_feature(context, XSTATE_CET_U, &cet_u_size)); } #endif // defined(ARCH_CPU_X86_64) } // namespace ThreadSnapshotWin::ThreadSnapshotWin() : ThreadSnapshot(), context_(), stack_(), teb_(), thread_(), initialized_() {} ThreadSnapshotWin::~ThreadSnapshotWin() {} bool ThreadSnapshotWin::Initialize( ProcessReaderWin* process_reader, const ProcessReaderWin::Thread& process_reader_thread, uint32_t* gather_indirectly_referenced_memory_bytes_remaining) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); thread_ = process_reader_thread; if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable( CheckedRange(thread_.stack_region_address, thread_.stack_region_size))) { stack_.Initialize(process_reader->Memory(), thread_.stack_region_address, thread_.stack_region_size); } else { stack_.Initialize(process_reader->Memory(), 0, 0); } if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable( CheckedRange(thread_.teb_address, thread_.teb_size))) { teb_.Initialize( process_reader->Memory(), thread_.teb_address, thread_.teb_size); } else { teb_.Initialize(process_reader->Memory(), 0, 0); } #if defined(ARCH_CPU_X86) context_.architecture = kCPUArchitectureX86; context_.x86 = &context_union_.x86; InitializeX86Context(process_reader_thread.context.context(), context_.x86); #elif defined(ARCH_CPU_X86_64) if (process_reader->Is64Bit()) { context_.architecture = kCPUArchitectureX86_64; context_.x86_64 = &context_union_.x86_64; CONTEXT* context = process_reader_thread.context.context(); InitializeX64Context(context, context_.x86_64); // Capturing process must have CET enabled. If captured process does not, // then this will not set any state in the context snapshot. if (IsXStateFeatureEnabled(XSTATE_MASK_CET_U)) { XSAVE_CET_U_FORMAT* cet_u = LocateXStateCetU(context); if (cet_u && cet_u->Ia32CetUMsr && cet_u->Ia32Pl3SspMsr) { InitializeX64XStateCet(context, cet_u, context_.x86_64); } } } else { context_.architecture = kCPUArchitectureX86; context_.x86 = &context_union_.x86; InitializeX86Context(process_reader_thread.context.context(), context_.x86); } #elif defined(ARCH_CPU_ARM64) context_.architecture = kCPUArchitectureARM64; context_.arm64 = &context_union_.arm64; InitializeARM64Context(process_reader_thread.context.context(), context_.arm64); #else #error Unsupported Windows Arch #endif // ARCH_CPU_X86 #ifdef CLIENT_STACKTRACES_ENABLED frames_ = thread_.frames; #endif #if defined(ARCH_CPU_X86_64) // Unconditionally store page around ssp if it is present. if (process_reader->Is64Bit() && context_.x86_64->xstate.cet_u.ssp) { WinVMAddress page_size = base::checked_cast(base::GetPageSize()); WinVMAddress page_mask = ~(page_size - 1); WinVMAddress ssp_base = context_.x86_64->xstate.cet_u.ssp & page_mask; if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable( CheckedRange(ssp_base, page_size))) { auto region = std::make_unique(); region->Initialize(process_reader->Memory(), ssp_base, page_size); pointed_to_memory_.push_back(std::move(region)); } } #endif // ARCH_CPU_X86_64 CaptureMemoryDelegateWin capture_memory_delegate( process_reader, thread_, &pointed_to_memory_, gather_indirectly_referenced_memory_bytes_remaining); CaptureMemory::PointedToByContext(context_, &capture_memory_delegate); if (gather_indirectly_referenced_memory_bytes_remaining) { CaptureMemory::PointedToByMemoryRange(stack_, &capture_memory_delegate); } INITIALIZATION_STATE_SET_VALID(initialized_); return true; } const CPUContext* ThreadSnapshotWin::Context() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return &context_; } const MemorySnapshot* ThreadSnapshotWin::Stack() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return &stack_; } uint64_t ThreadSnapshotWin::ThreadID() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return thread_.id; } std::string ThreadSnapshotWin::ThreadName() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return thread_.name; } int ThreadSnapshotWin::SuspendCount() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return thread_.suspend_count; } int ThreadSnapshotWin::Priority() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return thread_.priority; } uint64_t ThreadSnapshotWin::ThreadSpecificDataAddress() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return thread_.teb_address; } std::vector ThreadSnapshotWin::ExtraMemory() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::vector result; result.reserve(1 + pointed_to_memory_.size()); result.push_back(&teb_); for (const auto& pointed_to_memory : pointed_to_memory_) { result.push_back(pointed_to_memory.get()); } return result; } } // namespace internal } // namespace crashpad