debugging: handle alternate signal stacks better on RISCV

In the case that we are unwinding with context, if the retreived frame pointer
matches the signal context, assume that the value is valid and do not perform
confidence checks.  In any other case, continue to perform some validation to
avoid returning an incorrect frame pointer.

Given that the VDSO path is currently untested, remove the code to simplify the
logic in the frame walking.

PiperOrigin-RevId: 465360612
Change-Id: Iac656012182a12814bafecf20225ba68b90b4db1
pull/1244/head
Saleem Abdulrasool 2 years ago committed by Copybara-Service
parent 4b551344e6
commit 9ab9e487a6
  1. 78
      absl/debugging/internal/stacktrace_riscv-inl.inc

@ -32,54 +32,10 @@
#include <iostream>
#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>
@ -115,40 +71,23 @@ static void ** NextStackFrame(void **old_frame_pointer, const void *uc,
// $sp ->| ... |
// +----------------+
void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]);
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.
// 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 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.
//
// 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
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;
}
return new_frame_pointer;
}
#endif
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.
@ -165,6 +104,7 @@ static void ** NextStackFrame(void **old_frame_pointer, const void *uc,
reinterpret_cast<uintptr_t>(new_frame_pointer) > range.second)
return nullptr;
}
if (frame_size > max_size)
return nullptr;

Loading…
Cancel
Save