[compiler-rt] [ASan][test] Fix TestCases/Posix/stack-overflow.cpp on Solaris/sparcv9 (PR #109101)

Rainer Orth via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 18 01:56:08 PDT 2024


https://github.com/rorth created https://github.com/llvm/llvm-project/pull/109101

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`.

>From 0be899c0f2d55b21ac417857183887c4380f8558 Mon Sep 17 00:00:00 2001
From: Rainer Orth <ro at gcc.gnu.org>
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 6359f4348e3c48..1da3ce763302e3 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 <stddef.h>
 #    include <stdlib.h>
+#    include <sys/frame.h>
 #    include <thread.h>
 #    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 ffbaf1468ec8ff..351e00db6fb2dc 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<BufferedStackTrace> 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<BufferedStackTrace> 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,



More information about the llvm-commits mailing list