diff --git a/snapshot/capture_memory.cc b/snapshot/capture_memory.cc index 06d92581..3ff099fe 100644 --- a/snapshot/capture_memory.cc +++ b/snapshot/capture_memory.cc @@ -15,6 +15,10 @@ #include "snapshot/capture_memory.h" #include +#include + +// dbghelp must be after windows.h. +#include #include #include @@ -86,6 +90,10 @@ void CaptureMemory::PointedToByContext(const CPUContext& context, MaybeCaptureMemoryAround(delegate, context.x86_64->r14); MaybeCaptureMemoryAround(delegate, context.x86_64->r15); MaybeCaptureMemoryAround(delegate, context.x86_64->rip); + // Shadow stack region. + if (context.x86_64->xstate.enabled_features & XSTATE_MASK_CET_U) { + MaybeCaptureMemoryAround(delegate, context.x86_64->xstate.cet_u.ssp); + } } else { MaybeCaptureMemoryAround(delegate, context.x86->eax); MaybeCaptureMemoryAround(delegate, context.x86->ebx); diff --git a/snapshot/win/cpu_context_win.cc b/snapshot/win/cpu_context_win.cc index 0d778382..e79c94b3 100644 --- a/snapshot/win/cpu_context_win.cc +++ b/snapshot/win/cpu_context_win.cc @@ -124,6 +124,19 @@ void CommonInitializeX86Context(const T* context, CPUContextX86* out) { } } +#if defined(ARCH_CPU_X86_64) +DWORD64 CallGetEnabledXStateFeatures() { + // GetEnabledXStateFeatures needs Windows 7 SP1. + HINSTANCE kernel32 = GetModuleHandle(L"Kernel32.dll"); + decltype(GetEnabledXStateFeatures)* get_enabled_xstate_features = + reinterpret_cast( + GetProcAddress(kernel32, "GetEnabledXStateFeatures")); + if (!get_enabled_xstate_features) + return 0; + return get_enabled_xstate_features(); +} +#endif // defined(ARCH_CPU_X64) + } // namespace #if defined(ARCH_CPU_X86) @@ -198,6 +211,23 @@ void InitializeX64Context(const CONTEXT* context, CPUContextX86_64* out) { } } +void InitializeX64XStateCet(const CONTEXT* context, + XSAVE_CET_U_FORMAT* cet_u, + CPUContextX86_64* out) { + if (HasContextPart(context, CONTEXT_XSTATE)) { + if (cet_u) { + out->xstate.enabled_features |= XSTATE_MASK_CET_U; + out->xstate.cet_u.cetmsr = cet_u->Ia32CetUMsr; + out->xstate.cet_u.ssp = cet_u->Ia32Pl3SspMsr; + } + } +} + +bool IsXStateFeatureEnabled(DWORD64 features) { + static DWORD64 flags = CallGetEnabledXStateFeatures(); + return (flags & features) == features; +} + #elif defined(ARCH_CPU_ARM64) void InitializeARM64Context(const CONTEXT* context, CPUContextARM64* out) { diff --git a/snapshot/win/cpu_context_win.h b/snapshot/win/cpu_context_win.h index 69700cfe..720474d3 100644 --- a/snapshot/win/cpu_context_win.h +++ b/snapshot/win/cpu_context_win.h @@ -44,6 +44,16 @@ void InitializeX86Context(const WOW64_CONTEXT* context, CPUContextX86* out); //! Only reads a max of sizeof(CONTEXT) so will not initialize extended values. void InitializeX64Context(const CONTEXT* context, CPUContextX86_64* out); +//! \brief Initializes CET fields of a CPUContextX86_64 structure from +//! an xsave location if |context| flags support cet_u values. +void InitializeX64XStateCet(const CONTEXT* context, + XSAVE_CET_U_FORMAT* cet_u, + CPUContextX86_64* out); + +//! \brief Wraps GetXStateEnabledFeatures(), returns true if the specified set +//! of flags are all supported. +bool IsXStateFeatureEnabled(DWORD64 feature); + #endif // ARCH_CPU_X86_64 #if defined(ARCH_CPU_ARM64) || DOXYGEN diff --git a/snapshot/win/exception_snapshot_win.cc b/snapshot/win/exception_snapshot_win.cc index 4c9dcfb3..d1df987c 100644 --- a/snapshot/win/exception_snapshot_win.cc +++ b/snapshot/win/exception_snapshot_win.cc @@ -52,7 +52,19 @@ void NativeContextToCPUContext64(const CONTEXT* context_record, #if defined(ARCH_CPU_X86_64) context->architecture = kCPUArchitectureX86_64; context->x86_64 = &context_union->x86_64; + // Note that the context here is not extended, even if the flags suggest so, + // as we only copied in sizeof(CONTEXT). InitializeX64Context(context_record, context->x86_64); + // TODO(1250098) plumb through ssp via message from crashed process. For now + // we zero this if CET is available in the capturing process as otherwise + // WinDBG will show the relevant thread's ssp for the exception which will + // likely be more confusing than showing a zero value. + if (IsXStateFeatureEnabled(XSTATE_MASK_CET_U)) { + XSAVE_CET_U_FORMAT cet_u_fake; + cet_u_fake.Ia32CetUMsr = 0; + cet_u_fake.Ia32Pl3SspMsr = 0; + InitializeX64XStateCet(context_record, &cet_u_fake, context->x86_64); + } #elif defined(ARCH_CPU_ARM64) context->architecture = kCPUArchitectureARM64; context->arm64 = &context_union->arm64; diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc index 20149fae..a437a148 100644 --- a/snapshot/win/process_reader_win.cc +++ b/snapshot/win/process_reader_win.cc @@ -19,7 +19,9 @@ #include +#include "base/notreached.h" #include "base/numerics/safe_conversions.h" +#include "snapshot/win/cpu_context_win.h" #include "util/misc/capture_context.h" #include "util/misc/time.h" #include "util/win/nt_internals.h" @@ -163,18 +165,20 @@ bool FillThreadContextAndSuspendCount(HANDLE thread_handle, } #if defined(ARCH_CPU_32_BITS) - const bool is_native = true; + if (!thread->context.InitializeNative(thread_handle)) + return false; #elif defined(ARCH_CPU_64_BITS) - const bool is_native = !is_64_reading_32; if (is_64_reading_32) { if (!thread->context.InitializeWow64(thread_handle)) return false; - } -#endif - if (is_native) { + } else if (IsXStateFeatureEnabled(XSTATE_MASK_CET_U)) { + if (!thread->context.InitializeXState(thread_handle, XSTATE_MASK_CET_U)) + return false; + } else { if (!thread->context.InitializeNative(thread_handle)) return false; } +#endif if (!ResumeThread(thread_handle)) { PLOG(ERROR) << "ResumeThread"; @@ -220,6 +224,52 @@ bool ProcessReaderWin::ThreadContext::InitializeWow64(HANDLE thread_handle) { } #endif +#if defined(ARCH_CPU_64_BITS) +bool ProcessReaderWin::ThreadContext::InitializeXState( + HANDLE thread_handle, + ULONG64 XStateCompactionMask) { + static auto initialize_context_2 = []() { + // InitializeContext2 needs Windows 10 build 20348. + HINSTANCE kernel32 = GetModuleHandle(L"Kernel32.dll"); + return reinterpret_cast( + GetProcAddress(kernel32, "InitializeContext2")); + }(); + 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(ret_context) - data_.data(); + initialized_ = true; + + if (!GetThreadContext(thread_handle, ret_context)) { + PLOG(ERROR) << "GetThreadContext"; + return false; + } + + return true; +} +#endif // defined(ARCH_CPU_64_BITS) + ProcessReaderWin::Thread::Thread() : context(), id(0), diff --git a/snapshot/win/process_reader_win.h b/snapshot/win/process_reader_win.h index dc41a207..72b0c8d6 100644 --- a/snapshot/win/process_reader_win.h +++ b/snapshot/win/process_reader_win.h @@ -54,6 +54,8 @@ class ProcessReaderWin { } #if defined(ARCH_CPU_64_BITS) bool InitializeWow64(HANDLE thread_handle); + // Initializes internal structures for extended compacted contexts. + bool InitializeXState(HANDLE thread_handle, ULONG64 XStateCompactionMask); #endif void InitializeFromCurrentThread(); bool InitializeNative(HANDLE thread_handle); diff --git a/snapshot/win/thread_snapshot_win.cc b/snapshot/win/thread_snapshot_win.cc index 3a47cf6e..9286b392 100644 --- a/snapshot/win/thread_snapshot_win.cc +++ b/snapshot/win/thread_snapshot_win.cc @@ -26,6 +26,25 @@ 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_(), @@ -73,8 +92,16 @@ bool ThreadSnapshotWin::Initialize( if (process_reader->Is64Bit()) { context_.architecture = kCPUArchitectureX86_64; context_.x86_64 = &context_union_.x86_64; - InitializeX64Context(process_reader_thread.context.context(), - context_.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) { + InitializeX64XStateCet(context, cet_u, context_.x86_64); + } + } } else { context_.architecture = kCPUArchitectureX86; context_.x86 = &context_union_.x86;