summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2023-11-07 10:18:36 -0800
committerGravatar Copybara-Service <copybara-worker@google.com>2023-11-07 10:19:31 -0800
commitbb7bbb12c770cfcbc3fcde9afca6964e962f500e (patch)
treea73ad5339fc4d90914d4b5c87e6e3cba625f780a
parent116ee0fed75c1d032604f3b675e7e44e10d124b4 (diff)
Properly handle signal stacks and frame-size calculations
We can determine the signal stack, so use that information to make better decisions about when to calculate the frame size and when not to. This fixes a several tests where the memory layout had the signal stack and main stack in position that confused some of the greater-than/less-than comparisons. Also cleanup certain types to avoid more casting than necessary. PiperOrigin-RevId: 580221819 Change-Id: I0365b03e7893741603dc66e6d36a069d0b7f5404
-rw-r--r--absl/debugging/internal/stacktrace_aarch64-inl.inc55
1 files changed, 38 insertions, 17 deletions
diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc
index c46a91db..fba0ba49 100644
--- a/absl/debugging/internal/stacktrace_aarch64-inl.inc
+++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc
@@ -4,6 +4,7 @@
// Generate stack tracer for aarch64
#if defined(__linux__)
+#include <signal.h>
#include <sys/mman.h>
#include <ucontext.h>
#include <unistd.h>
@@ -70,7 +71,7 @@ static const unsigned char* GetKernelRtSigreturnAddress() {
// Compute the size of a stack frame in [low..high). We assume that
// low < high. Return size of kUnknownFrameSize.
template<typename T>
-static inline size_t ComputeStackFrameSize(const T* low,
+static size_t ComputeStackFrameSize(const T* low,
const T* high) {
const char* low_char_ptr = reinterpret_cast<const char *>(low);
const char* high_char_ptr = reinterpret_cast<const char *>(high);
@@ -78,6 +79,20 @@ static inline size_t ComputeStackFrameSize(const T* low,
: kUnknownFrameSize;
}
+// Saves stack info that is expensive to calculate to avoid recalculating per frame.
+struct StackInfo {
+ uintptr_t stack_low;
+ uintptr_t stack_high;
+ uintptr_t sig_stack_low;
+ uintptr_t sig_stack_high;
+};
+
+static bool InsideSignalStack(void** ptr, const StackInfo* stack_info) {
+ uintptr_t comparable_ptr = reinterpret_cast<uintptr_t>(ptr);
+ return (comparable_ptr >= stack_info->sig_stack_low &&
+ comparable_ptr < stack_info->sig_stack_high);
+}
+
// Given a pointer to a stack frame, locate and return the calling
// stackframe, or return null if no stackframe can be found. Perform sanity
// checks (the strictness of which is controlled by the boolean parameter
@@ -86,9 +101,8 @@ template<bool STRICT_UNWINDING, bool WITH_CONTEXT>
ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
static void **NextStackFrame(void **old_frame_pointer, const void *uc,
- size_t stack_low, size_t stack_high) {
+ const StackInfo *stack_info) {
void **new_frame_pointer = reinterpret_cast<void**>(*old_frame_pointer);
- bool check_frame_size = true;
#if defined(__linux__)
if (WITH_CONTEXT && uc != nullptr) {
@@ -114,10 +128,6 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc,
if (!absl::debugging_internal::AddressIsReadable(
new_frame_pointer))
return nullptr;
-
- // Skip frame size check if we return from a signal. We may be using a
- // an alternate stack for signals.
- check_frame_size = false;
}
}
#endif
@@ -126,9 +136,11 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc,
if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 7) != 0)
return nullptr;
- // Check frame size. In strict mode, we assume frames to be under
- // 100,000 bytes. In non-strict mode, we relax the limit to 1MB.
- if (check_frame_size) {
+ // Only check the size if both frames are in the same stack.
+ if (InsideSignalStack(new_frame_pointer, stack_info) ==
+ InsideSignalStack(old_frame_pointer, stack_info)) {
+ // Check frame size. In strict mode, we assume frames to be under
+ // 100,000 bytes. In non-strict mode, we relax the limit to 1MB.
const size_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
const size_t frame_size =
ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
@@ -136,15 +148,21 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc,
return nullptr;
// A very large frame may mean corrupt memory or an erroneous frame
// pointer. But also maybe just a plain-old large frame. Assume that if the
- // frame is within the known stack, then it is valid.
+ // frame is within a known stack, then it is valid.
if (frame_size > max_size) {
- if (stack_high < kUnknownStackEnd &&
+ size_t stack_low = stack_info->stack_low;
+ size_t stack_high = stack_info->stack_high;
+ if (InsideSignalStack(new_frame_pointer, stack_info)) {
+ stack_low = stack_info->sig_stack_low;
+ stack_high = stack_info->sig_stack_high;
+ }
+ if (stack_high < kUnknownStackEnd &&
static_cast<size_t>(getpagesize()) < stack_low) {
const uintptr_t new_fp_u =
reinterpret_cast<uintptr_t>(new_frame_pointer);
// Stack bounds are known.
if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) {
- // new_frame_pointer is not within the known stack.
+ // new_frame_pointer is not within a known stack.
return nullptr;
}
} else {
@@ -174,8 +192,11 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
int n = 0;
// Assume that the first page is not stack.
- size_t stack_low = static_cast<size_t>(getpagesize());
- size_t stack_high = kUnknownStackEnd;
+ StackInfo stack_info;
+ stack_info.stack_low = getpagesize();
+ stack_info.stack_high = kUnknownStackEnd;
+ stack_info.sig_stack_low = stack_info.stack_low;
+ stack_info.sig_stack_high = kUnknownStackEnd;
// The frame pointer points to low address of a frame. The first 64-bit
// word of a frame points to the next frame up the call chain, which normally
@@ -210,7 +231,7 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
// that is as complete as possible (even if it contains a few bogus
// entries in some rare cases).
frame_pointer = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(
- frame_pointer, ucp, stack_low, stack_high);
+ frame_pointer, ucp, &stack_info);
}
if (min_dropped_frames != nullptr) {
@@ -225,7 +246,7 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
num_dropped_frames++;
}
frame_pointer = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(
- frame_pointer, ucp, stack_low, stack_high);
+ frame_pointer, ucp, &stack_info);
}
*min_dropped_frames = num_dropped_frames;
}