diff options
Diffstat (limited to 'absl/debugging/internal/stacktrace_riscv-inl.inc')
-rw-r--r-- | absl/debugging/internal/stacktrace_riscv-inl.inc | 125 |
1 files changed, 40 insertions, 85 deletions
diff --git a/absl/debugging/internal/stacktrace_riscv-inl.inc b/absl/debugging/internal/stacktrace_riscv-inl.inc index 7123b71b..20183fa3 100644 --- a/absl/debugging/internal/stacktrace_riscv-inl.inc +++ b/absl/debugging/internal/stacktrace_riscv-inl.inc @@ -30,56 +30,14 @@ #include <cassert> #include <cstdint> #include <iostream> +#include <limits> +#include <utility> #include "absl/base/attributes.h" -#include "absl/debugging/internal/address_is_readable.h" -#include "absl/debugging/internal/vdso_support.h" #include "absl/debugging/stacktrace.h" static const uintptr_t kUnknownFrameSize = 0; -#if defined(__linux__) -// Returns the address of the VDSO __kernel_rt_sigreturn function, if present. -static const unsigned char *GetKernelRtSigreturnAddress() { - constexpr uintptr_t kImpossibleAddress = 0; - ABSL_CONST_INIT static std::atomic<uintptr_t> memoized(kImpossibleAddress); - uintptr_t address = memoized.load(std::memory_order_relaxed); - if (address != kImpossibleAddress) { - return reinterpret_cast<const unsigned char *>(address); - } - - address = reinterpret_cast<uintptr_t>(nullptr); - -#if ABSL_HAVE_VDSO_SUPPORT - absl::debugging_internal::VDSOSupport vdso; - if (vdso.IsPresent()) { - absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info; - // Symbol versioning pulled from arch/riscv/kernel/vdso/vdso.lds at v5.10. - auto lookup = [&](int type) { - return vdso.LookupSymbol("__vdso_rt_sigreturn", "LINUX_4.15", type, - &symbol_info); - }; - if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) || - symbol_info.address == nullptr) { - // Unexpected: VDSO is present, yet the expected symbol is missing or - // null. - assert(false && "VDSO is present, but doesn't have expected symbol"); - } else { - if (reinterpret_cast<uintptr_t>(symbol_info.address) != - kImpossibleAddress) { - address = reinterpret_cast<uintptr_t>(symbol_info.address); - } else { - assert(false && "VDSO returned invalid address"); - } - } - } -#endif - - memoized.store(address, std::memory_order_relaxed); - return reinterpret_cast<const unsigned char *>(address); -} -#endif // __linux__ - // Compute the size of a stack frame in [low..high). We assume that low < high. // Return size of kUnknownFrameSize. template <typename T> @@ -96,7 +54,8 @@ static inline uintptr_t ComputeStackFrameSize(const T *low, const T *high) { 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) { +static void ** NextStackFrame(void **old_frame_pointer, const void *uc, + const std::pair<size_t, size_t> range) { // . // . // . @@ -114,55 +73,43 @@ static void ** NextStackFrame(void **old_frame_pointer, const void *uc) { // $sp ->| ... | // +----------------+ void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]); - bool check_frame_size = true; - -#if defined(__linux__) - if (WITH_CONTEXT && uc != nullptr) { - // Check to see if next frame's return address is __kernel_rt_sigreturn. - if (old_frame_pointer[-1] == GetKernelRtSigreturnAddress()) { - const ucontext_t *ucv = static_cast<const ucontext_t *>(uc); - // old_frame_pointer is not suitable for unwinding, look at ucontext to - // discover frame pointer before signal. - // - // RISCV ELF psABI has the frame pointer at x8/fp/s0. - // -- RISCV psABI Table 18.2 - void **const pre_signal_frame_pointer = - reinterpret_cast<void **>(ucv->uc_mcontext.__gregs[8]); - - // Check the alleged frame pointer is actually readable. This is to - // prevent "double fault" in case we hit the first fault due to stack - // corruption. - if (!absl::debugging_internal::AddressIsReadable( - pre_signal_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 an - // alterate stack for signals. - check_frame_size = false; - } - } -#endif + uintptr_t frame_pointer = reinterpret_cast<uintptr_t>(new_frame_pointer); // The RISCV ELF psABI mandates that the stack pointer is always 16-byte // aligned. - // FIXME(abdulras) this doesn't hold for ILP32E which only mandates a 4-byte + // TODO(#1236) this doesn't hold for ILP32E which only mandates a 4-byte // alignment. - if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 15) != 0) + if (frame_pointer & 15) return nullptr; + // If the new frame pointer matches the signal context, avoid terminating + // early to deal with alternate signal stacks. + if (WITH_CONTEXT) + if (const ucontext_t *ucv = static_cast<const ucontext_t *>(uc)) + // RISCV ELF psABI has the frame pointer at x8/fp/s0. + // -- RISCV psABI Table 18.2 + if (ucv->uc_mcontext.__gregs[8] == frame_pointer) + return new_frame_pointer; + // 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) { - const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; - const uintptr_t frame_size = - ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); - if (frame_size == kUnknownFrameSize || frame_size > max_size) + const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; + const uintptr_t frame_size = + ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); + if (frame_size == kUnknownFrameSize) { + if (STRICT_UNWINDING) + return nullptr; + + // In non-strict mode permit non-contiguous stacks (e.g. alternate signal + // frame handling). + if (reinterpret_cast<uintptr_t>(new_frame_pointer) < range.first || + reinterpret_cast<uintptr_t>(new_frame_pointer) > range.second) return nullptr; } + if (frame_size > max_size) + return nullptr; + return new_frame_pointer; } @@ -180,6 +127,12 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, #error reading stack pointer not yet supported on this platform #endif + std::pair<size_t, size_t> stack = { + // assume that the first page is not the stack. + static_cast<size_t>(sysconf(_SC_PAGESIZE)), + std::numeric_limits<size_t>::max() - sizeof(void *) + }; + int n = 0; void *return_address = nullptr; while (frame_pointer && n < max_depth) { @@ -190,7 +143,8 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, // non-strict unwinding rules to produce a stack trace that is as complete // as possible (even if it contains a few bogus entries in some rare cases). void **next_frame_pointer = - NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp); + NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, + stack); if (skip_count > 0) { skip_count--; @@ -217,7 +171,8 @@ 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); + NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, + stack); } *min_dropped_frames = num_dropped_frames; } |