diff options
author | Abseil Team <absl-team@google.com> | 2023-11-07 10:18:36 -0800 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-11-07 10:19:31 -0800 |
commit | bb7bbb12c770cfcbc3fcde9afca6964e962f500e (patch) | |
tree | a73ad5339fc4d90914d4b5c87e6e3cba625f780a /absl | |
parent | 116ee0fed75c1d032604f3b675e7e44e10d124b4 (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
Diffstat (limited to 'absl')
-rw-r--r-- | absl/debugging/internal/stacktrace_aarch64-inl.inc | 55 |
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; } |