From 0be899c0f2d55b21ac417857183887c4380f8558 Mon Sep 17 00:00:00 2001 From: Rainer Orth Date: Wed, 18 Sep 2024 10:53:32 +0200 Subject: [PATCH] [ASan][test] Fix TestCases/Posix/stack-overflow.cpp on Solaris/sparcv9 When ASan testing is enabled on SPARC as per PR #107405, the ``` AddressSanitizer-sparc-sunos :: TestCases/Posix/stack-overflow.cpp ``` test `FAIL`s: ``` compiler-rt/test/asan/TestCases/Posix/stack-overflow.cpp:80:12: error: CHECK: expected string not found in input // CHECK: {{stack-overflow on address 0x.* \(pc 0x.* bp 0x.* sp 0x.* T.*\)}} ^ AddressSanitizer:DEADLYSIGNAL AddressSanitizer:DEADLYSIGNAL ================================================================= ==11358==ERROR: AddressSanitizer: SEGV on unknown address 0xff3fff90 (pc 0x000db0c0 bp 0xfeed59f8 sp 0xfeed5978 T0) ==11358==The signal is caused by a READ memory access. AddressSanitizer:DEADLYSIGNAL AddressSanitizer: nested bug in the same thread, aborting. ``` It turns out that `sanitizer_linux.cpp` (`GetPcSpBp`) tries to dereference the stack pointer to get at the saved frame pointer, which cannot work since `sp` has been invalidated by the stack overflow in the test. The access attempt thus leads to a second `SEGV`. Solaris `walkcontext(3C)` doesn't have that problem: in the original OpenSolaris sources (`$SRC/lib/libc/port/gen/walkstack.c`) they used `/proc/self/as` to avoid the fault, which is quite heavy-handed. Solaris 11.4 uses a non-faulting load instead (`load_no_fault_uint32`, which just uses the `lduwa` insn). This patch follows this lead, returning a `NULL` `bp` in the failure case. Unfortunately, this leads to `SEGV`s in the depth of the unwinder, so this patch avoids printing a stack trace in this case. Tested on `sparcv9-sun-solaris2.11` and `sparc64-unknown-linux-gnu`. --- .../lib/sanitizer_common/sanitizer_linux.cpp | 18 ++++++++++++++++-- .../sanitizer_symbolizer_report.cpp | 15 +++++++++------ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp index 6359f4348e3c4..1da3ce763302e 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp @@ -107,7 +107,9 @@ extern struct ps_strings *__ps_strings; # endif // SANITIZER_NETBSD # if SANITIZER_SOLARIS +# include # include +# include # include # define environ _environ # endif @@ -2617,7 +2619,19 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { # if SANITIZER_SOLARIS ucontext_t *ucontext = (ucontext_t *)context; *pc = ucontext->uc_mcontext.gregs[REG_PC]; - *sp = ucontext->uc_mcontext.gregs[REG_O6] + STACK_BIAS; + *sp = ucontext->uc_mcontext.gregs[REG_SP] + STACK_BIAS; + // Avoid SEGV when dereferencing sp on stack overflow with non-faulting load. + // This requires a SPARC V9 CPU. Cannot use #ASI_PNF here: only supported + // since clang-19. +# if defined(__sparcv9) + asm("ldxa [%[fp]] 0x82, %[bp]" +# else + asm("lduwa [%[fp]] 0x82, %[bp]" +# endif + : [bp] "=r"(*bp) + : [fp] "r"(&((struct frame *)*sp)->fr_savfp)); + if (*bp) + *bp += STACK_BIAS; # else // Historical BSDism here. struct sigcontext *scontext = (struct sigcontext *)context; @@ -2628,8 +2642,8 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { *pc = scontext->si_regs.pc; *sp = scontext->si_regs.u_regs[14]; # endif -# endif *bp = (uptr)((uhwptr *)*sp)[14] + STACK_BIAS; +# endif # elif defined(__mips__) ucontext_t *ucontext = (ucontext_t *)context; *pc = ucontext->uc_mcontext.pc; diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_report.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_report.cpp index ffbaf1468ec8f..351e00db6fb2d 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_report.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_report.cpp @@ -227,12 +227,15 @@ static void ReportStackOverflowImpl(const SignalContext &sig, u32 tid, SanitizerToolName, kDescription, (void *)sig.addr, (void *)sig.pc, (void *)sig.bp, (void *)sig.sp, tid); Printf("%s", d.Default()); - InternalMmapVector stack_buffer(1); - BufferedStackTrace *stack = stack_buffer.data(); - stack->Reset(); - unwind(sig, unwind_context, stack); - stack->Print(); - ReportErrorSummary(kDescription, stack); + // Avoid SEGVs in the unwinder when bp couldn't be determined. + if (sig.bp) { + InternalMmapVector stack_buffer(1); + BufferedStackTrace *stack = stack_buffer.data(); + stack->Reset(); + unwind(sig, unwind_context, stack); + stack->Print(); + ReportErrorSummary(kDescription, stack); + } } static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid,