summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2023-06-09 10:10:46 -0700
committerGravatar Copybara-Service <copybara-worker@google.com>2023-06-09 10:11:36 -0700
commit2b042424db44e9614d3c100832dff1a54f37b351 (patch)
tree0af078d4c53ae85bc618562c310871327482737c
parent0f4133aacfa2ebc84c76f69e6ab02ce58603072f (diff)
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
-rw-r--r--absl/debugging/internal/stacktrace_aarch64-inl.inc13
1 files 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<void **>(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;