Fix a corner case in the aarch64 unwinder

In case of two nested back-to-back signals (such as what happens in NestedSignal test) we could end up erroneously using the frame pointer from ucontext_t twice, leading to premature backtrace termination.

In the situation where this happens, the call stack looks like
#0 <unwinder frames>
#1 SigUsr2Handler
#2 __kernel_rt_sigreturn
#3 raise
#4 SigUsr1Handler
#5 __kernel_rt_sigreturn
#6 raise
#7 RaiseSignal
...

When unwinding from #2, we get the fp value from the ucontext (as we should). However, because raise does not modify the fp and because SigUsr1Handler is also a signal handler, when we try to unwind from #4 (#3 is skipped), NextStackFrame ends up looking at the ucontext fp again, and comparing it with the previous (identical) FP value. Non-strict equality accepts this as a valid frame, but the unwinder later bails out due to a zero-sized frame.

Using a strict equality causes NextStackFrame to reject the ucontext fp and use the FP from FP chain instead. This causes us to skip a few more frames, but at least we continue to unwind instead of giving up.

In this case, the computed backtrace skips functions #3, #4 and #6.

PiperOrigin-RevId: 804308754
Change-Id: I5d43e6bea80e4abff1075ada03782ae11c599161
This commit is contained in:
Abseil Team
2025-09-08 01:01:03 -07:00
committed by Copybara-Service
parent 7fc86c6786
commit 26c9126028

View File

@@ -123,7 +123,7 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc,
// earlier in the stack than the old_frame_pointer, then use it. If it is
// later, then we have already unwound through it and it needs no special
// handling.
if (pre_signal_frame_pointer >= old_frame_pointer) {
if (pre_signal_frame_pointer > old_frame_pointer) {
new_frame_pointer = pre_signal_frame_pointer;
}
}