Better identification of context frames.

Since the introduction of inlined frames, it is not sufficient to check
the stack trace length (== 1) in order to identify context frames.
Updating all location that were depending on this assumption to check
for frame trust level instead.

Change-Id: I98f966889367c2270c268b8e78b67418c89c50f1
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/3499020
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Ivan Penkov 2022-03-01 21:01:34 +00:00
parent 42d2475617
commit c685fe1153
8 changed files with 42 additions and 33 deletions

View file

@ -223,7 +223,7 @@ StackFrameAMD64* StackwalkerAMD64::GetCallerByFramePointerRecovery(
StackFrameAMD64* StackwalkerAMD64::GetCallerBySimulatingReturn( StackFrameAMD64* StackwalkerAMD64::GetCallerBySimulatingReturn(
const vector<StackFrame*>& frames) { const vector<StackFrame*>& frames) {
assert(frames.size() == 1); assert(frames.back()->trust == StackFrame::FRAME_TRUST_CONTEXT);
StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back()); StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
uint64_t last_rsp = last_frame->context.rsp; uint64_t last_rsp = last_frame->context.rsp;
uint64_t caller_rip_address, caller_rip; uint64_t caller_rip_address, caller_rip;
@ -257,7 +257,8 @@ StackFrameAMD64* StackwalkerAMD64::GetCallerByStackScan(
uint64_t caller_rip_address, caller_rip; uint64_t caller_rip_address, caller_rip;
if (!ScanForReturnAddress(last_rsp, &caller_rip_address, &caller_rip, if (!ScanForReturnAddress(last_rsp, &caller_rip_address, &caller_rip,
frames.size() == 1 /* is_context_frame */)) { /*is_context_frame=*/last_frame->trust ==
StackFrame::FRAME_TRUST_CONTEXT)) {
// No plausible return address was found. // No plausible return address was found.
return NULL; return NULL;
} }
@ -322,7 +323,8 @@ StackFrame* StackwalkerAMD64::GetCallerFrame(const CallStack* stack,
// According to https://reviews.llvm.org/D24748, LLVM doesn't generate unwind // According to https://reviews.llvm.org/D24748, LLVM doesn't generate unwind
// info for such functions. According to MSDN, leaf functions can be unwound // info for such functions. According to MSDN, leaf functions can be unwound
// simply by simulating a return. // simply by simulating a return.
if (!new_frame.get() && stack->frames()->size() == 1 && if (!new_frame.get() &&
last_frame->trust == StackFrame::FRAME_TRUST_CONTEXT &&
system_info_->os_short == "windows") { system_info_->os_short == "windows") {
new_frame.reset(GetCallerBySimulatingReturn(frames)); new_frame.reset(GetCallerBySimulatingReturn(frames));
} }
@ -356,7 +358,9 @@ StackFrame* StackwalkerAMD64::GetCallerFrame(const CallStack* stack,
// Should we terminate the stack walk? (end-of-stack or broken invariant) // Should we terminate the stack walk? (end-of-stack or broken invariant)
if (TerminateWalk(new_frame->context.rip, new_frame->context.rsp, if (TerminateWalk(new_frame->context.rip, new_frame->context.rsp,
last_frame->context.rsp, frames.size() == 1)) { last_frame->context.rsp,
/*first_unwind=*/last_frame->trust ==
StackFrame::FRAME_TRUST_CONTEXT)) {
return NULL; return NULL;
} }

View file

@ -168,7 +168,8 @@ StackFrameARM* StackwalkerARM::GetCallerByStackScan(
uint32_t caller_sp, caller_pc; uint32_t caller_sp, caller_pc;
if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc, if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc,
frames.size() == 1 /* is_context_frame */)) { /*is_context_frame=*/last_frame->trust ==
StackFrame::FRAME_TRUST_CONTEXT)) {
// No plausible return address was found. // No plausible return address was found.
return NULL; return NULL;
} }
@ -276,7 +277,8 @@ StackFrame* StackwalkerARM::GetCallerFrame(const CallStack* stack,
if (TerminateWalk(frame->context.iregs[MD_CONTEXT_ARM_REG_PC], if (TerminateWalk(frame->context.iregs[MD_CONTEXT_ARM_REG_PC],
frame->context.iregs[MD_CONTEXT_ARM_REG_SP], frame->context.iregs[MD_CONTEXT_ARM_REG_SP],
last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP], last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP],
frames.size() == 1)) { /*first_unwind=*/last_frame->trust ==
StackFrame::FRAME_TRUST_CONTEXT)) {
return NULL; return NULL;
} }

View file

@ -181,7 +181,8 @@ StackFrameARM64* StackwalkerARM64::GetCallerByStackScan(
uint64_t caller_sp, caller_pc; uint64_t caller_sp, caller_pc;
if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc, if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc,
frames.size() == 1 /* is_context_frame */)) { /*is_context_frame=*/last_frame->trust ==
StackFrame::FRAME_TRUST_CONTEXT)) {
// No plausible return address was found. // No plausible return address was found.
return NULL; return NULL;
} }
@ -320,7 +321,8 @@ StackFrame* StackwalkerARM64::GetCallerFrame(const CallStack* stack,
if (TerminateWalk(frame->context.iregs[MD_CONTEXT_ARM64_REG_PC], if (TerminateWalk(frame->context.iregs[MD_CONTEXT_ARM64_REG_PC],
frame->context.iregs[MD_CONTEXT_ARM64_REG_SP], frame->context.iregs[MD_CONTEXT_ARM64_REG_SP],
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP], last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP],
frames.size() == 1)) { /*first_unwind=*/last_frame->trust ==
StackFrame::FRAME_TRUST_CONTEXT)) {
return NULL; return NULL;
} }

