[compiler-rt] [sanitizer_common] Disable the fast unwinder on Alpha Linux (PR #220231)
Matt Turner via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 5 20:07:19 PDT 2026
https://github.com/mattst88 updated https://github.com/llvm/llvm-project/pull/220231
>From 28cdf392e06290659750703e157b92abbd23a17c Mon Sep 17 00:00:00 2001
From: Matt Turner <mattst88 at gmail.com>
Date: Tue, 1 Sep 2026 08:11:42 -0400
Subject: [PATCH 1/2] [sanitizer_common] Disable the fast unwinder on Alpha
Linux
BufferedStackTrace::UnwindFast walks the stack assuming frame[0] is the
caller's frame pointer and frame[1] the return address. Alpha chains
neither, so the walk yields a few garbage frames and then stops:
freed by thread T0 here:
#0 0x020000971274 in free asan_malloc_linux.cpp:51
#1 0x0200007fa48c (<unknown module>)
Opt Alpha out of the fast unwinder the way Linux/MIPS already is. The
_Unwind_Backtrace based unwinder gives complete traces; with it, GCC's
g++.dg/asan/deep-stack-uaf-1.C passes on alpha-unknown-linux-gnu.
Assisted-by: Claude Code
---
compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h
index 47aed488c71a7..3aea61ba2dc8c 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h
@@ -22,12 +22,12 @@ struct BufferedStackTrace;
static const u32 kStackTraceMax = 255;
-#if SANITIZER_LINUX && defined(__mips__)
-# define SANITIZER_CAN_FAST_UNWIND 0
+#if SANITIZER_LINUX && (defined(__mips__) || defined(__alpha__))
+# define SANITIZER_CAN_FAST_UNWIND 0
#elif SANITIZER_WINDOWS
-# define SANITIZER_CAN_FAST_UNWIND 0
+# define SANITIZER_CAN_FAST_UNWIND 0
#else
-# define SANITIZER_CAN_FAST_UNWIND 1
+# define SANITIZER_CAN_FAST_UNWIND 1
#endif
// Fast unwind is the only option on Mac for now; we will need to
>From e016ddb1c97a1973f267cef81c35cfe29a68fb84 Mon Sep 17 00:00:00 2001
From: Matt Turner <mattst88 at gmail.com>
Date: Sat, 5 Sep 2026 22:29:55 -0400
Subject: [PATCH 2/2] [asan][test] Check that stack traces are not truncated
Add a test that the access, free and malloc traces each name the whole
call chain down to main, in order, and that each names its own call site
in main. A trace that stops early -- as every trace but the access one
does when the fast unwinder runs on a target that chains no frames --
fails it.
deep_stack_uaf.cpp already covers trace depth, but it looks for three
individual frames anywhere in the trace, so it passes on a trace with
holes in it, and it needs C++ name demangling to do that.
Assisted-by: Claude Code
---
.../asan/TestCases/complete_stack_trace.c | 52 +++++++++++++++++++
1 file changed, 52 insertions(+)
create mode 100644 compiler-rt/test/asan/TestCases/complete_stack_trace.c
diff --git a/compiler-rt/test/asan/TestCases/complete_stack_trace.c b/compiler-rt/test/asan/TestCases/complete_stack_trace.c
new file mode 100644
index 0000000000000..d9f01af6ed217
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/complete_stack_trace.c
@@ -0,0 +1,52 @@
+// Check that a report's stack traces are complete: the access, the free and the
+// malloc traces must each name the whole call chain, not just the innermost
+// frame or two. An unwinder that walks a frame layout the target does not use
+// stops early and still prints a plausible looking report.
+
+// RUN: %clang_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// REQUIRES: stable-runtime
+
+#include <stdlib.h>
+
+char *p;
+
+__attribute__((noinline)) void alloc_3(void) { p = (char *)malloc(10); }
+__attribute__((noinline)) void alloc_2(void) { alloc_3(); }
+__attribute__((noinline)) void alloc_1(void) { alloc_2(); }
+
+__attribute__((noinline)) void free_3(void) { free(p); }
+__attribute__((noinline)) void free_2(void) { free_3(); }
+__attribute__((noinline)) void free_1(void) { free_2(); }
+
+__attribute__((noinline)) char read_3(void) { return p[5]; }
+__attribute__((noinline)) char read_2(void) { return read_3(); }
+__attribute__((noinline)) char read_1(void) { return read_2(); }
+
+int main() {
+ alloc_1();
+ free_1();
+ return read_1();
+}
+
+// The free and malloc traces start inside the interceptor, whose frame count
+// varies, so only the chain below it is pinned. main's three call sites are on
+// three lines, so each trace has to name its own.
+
+// CHECK: ERROR: AddressSanitizer: heap-use-after-free on address
+// CHECK: READ of size 1 at 0x{{.*}} thread T0
+// CHECK-NEXT: {{ *#0 0x.* in read_3 .*complete_stack_trace.c}}
+// CHECK-NEXT: {{ *#1 0x.* in read_2 .*complete_stack_trace.c}}
+// CHECK-NEXT: {{ *#2 0x.* in read_1 .*complete_stack_trace.c}}
+// CHECK-NEXT: {{ *#3 0x.* in main .*complete_stack_trace.c:}}[[@LINE-12]]
+
+// CHECK: freed by thread T0 here:
+// CHECK: {{ *#[0-9]+ 0x.* in free_3 .*complete_stack_trace.c}}
+// CHECK-NEXT: {{ *#[0-9]+ 0x.* in free_2 .*complete_stack_trace.c}}
+// CHECK-NEXT: {{ *#[0-9]+ 0x.* in free_1 .*complete_stack_trace.c}}
+// CHECK-NEXT: {{ *#[0-9]+ 0x.* in main .*complete_stack_trace.c:}}[[@LINE-19]]
+
+// CHECK: previously allocated by thread T0 here:
+// CHECK: {{ *#[0-9]+ 0x.* in alloc_3 .*complete_stack_trace.c}}
+// CHECK-NEXT: {{ *#[0-9]+ 0x.* in alloc_2 .*complete_stack_trace.c}}
+// CHECK-NEXT: {{ *#[0-9]+ 0x.* in alloc_1 .*complete_stack_trace.c}}
+// CHECK-NEXT: {{ *#[0-9]+ 0x.* in main .*complete_stack_trace.c:}}[[@LINE-26]]
More information about the llvm-commits
mailing list