Skip to content

Commit d8990d2

Browse files
authored
feat: Support optional usage of stack pointer for captured stack frame (#137)
* Support optional usage of stack pointer for captured stack frame
1 parent b160f39 commit d8990d2

10 files changed

+111
-13
lines changed

client/crashpad_info.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ CrashpadInfo::CrashpadInfo()
133133
crashpad_handler_behavior_(TriState::kUnset),
134134
system_crash_reporter_forwarding_(TriState::kUnset),
135135
gather_indirectly_referenced_memory_(TriState::kUnset),
136-
padding_1_(0),
136+
limit_stack_capture_to_sp_(TriState::kUnset),
137137
extra_memory_ranges_(nullptr),
138138
simple_annotations_(nullptr),
139139
user_data_minidump_stream_head_(nullptr),

client/crashpad_info.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,25 @@ struct CrashpadInfo {
244244
indirectly_referenced_memory_cap_ = limit;
245245
}
246246

247+
//! \brief Limits stack capture to stack pointer.
248+
//!
249+
//! When handling an exception, the Crashpad handler will scan all modules in
250+
//! a process. The first one that has a CrashpadInfo structure populated with
251+
//! a value other than TriState::kUnset for this field will dictate whether
252+
//! stack capture is limited.
253+
//!
254+
//! This causes Crashpad to use the current stack pointer as the upper bound
255+
//! of the stack capture range, once validated to be within TEB
256+
//! StackLimit/StackBase values. This reduces the capture range compared to
257+
//! using the full TEB-derived stack region. This is useful when running under
258+
//! Wine/Proton where TEB values may be incorrect.
259+
//!
260+
//! \param[in] limit_stack_capture_to_sp Whether to limit stack capture to
261+
//! the stack pointer.
262+
void set_limit_stack_capture_to_sp(TriState limit_stack_capture_to_sp) {
263+
limit_stack_capture_to_sp_ = limit_stack_capture_to_sp;
264+
}
265+
247266
//! \brief Adds a custom stream to the minidump.
248267
//!
249268
//! The memory block referenced by \a data and \a size will added to the
@@ -329,7 +348,7 @@ struct CrashpadInfo {
329348
TriState crashpad_handler_behavior_;
330349
TriState system_crash_reporter_forwarding_;
331350
TriState gather_indirectly_referenced_memory_;
332-
uint8_t padding_1_;
351+
TriState limit_stack_capture_to_sp_;
333352
SimpleAddressRangeBag* extra_memory_ranges_; // weak
334353
SimpleStringDictionary* simple_annotations_; // weak
335354
internal::UserDataMinidumpStreamListEntry* user_data_minidump_stream_head_;

snapshot/crashpad_info_client_options.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ CrashpadInfoClientOptions::CrashpadInfoClientOptions()
3939
: crashpad_handler_behavior(TriState::kUnset),
4040
system_crash_reporter_forwarding(TriState::kUnset),
4141
gather_indirectly_referenced_memory(TriState::kUnset),
42-
indirectly_referenced_memory_cap(0) {
42+
indirectly_referenced_memory_cap(0),
43+
limit_stack_capture_to_sp(TriState::kUnset) {
4344
}
4445

4546
} // namespace crashpad

snapshot/crashpad_info_client_options.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ struct CrashpadInfoClientOptions {
6565

6666
//! \sa CrashpadInfo::set_gather_indirectly_referenced_memory()
6767
uint32_t indirectly_referenced_memory_cap;
68+
69+
//! \sa CrashpadInfo::set_limit_stack_capture_to_sp()
70+
TriState limit_stack_capture_to_sp;
6871
};
6972

7073
} // namespace crashpad

snapshot/crashpad_types/crashpad_info_reader.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class CrashpadInfoReader::InfoContainerSpecific : public InfoContainer {
9696
UnsetIfNotValidTriState(&info.crashpad_handler_behavior);
9797
UnsetIfNotValidTriState(&info.system_crash_reporter_forwarding);
9898
UnsetIfNotValidTriState(&info.gather_indirectly_referenced_memory);
99+
UnsetIfNotValidTriState(&info.limit_stack_capture_to_sp);
99100

100101
return true;
101102
}
@@ -109,7 +110,7 @@ class CrashpadInfoReader::InfoContainerSpecific : public InfoContainer {
109110
TriState crashpad_handler_behavior;
110111
TriState system_crash_reporter_forwarding;
111112
TriState gather_indirectly_referenced_memory;
112-
uint8_t padding_1;
113+
TriState limit_stack_capture_to_sp;
113114
typename Traits::Address extra_memory_ranges;
114115
typename Traits::Address simple_annotations;
115116
typename Traits::Address user_data_minidump_stream_head;
@@ -181,6 +182,8 @@ DEFINE_GETTER(uint32_t,
181182
IndirectlyReferencedMemoryCap,
182183
indirectly_referenced_memory_cap)
183184

185+
DEFINE_GETTER(TriState, LimitStackCaptureToSp, limit_stack_capture_to_sp)
186+
184187
DEFINE_GETTER(VMAddress, ExtraMemoryRanges, extra_memory_ranges)
185188

186189
DEFINE_GETTER(VMAddress, SimpleAnnotations, simple_annotations)

snapshot/crashpad_types/crashpad_info_reader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class CrashpadInfoReader {
5454
TriState SystemCrashReporterForwarding();
5555
TriState GatherIndirectlyReferencedMemory();
5656
uint32_t IndirectlyReferencedMemoryCap();
57+
TriState LimitStackCaptureToSp();
5758
VMAddress ExtraMemoryRanges();
5859
VMAddress SimpleAnnotations();
5960
VMAddress AnnotationsList();

snapshot/win/module_snapshot_win.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ void ModuleSnapshotWin::GetCrashpadOptionsInternal(
259259
options->system_crash_reporter_forwarding = TriState::kUnset;
260260
options->gather_indirectly_referenced_memory = TriState::kUnset;
261261
options->indirectly_referenced_memory_cap = 0;
262+
options->limit_stack_capture_to_sp = TriState::kUnset;
262263
return;
263264
}
264265

@@ -270,6 +271,8 @@ void ModuleSnapshotWin::GetCrashpadOptionsInternal(
270271
crashpad_info_->GatherIndirectlyReferencedMemory();
271272
options->indirectly_referenced_memory_cap =
272273
crashpad_info_->IndirectlyReferencedMemoryCap();
274+
options->limit_stack_capture_to_sp =
275+
crashpad_info_->LimitStackCaptureToSp();
273276
}
274277

275278
const VS_FIXEDFILEINFO* ModuleSnapshotWin::VSFixedFileInfo() const {

snapshot/win/process_snapshot_win.cc

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,18 @@ const ProcessMemory* ProcessSnapshotWin::Memory() const {
245245
void ProcessSnapshotWin::InitializeThreads(uint32_t* budget_remaining_pointer) {
246246
const std::vector<ProcessReaderWin::Thread>& process_reader_threads =
247247
process_reader_.Threads();
248+
249+
// Check if stack capture limit is enabled via CrashpadInfo
250+
bool limit_stack_capture_to_sp =
251+
options_.limit_stack_capture_to_sp == TriState::kEnabled;
252+
248253
for (const ProcessReaderWin::Thread& process_reader_thread :
249254
process_reader_threads) {
250255
auto thread = std::make_unique<internal::ThreadSnapshotWin>();
251256
if (thread->Initialize(&process_reader_,
252257
process_reader_thread,
253-
budget_remaining_pointer)) {
258+
budget_remaining_pointer,
259+
limit_stack_capture_to_sp)) {
254260
threads_.push_back(std::move(thread));
255261
}
256262
}
@@ -359,12 +365,16 @@ void ProcessSnapshotWin::GetCrashpadOptionsInternal(
359365
local_options.indirectly_referenced_memory_cap =
360366
module_options.indirectly_referenced_memory_cap;
361367
}
368+
if (local_options.limit_stack_capture_to_sp == TriState::kUnset) {
369+
local_options.limit_stack_capture_to_sp = module_options.limit_stack_capture_to_sp;
370+
}
362371

363372
// If non-default values have been found for all options, the loop can end
364373
// early.
365374
if (local_options.crashpad_handler_behavior != TriState::kUnset &&
366375
local_options.system_crash_reporter_forwarding != TriState::kUnset &&
367-
local_options.gather_indirectly_referenced_memory != TriState::kUnset) {
376+
local_options.gather_indirectly_referenced_memory != TriState::kUnset &&
377+
local_options.limit_stack_capture_to_sp != TriState::kUnset) {
368378
break;
369379
}
370380
}

snapshot/win/thread_snapshot_win.cc

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,51 @@ XSAVE_CET_U_FORMAT* LocateXStateCetU(CONTEXT* context) {
4646
locate_xstate_feature(context, XSTATE_CET_U, &cet_u_size));
4747
}
4848
#endif // defined(ARCH_CPU_X86_64)
49+
50+
// Helper function to adjust stack capture range based on current stack pointer
51+
bool AdjustStackCaptureRange(ProcessReaderWin* process_reader,
52+
const ProcessReaderWin::Thread& thread,
53+
WinVMAddress* stack_capture_address,
54+
WinVMSize* stack_capture_size) {
55+
WinVMAddress sp = 0;
56+
WinVMAddress stack_base = thread.stack_region_address + thread.stack_region_size;
57+
58+
// Get the stack pointer from the context
59+
#if defined(ARCH_CPU_X86)
60+
sp = thread.context.context<CONTEXT>()->Esp;
61+
#elif defined(ARCH_CPU_X86_64)
62+
if (process_reader->Is64Bit()) {
63+
sp = thread.context.context<CONTEXT>()->Rsp;
64+
} else {
65+
sp = thread.context.context<WOW64_CONTEXT>()->Esp;
66+
}
67+
#elif defined(ARCH_CPU_ARM64)
68+
sp = thread.context.context<CONTEXT>()->Sp;
69+
#endif
70+
71+
// Verify SP is within the valid stack region
72+
if (sp >= thread.stack_region_address && sp < stack_base) {
73+
// Account for potential red zone
74+
// Windows x64 follows Microsoft x64 calling convention and has no red zone
75+
// ARM64 has a 16-byte red zone
76+
const WinVMSize red_zone_size =
77+
#if defined(ARCH_CPU_ARM64)
78+
16;
79+
#else
80+
0;
81+
#endif
82+
83+
// Adjust stack capture to start from SP (minus red zone) to stack base
84+
WinVMAddress adjusted_start = sp > red_zone_size ? sp - red_zone_size : sp;
85+
if (adjusted_start >= thread.stack_region_address && adjusted_start < stack_base) {
86+
*stack_capture_address = adjusted_start;
87+
*stack_capture_size = stack_base - adjusted_start;
88+
return true;
89+
}
90+
}
91+
return false;
92+
}
93+
4994
} // namespace
5095

5196
ThreadSnapshotWin::ThreadSnapshotWin()
@@ -61,16 +106,26 @@ ThreadSnapshotWin::~ThreadSnapshotWin() {}
61106
bool ThreadSnapshotWin::Initialize(
62107
ProcessReaderWin* process_reader,
63108
const ProcessReaderWin::Thread& process_reader_thread,
64-
uint32_t* gather_indirectly_referenced_memory_bytes_remaining) {
109+
uint32_t* gather_indirectly_referenced_memory_bytes_remaining,
110+
bool limit_stack_capture_to_sp) {
65111
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
66112

67113
thread_ = process_reader_thread;
114+
115+
WinVMAddress stack_capture_address = thread_.stack_region_address;
116+
WinVMSize stack_capture_size = thread_.stack_region_size;
117+
118+
// If limit_stack_capture_to_sp is enabled, calculate stack range based on current SP
119+
if (limit_stack_capture_to_sp) {
120+
AdjustStackCaptureRange(
121+
process_reader, thread_, &stack_capture_address, &stack_capture_size);
122+
}
123+
68124
if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable(
69-
CheckedRange<WinVMAddress, WinVMSize>(thread_.stack_region_address,
70-
thread_.stack_region_size))) {
71-
stack_.Initialize(process_reader->Memory(),
72-
thread_.stack_region_address,
73-
thread_.stack_region_size);
125+
CheckedRange<WinVMAddress, WinVMSize>(stack_capture_address,
126+
stack_capture_size))) {
127+
stack_.Initialize(
128+
process_reader->Memory(), stack_capture_address, stack_capture_size);
74129
} else {
75130
stack_.Initialize(process_reader->Memory(), 0, 0);
76131
}

snapshot/win/thread_snapshot_win.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,16 @@ class ThreadSnapshotWin final : public ThreadSnapshot {
5555
//! non-null, add extra memory regions to the snapshot pointed to by the
5656
//! thread's stack. The size of the regions added is subtracted from the
5757
//! count, and when it's `0`, no more regions will be added.
58+
//! \param[in] limit_stack_capture_to_sp If `true`, limit the stack capture
59+
//! to the current stack pointer instead of using full TEB-derived range.
5860
//!
5961
//! \return `true` if the snapshot could be created, `false` otherwise with
6062
//! an appropriate message logged.
6163
bool Initialize(
6264
ProcessReaderWin* process_reader,
6365
const ProcessReaderWin::Thread& process_reader_thread,
64-
uint32_t* gather_indirectly_referenced_memory_bytes_remaining);
66+
uint32_t* gather_indirectly_referenced_memory_bytes_remaining,
67+
bool limit_stack_capture_to_sp = false);
6568

6669
// ThreadSnapshot:
6770

0 commit comments

Comments
 (0)