View file

@ -276,7 +276,8 @@ StackFrame* StackwalkerMIPS::GetCallerFrame(const CallStack* stack,
if (TerminateWalk(new_frame->context.epc, if (TerminateWalk(new_frame->context.epc,
new_frame->context.iregs[MD_CONTEXT_MIPS_REG_SP], new_frame->context.iregs[MD_CONTEXT_MIPS_REG_SP],
last_frame->context.iregs[MD_CONTEXT_MIPS_REG_SP], last_frame->context.iregs[MD_CONTEXT_MIPS_REG_SP],
frames.size() == 1)) { /*first_unwind=*/last_frame->trust ==
StackFrame::FRAME_TRUST_CONTEXT)) {
return NULL; return NULL;
} }

View file

@ -132,10 +132,9 @@ StackFrame* StackwalkerPPC::GetCallerFrame(const CallStack* stack,
frame->trust = StackFrame::FRAME_TRUST_FP; frame->trust = StackFrame::FRAME_TRUST_FP;
// Should we terminate the stack walk? (end-of-stack or broken invariant) // Should we terminate the stack walk? (end-of-stack or broken invariant)
if (TerminateWalk(instruction, if (TerminateWalk(instruction, stack_pointer, last_frame->context.gpr[1],
stack_pointer, /*first_unwind=*/last_frame->trust ==
last_frame->context.gpr[1], StackFrame::FRAME_TRUST_CONTEXT)) {
stack->frames()->size() == 1)) {
return NULL; return NULL;
} }

View file

@ -123,10 +123,9 @@ StackFrame* StackwalkerPPC64::GetCallerFrame(const CallStack* stack,
frame->trust = StackFrame::FRAME_TRUST_FP; frame->trust = StackFrame::FRAME_TRUST_FP;
// Should we terminate the stack walk? (end-of-stack or broken invariant) // Should we terminate the stack walk? (end-of-stack or broken invariant)
if (TerminateWalk(instruction, if (TerminateWalk(instruction, stack_pointer, last_frame->context.gpr[1],
stack_pointer, /*is_context_frame=*/last_frame->trust ==
last_frame->context.gpr[1], StackFrame::FRAME_TRUST_CONTEXT)) {
stack->frames()->size() == 1)) {
return NULL; return NULL;
} }

View file

@ -112,10 +112,9 @@ StackFrame* StackwalkerSPARC::GetCallerFrame(const CallStack* stack,
} }
// Should we terminate the stack walk? (end-of-stack or broken invariant) // Should we terminate the stack walk? (end-of-stack or broken invariant)
if (TerminateWalk(instruction, if (TerminateWalk(instruction, stack_pointer, last_frame->context.g_r[14],
stack_pointer, /*is_context_frame=*/last_frame->trust ==
last_frame->context.g_r[14], StackFrame::FRAME_TRUST_CONTEXT)) {
stack->frames()->size() == 1)) {
return NULL; return NULL;
} }

View file

@ -394,9 +394,10 @@ StackFrameX86* StackwalkerX86::GetCallerByWindowsFrameInfo(
// frame pointer. // frame pointer.
uint32_t location_start = last_frame->context.esp; uint32_t location_start = last_frame->context.esp;
uint32_t location, eip; uint32_t location, eip;
if (!stack_scan_allowed if (!stack_scan_allowed ||
|| !ScanForReturnAddress(location_start, &location, &eip, !ScanForReturnAddress(location_start, &location, &eip,
frames.size() == 1 /* is_context_frame */)) { /*is_context_frame=*/last_frame->trust ==
StackFrame::FRAME_TRUST_CONTEXT)) {
// if we can't find an instruction pointer even with stack scanning, // if we can't find an instruction pointer even with stack scanning,
// give up. // give up.
return NULL; return NULL;
@ -438,9 +439,10 @@ StackFrameX86* StackwalkerX86::GetCallerByWindowsFrameInfo(
// looking one 32-bit word above that location. // looking one 32-bit word above that location.
uint32_t location_start = dictionary[".raSearchStart"] + 4; uint32_t location_start = dictionary[".raSearchStart"] + 4;
uint32_t location; uint32_t location;
if (stack_scan_allowed if (stack_scan_allowed &&
&& ScanForReturnAddress(location_start, &location, &eip, ScanForReturnAddress(location_start, &location, &eip,
frames.size() == 1 /* is_context_frame */)) { /*is_context_frame=*/last_frame->trust ==
StackFrame::FRAME_TRUST_CONTEXT)) {
// This is a better return address that what program string // This is a better return address that what program string
// evaluation found. Use it, and set %esp to the location above the // evaluation found. Use it, and set %esp to the location above the
// one where the return address was found. // one where the return address was found.
@ -596,9 +598,10 @@ StackFrameX86* StackwalkerX86::GetCallerByEBPAtBase(
// return address. This can happen if last_frame is executing code // return address. This can happen if last_frame is executing code
// for a module for which we don't have symbols, and that module // for a module for which we don't have symbols, and that module
// is compiled without a frame pointer. // is compiled without a frame pointer.
if (!stack_scan_allowed if (!stack_scan_allowed ||
|| !ScanForReturnAddress(last_esp, &caller_esp, &caller_eip, !ScanForReturnAddress(last_esp, &caller_esp, &caller_eip,
frames.size() == 1 /* is_context_frame */)) { /*is_context_frame=*/last_frame->trust ==
StackFrame::FRAME_TRUST_CONTEXT)) {
// if we can't find an instruction pointer even with stack scanning, // if we can't find an instruction pointer even with stack scanning,
// give up. // give up.
return NULL; return NULL;
@ -678,10 +681,10 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack* stack,
return NULL; return NULL;
// Should we terminate the stack walk? (end-of-stack or broken invariant) // Should we terminate the stack walk? (end-of-stack or broken invariant)
if (TerminateWalk(new_frame->context.eip, if (TerminateWalk(new_frame->context.eip, new_frame->context.esp,
new_frame->context.esp,
last_frame->context.esp, last_frame->context.esp,
frames.size() == 1)) { /*first_unwind=*/last_frame->trust ==
StackFrame::FRAME_TRUST_CONTEXT)) {
return NULL; return NULL;
} }