Commit bb7bbb12 by Abseil Team Committed by Copybara-Service

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
parent 116ee0fe
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
// Generate stack tracer for aarch64 // Generate stack tracer for aarch64
#if defined(__linux__) #if defined(__linux__)
#include <signal.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <ucontext.h> #include <ucontext.h>
#include <unistd.h> #include <unistd.h>
...@@ -70,7 +71,7 @@ static const unsigned char* GetKernelRtSigreturnAddress() { ...@@ -70,7 +71,7 @@ static const unsigned char* GetKernelRtSigreturnAddress() {
// Compute the size of a stack frame in [low..high). We assume that // Compute the size of a stack frame in [low..high). We assume that
// low < high. Return size of kUnknownFrameSize. // low < high. Return size of kUnknownFrameSize.
template<typename T> template<typename T>
static inline size_t ComputeStackFrameSize(const T* low, static size_t ComputeStackFrameSize(const T* low,
const T* high) { const T* high) {
const char* low_char_ptr = reinterpret_cast<const char *>(low); const char* low_char_ptr = reinterpret_cast<const char *>(low);
const char* high_char_ptr = reinterpret_cast<const char *>(high); const char* high_char_ptr = reinterpret_cast<const char *>(high);
...@@ -78,6 +79,20 @@ static inline size_t ComputeStackFrameSize(const T* low, ...@@ -78,6 +79,20 @@ static inline size_t ComputeStackFrameSize(const T* low,
: kUnknownFrameSize; : 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 // Given a pointer to a stack frame, locate and return the calling
// stackframe, or return null if no stackframe can be found. Perform sanity // stackframe, or return null if no stackframe can be found. Perform sanity
// checks (the strictness of which is controlled by the boolean parameter // checks (the strictness of which is controlled by the boolean parameter
...@@ -86,9 +101,8 @@ template<bool STRICT_UNWINDING, bool WITH_CONTEXT> ...@@ -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_ADDRESS // May read random elements from stack.
ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // 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, 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); void **new_frame_pointer = reinterpret_cast<void**>(*old_frame_pointer);
bool check_frame_size = true;
#if defined(__linux__) #if defined(__linux__)
if (WITH_CONTEXT && uc != nullptr) { if (WITH_CONTEXT && uc != nullptr) {
...@@ -114,10 +128,6 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc, ...@@ -114,10 +128,6 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc,
if (!absl::debugging_internal::AddressIsReadable( if (!absl::debugging_internal::AddressIsReadable(
new_frame_pointer)) new_frame_pointer))
return nullptr; 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 #endif
...@@ -126,9 +136,11 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc, ...@@ -126,9 +136,11 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc,
if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 7) != 0) if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 7) != 0)
return nullptr; return nullptr;
// 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 // 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. // 100,000 bytes. In non-strict mode, we relax the limit to 1MB.
if (check_frame_size) {
const size_t max_size = STRICT_UNWINDING ? 100000 : 1000000; const size_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
const size_t frame_size = const size_t frame_size =
ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
...@@ -136,15 +148,21 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc, ...@@ -136,15 +148,21 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc,
return nullptr; return nullptr;
// A very large frame may mean corrupt memory or an erroneous frame // 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 // 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 (frame_size > max_size) {
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 && if (stack_high < kUnknownStackEnd &&
static_cast<size_t>(getpagesize()) < stack_low) { static_cast<size_t>(getpagesize()) < stack_low) {
const uintptr_t new_fp_u = const uintptr_t new_fp_u =
reinterpret_cast<uintptr_t>(new_frame_pointer); reinterpret_cast<uintptr_t>(new_frame_pointer);
// Stack bounds are known. // Stack bounds are known.
if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) { 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; return nullptr;
} }
} else { } else {
...@@ -174,8 +192,11 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, ...@@ -174,8 +192,11 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
int n = 0; int n = 0;
// Assume that the first page is not stack. // Assume that the first page is not stack.
size_t stack_low = static_cast<size_t>(getpagesize()); StackInfo stack_info;
size_t stack_high = kUnknownStackEnd; 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 // 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 // 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, ...@@ -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 // that is as complete as possible (even if it contains a few bogus
// entries in some rare cases). // entries in some rare cases).
frame_pointer = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>( 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) { if (min_dropped_frames != nullptr) {
...@@ -225,7 +246,7 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, ...@@ -225,7 +246,7 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
num_dropped_frames++; num_dropped_frames++;
} }
frame_pointer = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>( 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; *min_dropped_frames = num_dropped_frames;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment