Commit 9ab9e487 by Saleem Abdulrasool Committed by Copybara-Service

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
parent 4b551344
...@@ -32,54 +32,10 @@ ...@@ -32,54 +32,10 @@
#include <iostream> #include <iostream>
#include "absl/base/attributes.h" #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" #include "absl/debugging/stacktrace.h"
static const uintptr_t kUnknownFrameSize = 0; 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. // Compute the size of a stack frame in [low..high). We assume that low < high.
// Return size of kUnknownFrameSize. // Return size of kUnknownFrameSize.
template <typename T> template <typename T>
...@@ -115,40 +71,23 @@ static void ** NextStackFrame(void **old_frame_pointer, const void *uc, ...@@ -115,40 +71,23 @@ static void ** NextStackFrame(void **old_frame_pointer, const void *uc,
// $sp ->| ... | // $sp ->| ... |
// +----------------+ // +----------------+
void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]); 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 // The RISCV ELF psABI mandates that the stack pointer is always 16-byte
// aligned. // aligned.
// TODO(#1236) 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. // alignment.
if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 15) != 0) if (frame_pointer & 15)
return nullptr; return nullptr;
#if defined(__linux__) // If the new frame pointer matches the signal context, avoid terminating
if (WITH_CONTEXT && uc != nullptr) { // early to deal with alternate signal stacks.
// Check to see if next frame's return address is __kernel_rt_sigreturn. if (WITH_CONTEXT)
if (old_frame_pointer[-1] == GetKernelRtSigreturnAddress()) { if (const ucontext_t *ucv = static_cast<const ucontext_t *>(uc))
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 ELF psABI has the frame pointer at x8/fp/s0.
// -- RISCV psABI Table 18.2 // -- RISCV psABI Table 18.2
void **const pre_signal_frame_pointer = if (ucv->uc_mcontext.__gregs[8] == frame_pointer)
reinterpret_cast<void **>(ucv->uc_mcontext.__gregs[8]); return new_frame_pointer;
// 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
// Check frame size. In strict mode, we assume frames to be under 100,000 // 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. // 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, ...@@ -165,6 +104,7 @@ static void ** NextStackFrame(void **old_frame_pointer, const void *uc,
reinterpret_cast<uintptr_t>(new_frame_pointer) > range.second) reinterpret_cast<uintptr_t>(new_frame_pointer) > range.second)
return nullptr; return nullptr;
} }
if (frame_size > max_size) if (frame_size > max_size)
return nullptr; return nullptr;
......
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