From 2b042424db44e9614d3c100832dff1a54f37b351 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 9 Jun 2023 10:10:46 -0700 Subject: Fix unwinding through nested signal frames on aarch64. This fixes an endless loop in the absl Arm stack unwinder where encountering a second signal return trampoline (as one has in nested signal frames), would restart unwinding at the outermost signal, resulting in an endless loop. This does not change any behavior in the non-nested signal case, so I believe it is safe for any stack that hasn't encountered this bug already. I would love to test this beyond the absl unwinding test cases and the fingerprint_test included here, but I'm at a loss for other test cases. PiperOrigin-RevId: 539113007 Change-Id: I10037f9fa77b45cc4db61f89b9c6380ec3529113 --- absl/debugging/internal/stacktrace_aarch64-inl.inc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc index b66beba2..520e9a84 100644 --- a/absl/debugging/internal/stacktrace_aarch64-inl.inc +++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc @@ -94,16 +94,21 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc) { void **const pre_signal_frame_pointer = reinterpret_cast(ucv->uc_mcontext.regs[29]); + // The most recent signal always needs special handling to find the frame + // pointer, but a nested signal does not. If pre_signal_frame_pointer is + // 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) { + new_frame_pointer = pre_signal_frame_pointer; + } // Check that alleged frame pointer is actually readable. This is to // prevent "double fault" in case we hit the first fault due to e.g. // stack corruption. if (!absl::debugging_internal::AddressIsReadable( - pre_signal_frame_pointer)) + new_frame_pointer)) return nullptr; - // Alleged frame pointer is readable, use it for further unwinding. - new_frame_pointer = pre_signal_frame_pointer; - // Skip frame size check if we return from a signal. We may be using a // an alternate stack for signals. check_frame_size = false; -- cgit v1.2.3