diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index 78a40d895..3af2fe538 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -133,7 +133,7 @@ CrashpadInfo::CrashpadInfo() crashpad_handler_behavior_(TriState::kUnset), system_crash_reporter_forwarding_(TriState::kUnset), gather_indirectly_referenced_memory_(TriState::kUnset), - padding_1_(0), + limit_stack_capture_to_sp_(TriState::kUnset), extra_memory_ranges_(nullptr), simple_annotations_(nullptr), user_data_minidump_stream_head_(nullptr), diff --git a/client/crashpad_info.h b/client/crashpad_info.h index b777a81f0..8b02f68b1 100644 --- a/client/crashpad_info.h +++ b/client/crashpad_info.h @@ -244,6 +244,25 @@ struct CrashpadInfo { indirectly_referenced_memory_cap_ = limit; } + //! \brief Limits stack capture to stack pointer. + //! + //! When handling an exception, the Crashpad handler will scan all modules in + //! a process. The first one that has a CrashpadInfo structure populated with + //! a value other than TriState::kUnset for this field will dictate whether + //! stack capture is limited. + //! + //! This causes Crashpad to use the current stack pointer as the upper bound + //! of the stack capture range, once validated to be within TEB + //! StackLimit/StackBase values. This reduces the capture range compared to + //! using the full TEB-derived stack region. This is useful when running under + //! Wine/Proton where TEB values may be incorrect. + //! + //! \param[in] limit_stack_capture_to_sp Whether to limit stack capture to + //! the stack pointer. + void set_limit_stack_capture_to_sp(TriState limit_stack_capture_to_sp) { + limit_stack_capture_to_sp_ = limit_stack_capture_to_sp; + } + //! \brief Adds a custom stream to the minidump. //! //! The memory block referenced by \a data and \a size will added to the @@ -329,7 +348,7 @@ struct CrashpadInfo { TriState crashpad_handler_behavior_; TriState system_crash_reporter_forwarding_; TriState gather_indirectly_referenced_memory_; - uint8_t padding_1_; + TriState limit_stack_capture_to_sp_; SimpleAddressRangeBag* extra_memory_ranges_; // weak SimpleStringDictionary* simple_annotations_; // weak internal::UserDataMinidumpStreamListEntry* user_data_minidump_stream_head_; diff --git a/snapshot/crashpad_info_client_options.cc b/snapshot/crashpad_info_client_options.cc index c44d5b632..9484095f4 100644 --- a/snapshot/crashpad_info_client_options.cc +++ b/snapshot/crashpad_info_client_options.cc @@ -39,7 +39,8 @@ CrashpadInfoClientOptions::CrashpadInfoClientOptions() : crashpad_handler_behavior(TriState::kUnset), system_crash_reporter_forwarding(TriState::kUnset), gather_indirectly_referenced_memory(TriState::kUnset), - indirectly_referenced_memory_cap(0) { + indirectly_referenced_memory_cap(0), + limit_stack_capture_to_sp(TriState::kUnset) { } } // namespace crashpad diff --git a/snapshot/crashpad_info_client_options.h b/snapshot/crashpad_info_client_options.h index ff434d08d..d88103218 100644 --- a/snapshot/crashpad_info_client_options.h +++ b/snapshot/crashpad_info_client_options.h @@ -65,6 +65,9 @@ struct CrashpadInfoClientOptions { //! \sa CrashpadInfo::set_gather_indirectly_referenced_memory() uint32_t indirectly_referenced_memory_cap; + + //! \sa CrashpadInfo::set_limit_stack_capture_to_sp() + TriState limit_stack_capture_to_sp; }; } // namespace crashpad diff --git a/snapshot/crashpad_types/crashpad_info_reader.cc b/snapshot/crashpad_types/crashpad_info_reader.cc index 9c8ca085a..359a3e33f 100644 --- a/snapshot/crashpad_types/crashpad_info_reader.cc +++ b/snapshot/crashpad_types/crashpad_info_reader.cc @@ -96,6 +96,7 @@ class CrashpadInfoReader::InfoContainerSpecific : public InfoContainer { UnsetIfNotValidTriState(&info.crashpad_handler_behavior); UnsetIfNotValidTriState(&info.system_crash_reporter_forwarding); UnsetIfNotValidTriState(&info.gather_indirectly_referenced_memory); + UnsetIfNotValidTriState(&info.limit_stack_capture_to_sp); return true; } @@ -109,7 +110,7 @@ class CrashpadInfoReader::InfoContainerSpecific : public InfoContainer { TriState crashpad_handler_behavior; TriState system_crash_reporter_forwarding; TriState gather_indirectly_referenced_memory; - uint8_t padding_1; + TriState limit_stack_capture_to_sp; typename Traits::Address extra_memory_ranges; typename Traits::Address simple_annotations; typename Traits::Address user_data_minidump_stream_head; @@ -181,6 +182,8 @@ DEFINE_GETTER(uint32_t, IndirectlyReferencedMemoryCap, indirectly_referenced_memory_cap) +DEFINE_GETTER(TriState, LimitStackCaptureToSp, limit_stack_capture_to_sp) + DEFINE_GETTER(VMAddress, ExtraMemoryRanges, extra_memory_ranges) DEFINE_GETTER(VMAddress, SimpleAnnotations, simple_annotations) diff --git a/snapshot/crashpad_types/crashpad_info_reader.h b/snapshot/crashpad_types/crashpad_info_reader.h index 22299c9f2..0e40f371d 100644 --- a/snapshot/crashpad_types/crashpad_info_reader.h +++ b/snapshot/crashpad_types/crashpad_info_reader.h @@ -54,6 +54,7 @@ class CrashpadInfoReader { TriState SystemCrashReporterForwarding(); TriState GatherIndirectlyReferencedMemory(); uint32_t IndirectlyReferencedMemoryCap(); + TriState LimitStackCaptureToSp(); VMAddress ExtraMemoryRanges(); VMAddress SimpleAnnotations(); VMAddress AnnotationsList(); diff --git a/snapshot/win/module_snapshot_win.cc b/snapshot/win/module_snapshot_win.cc index 1d901c0cf..5c851da3a 100644 --- a/snapshot/win/module_snapshot_win.cc +++ b/snapshot/win/module_snapshot_win.cc @@ -259,6 +259,7 @@ void ModuleSnapshotWin::GetCrashpadOptionsInternal( options->system_crash_reporter_forwarding = TriState::kUnset; options->gather_indirectly_referenced_memory = TriState::kUnset; options->indirectly_referenced_memory_cap = 0; + options->limit_stack_capture_to_sp = TriState::kUnset; return; } @@ -270,6 +271,8 @@ void ModuleSnapshotWin::GetCrashpadOptionsInternal( crashpad_info_->GatherIndirectlyReferencedMemory(); options->indirectly_referenced_memory_cap = crashpad_info_->IndirectlyReferencedMemoryCap(); + options->limit_stack_capture_to_sp = + crashpad_info_->LimitStackCaptureToSp(); } const VS_FIXEDFILEINFO* ModuleSnapshotWin::VSFixedFileInfo() const { diff --git a/snapshot/win/process_snapshot_win.cc b/snapshot/win/process_snapshot_win.cc index 6c06165f6..3b728a332 100644 --- a/snapshot/win/process_snapshot_win.cc +++ b/snapshot/win/process_snapshot_win.cc @@ -245,12 +245,18 @@ const ProcessMemory* ProcessSnapshotWin::Memory() const { void ProcessSnapshotWin::InitializeThreads(uint32_t* budget_remaining_pointer) { const std::vector& process_reader_threads = process_reader_.Threads(); + + // Check if stack capture limit is enabled via CrashpadInfo + bool limit_stack_capture_to_sp = + options_.limit_stack_capture_to_sp == TriState::kEnabled; + for (const ProcessReaderWin::Thread& process_reader_thread : process_reader_threads) { auto thread = std::make_unique(); if (thread->Initialize(&process_reader_, process_reader_thread, - budget_remaining_pointer)) { + budget_remaining_pointer, + limit_stack_capture_to_sp)) { threads_.push_back(std::move(thread)); } } @@ -359,12 +365,16 @@ void ProcessSnapshotWin::GetCrashpadOptionsInternal( local_options.indirectly_referenced_memory_cap = module_options.indirectly_referenced_memory_cap; } + if (local_options.limit_stack_capture_to_sp == TriState::kUnset) { + local_options.limit_stack_capture_to_sp = module_options.limit_stack_capture_to_sp; + } // 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 && - local_options.gather_indirectly_referenced_memory != TriState::kUnset) { + local_options.gather_indirectly_referenced_memory != TriState::kUnset && + local_options.limit_stack_capture_to_sp != TriState::kUnset) { break; } } diff --git a/snapshot/win/thread_snapshot_win.cc b/snapshot/win/thread_snapshot_win.cc index 242d82081..001a38e21 100644 --- a/snapshot/win/thread_snapshot_win.cc +++ b/snapshot/win/thread_snapshot_win.cc @@ -46,6 +46,51 @@ XSAVE_CET_U_FORMAT* LocateXStateCetU(CONTEXT* context) { locate_xstate_feature(context, XSTATE_CET_U, &cet_u_size)); } #endif // defined(ARCH_CPU_X86_64) + +// Helper function to adjust stack capture range based on current stack pointer +bool AdjustStackCaptureRange(ProcessReaderWin* process_reader, + const ProcessReaderWin::Thread& thread, + WinVMAddress* stack_capture_address, + WinVMSize* stack_capture_size) { + WinVMAddress sp = 0; + WinVMAddress stack_base = thread.stack_region_address + thread.stack_region_size; + + // Get the stack pointer from the context +#if defined(ARCH_CPU_X86) + sp = thread.context.context()->Esp; +#elif defined(ARCH_CPU_X86_64) + if (process_reader->Is64Bit()) { + sp = thread.context.context()->Rsp; + } else { + sp = thread.context.context()->Esp; + } +#elif defined(ARCH_CPU_ARM64) + sp = thread.context.context()->Sp; +#endif + + // Verify SP is within the valid stack region + if (sp >= thread.stack_region_address && sp < stack_base) { + // Account for potential red zone + // Windows x64 follows Microsoft x64 calling convention and has no red zone + // ARM64 has a 16-byte red zone + const WinVMSize red_zone_size = +#if defined(ARCH_CPU_ARM64) + 16; +#else + 0; +#endif + + // Adjust stack capture to start from SP (minus red zone) to stack base + WinVMAddress adjusted_start = sp > red_zone_size ? sp - red_zone_size : sp; + if (adjusted_start >= thread.stack_region_address && adjusted_start < stack_base) { + *stack_capture_address = adjusted_start; + *stack_capture_size = stack_base - adjusted_start; + return true; + } + } + return false; +} + } // namespace ThreadSnapshotWin::ThreadSnapshotWin() @@ -61,16 +106,26 @@ ThreadSnapshotWin::~ThreadSnapshotWin() {} bool ThreadSnapshotWin::Initialize( ProcessReaderWin* process_reader, const ProcessReaderWin::Thread& process_reader_thread, - uint32_t* gather_indirectly_referenced_memory_bytes_remaining) { + uint32_t* gather_indirectly_referenced_memory_bytes_remaining, + bool limit_stack_capture_to_sp) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); thread_ = process_reader_thread; + + WinVMAddress stack_capture_address = thread_.stack_region_address; + WinVMSize stack_capture_size = thread_.stack_region_size; + + // If limit_stack_capture_to_sp is enabled, calculate stack range based on current SP + if (limit_stack_capture_to_sp) { + AdjustStackCaptureRange( + process_reader, thread_, &stack_capture_address, &stack_capture_size); + } + 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); + CheckedRange(stack_capture_address, + stack_capture_size))) { + stack_.Initialize( + process_reader->Memory(), stack_capture_address, stack_capture_size); } else { stack_.Initialize(process_reader->Memory(), 0, 0); } diff --git a/snapshot/win/thread_snapshot_win.h b/snapshot/win/thread_snapshot_win.h index efc16e73e..935a0d1b2 100644 --- a/snapshot/win/thread_snapshot_win.h +++ b/snapshot/win/thread_snapshot_win.h @@ -55,13 +55,16 @@ class ThreadSnapshotWin final : public ThreadSnapshot { //! non-null, add extra memory regions to the snapshot pointed to by the //! thread's stack. The size of the regions added is subtracted from the //! count, and when it's `0`, no more regions will be added. + //! \param[in] limit_stack_capture_to_sp If `true`, limit the stack capture + //! to the current stack pointer instead of using full TEB-derived range. //! //! \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, - uint32_t* gather_indirectly_referenced_memory_bytes_remaining); + uint32_t* gather_indirectly_referenced_memory_bytes_remaining, + bool limit_stack_capture_to_sp = false); // ThreadSnapshot: