[GWP-ASan] Split the unwinder into segv/non-segv.
Note: Resubmission with frame pointers force-enabled to fix builds with
-DCOMPILER_RT_BUILD_BUILTINS=False
Summary:
Splits the unwinder into a non-segv (for allocation/deallocation traces) and a
segv unwinder. This ensures that implementations can select an accurate, slower
unwinder in the segv handler (if they choose to use the GWP-ASan provided one).
This is important as fast frame-pointer unwinders (like the sanitizer unwinder)
don't like unwinding through signal handlers.
Reviewers: morehouse, cryptoad
Reviewed By: morehouse, cryptoad
Subscribers: cryptoad, mgorny, eugenis, pcc, #sanitizers
Tags: #sanitizers
Differential Revision: https://reviews.llvm.org/D83994
GitOrigin-RevId: 4f029d1be4e19270416637327f56668744e64b5c
diff --git a/optional/backtrace_linux_libc.cpp b/optional/backtrace_linux_libc.cpp
index bb0aad2..92eb293 100644
--- a/optional/backtrace_linux_libc.cpp
+++ b/optional/backtrace_linux_libc.cpp
@@ -23,6 +23,14 @@
return backtrace(reinterpret_cast<void **>(TraceBuffer), Size);
}
+// We don't need any custom handling for the Segv backtrace - the libc unwinder
+// has no problems with unwinding through a signal handler. Force inlining here
+// to avoid the additional frame.
+GWP_ASAN_ALWAYS_INLINE size_t SegvBacktrace(uintptr_t *TraceBuffer, size_t Size,
+ void * /*Context*/) {
+ return Backtrace(TraceBuffer, Size);
+}
+
static void PrintBacktrace(uintptr_t *Trace, size_t TraceLength,
gwp_asan::crash_handler::Printf_t Printf) {
if (TraceLength == 0) {
@@ -53,4 +61,8 @@
return PrintBacktrace;
}
} // namespace options
+
+namespace crash_handler {
+SegvBacktrace_t getSegvBacktraceFunction() { return SegvBacktrace; }
+} // namespace crash_handler
} // namespace gwp_asan
diff --git a/optional/backtrace_sanitizer_common.cpp b/optional/backtrace_sanitizer_common.cpp
index 3ac4b52..a8083e4 100644
--- a/optional/backtrace_sanitizer_common.cpp
+++ b/optional/backtrace_sanitizer_common.cpp
@@ -22,28 +22,45 @@
void *context,
bool request_fast,
u32 max_depth) {
- if (!StackTrace::WillUseFastUnwind(request_fast)) {
- return Unwind(max_depth, pc, bp, context, 0, 0, request_fast);
- }
- Unwind(max_depth, pc, 0, context, 0, 0, false);
+ if (!StackTrace::WillUseFastUnwind(request_fast))
+ return Unwind(max_depth, pc, 0, context, 0, 0, false);
+
+ uptr top = 0;
+ uptr bottom = 0;
+ GetThreadStackTopAndBottom(/*at_initialization*/ false, &top, &bottom);
+
+ return Unwind(max_depth, pc, bp, context, top, bottom, request_fast);
}
namespace {
-size_t Backtrace(uintptr_t *TraceBuffer, size_t Size) {
+size_t BacktraceCommon(uintptr_t *TraceBuffer, size_t Size, void *Context) {
+ // Use the slow sanitizer unwinder in the segv handler. Fast frame pointer
+ // unwinders can end up dropping frames because the kernel sigreturn() frame's
+ // return address is the return address at time of fault. This has the result
+ // of never actually capturing the PC where the signal was raised.
+ bool UseFastUnwind = (Context == nullptr);
+
__sanitizer::BufferedStackTrace Trace;
Trace.Reset();
if (Size > __sanitizer::kStackTraceMax)
Size = __sanitizer::kStackTraceMax;
Trace.Unwind((__sanitizer::uptr)__builtin_return_address(0),
- (__sanitizer::uptr)__builtin_frame_address(0),
- /* ucontext */ nullptr,
- /* fast unwind */ true, Size - 1);
+ (__sanitizer::uptr)__builtin_frame_address(0), Context,
+ UseFastUnwind, Size - 1);
memcpy(TraceBuffer, Trace.trace, Trace.size * sizeof(uintptr_t));
return Trace.size;
}
+size_t Backtrace(uintptr_t *TraceBuffer, size_t Size) {
+ return BacktraceCommon(TraceBuffer, Size, nullptr);
+}
+
+size_t SegvBacktrace(uintptr_t *TraceBuffer, size_t Size, void *Context) {
+ return BacktraceCommon(TraceBuffer, Size, Context);
+}
+
static void PrintBacktrace(uintptr_t *Trace, size_t TraceLength,
gwp_asan::crash_handler::Printf_t Printf) {
__sanitizer::StackTrace StackTrace;
@@ -77,4 +94,8 @@
return PrintBacktrace;
}
} // namespace options
+
+namespace crash_handler {
+SegvBacktrace_t getSegvBacktraceFunction() { return SegvBacktrace; }
+} // namespace crash_handler
} // namespace gwp_asan
diff --git a/optional/segv_handler.h b/optional/segv_handler.h
index 10af150..0fed4f2 100644
--- a/optional/segv_handler.h
+++ b/optional/segv_handler.h
@@ -59,6 +59,15 @@
// without any symbolization.
PrintBacktrace_t getBasicPrintBacktraceFunction();
+// Returns a function pointer to a backtrace function that's suitable for
+// unwinding through a signal handler. This is important primarily for frame-
+// pointer based unwinders, DWARF or other unwinders can simply provide the
+// normal backtrace function as the implementation here. On POSIX, SignalContext
+// should be the `ucontext_t` from the signal handler.
+typedef size_t (*SegvBacktrace_t)(uintptr_t *TraceBuffer, size_t Size,
+ void *SignalContext);
+SegvBacktrace_t getSegvBacktraceFunction();
+
// Install the SIGSEGV crash handler for printing use-after-free and heap-
// buffer-{under|over}flow exceptions if the user asked for it. This is platform
// specific as even though POSIX and Windows both support registering handlers
@@ -67,14 +76,14 @@
// before this function.
void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf,
PrintBacktrace_t PrintBacktrace,
- options::Backtrace_t Backtrace);
+ SegvBacktrace_t SegvBacktrace);
void uninstallSignalHandlers();
void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
const gwp_asan::AllocationMetadata *Metadata,
- options::Backtrace_t Backtrace, Printf_t Printf,
- PrintBacktrace_t PrintBacktrace);
+ SegvBacktrace_t SegvBacktrace, Printf_t Printf,
+ PrintBacktrace_t PrintBacktrace, void *Context);
} // namespace crash_handler
} // namespace gwp_asan
diff --git a/optional/segv_handler_posix.cpp b/optional/segv_handler_posix.cpp
index 22589b8..1bd7a60 100644
--- a/optional/segv_handler_posix.cpp
+++ b/optional/segv_handler_posix.cpp
@@ -23,14 +23,14 @@
using gwp_asan::GuardedPoolAllocator;
using gwp_asan::crash_handler::PrintBacktrace_t;
using gwp_asan::crash_handler::Printf_t;
-using gwp_asan::options::Backtrace_t;
+using gwp_asan::crash_handler::SegvBacktrace_t;
struct sigaction PreviousHandler;
bool SignalHandlerInstalled;
gwp_asan::GuardedPoolAllocator *GPAForSignalHandler;
Printf_t PrintfForSignalHandler;
PrintBacktrace_t PrintBacktraceForSignalHandler;
-Backtrace_t BacktraceForSignalHandler;
+SegvBacktrace_t BacktraceForSignalHandler;
static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
if (GPAForSignalHandler) {
@@ -40,7 +40,7 @@
reinterpret_cast<uintptr_t>(info->si_addr),
GPAForSignalHandler->getAllocatorState(),
GPAForSignalHandler->getMetadataRegion(), BacktraceForSignalHandler,
- PrintfForSignalHandler, PrintBacktraceForSignalHandler);
+ PrintfForSignalHandler, PrintBacktraceForSignalHandler, ucontext);
}
// Process any previous handlers.
@@ -138,11 +138,11 @@
void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf,
PrintBacktrace_t PrintBacktrace,
- options::Backtrace_t Backtrace) {
+ SegvBacktrace_t SegvBacktrace) {
GPAForSignalHandler = GPA;
PrintfForSignalHandler = Printf;
PrintBacktraceForSignalHandler = PrintBacktrace;
- BacktraceForSignalHandler = Backtrace;
+ BacktraceForSignalHandler = SegvBacktrace;
struct sigaction Action;
Action.sa_sigaction = sigSegvHandler;
@@ -160,8 +160,8 @@
void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
const gwp_asan::AllocationMetadata *Metadata,
- options::Backtrace_t Backtrace, Printf_t Printf,
- PrintBacktrace_t PrintBacktrace) {
+ SegvBacktrace_t SegvBacktrace, Printf_t Printf,
+ PrintBacktrace_t PrintBacktrace, void *Context) {
assert(State && "dumpReport missing Allocator State.");
assert(Metadata && "dumpReport missing Metadata.");
assert(Printf && "dumpReport missing Printf.");
@@ -194,7 +194,8 @@
// Print the fault backtrace.
static constexpr unsigned kMaximumStackFramesForCrashTrace = 512;
uintptr_t Trace[kMaximumStackFramesForCrashTrace];
- size_t TraceLength = Backtrace(Trace, kMaximumStackFramesForCrashTrace);
+ size_t TraceLength =
+ SegvBacktrace(Trace, kMaximumStackFramesForCrashTrace, Context);
PrintBacktrace(Trace, TraceLength, Printf);
diff --git a/tests/harness.h b/tests/harness.h
index e47254e..d303b2c 100644
--- a/tests/harness.h
+++ b/tests/harness.h
@@ -86,7 +86,8 @@
gwp_asan::crash_handler::installSignalHandlers(
&GPA, gwp_asan::test::getPrintfFunction(),
- gwp_asan::options::getPrintBacktraceFunction(), Opts.Backtrace);
+ gwp_asan::options::getPrintBacktraceFunction(),
+ gwp_asan::crash_handler::getSegvBacktraceFunction());
}
void TearDown() override {