[compiler-rt] fb65131 - Add API to temporalily suppress usage of ASAN's fake stack (#160135)

via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 5 09:53:26 PST 2025


Author: happyCoder92
Date: 2025-12-05T09:53:21-08:00
New Revision: fb6513130de7578c847aaaf6c278809b6c2476c3

URL: https://github.com/llvm/llvm-project/commit/fb6513130de7578c847aaaf6c278809b6c2476c3
DIFF: https://github.com/llvm/llvm-project/commit/fb6513130de7578c847aaaf6c278809b6c2476c3.diff

LOG: Add API to temporalily suppress usage of ASAN's fake stack (#160135)

Intended use-case is for threads that use (or switch to) stack with
special properties e.g. backed by MADV_DONTDUMP memory.

---------

Co-authored-by: Vitaly Buka <vitalybuka at google.com>

Added: 
    compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp
    compiler-rt/test/asan/TestCases/suppress_fake_stack_force_disabled.cpp

Modified: 
    compiler-rt/include/sanitizer/asan_interface.h
    compiler-rt/lib/asan/asan_fake_stack.cpp
    compiler-rt/lib/asan/asan_interface.inc
    compiler-rt/lib/asan/asan_thread.cpp
    compiler-rt/lib/asan/asan_thread.h
    compiler-rt/lib/asan_abi/asan_abi.cpp
    compiler-rt/lib/asan_abi/asan_abi.h
    compiler-rt/lib/asan_abi/asan_abi_shim.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/include/sanitizer/asan_interface.h b/compiler-rt/include/sanitizer/asan_interface.h
index 37b6d08f4db19..544f2e4b32687 100644
--- a/compiler-rt/include/sanitizer/asan_interface.h
+++ b/compiler-rt/include/sanitizer/asan_interface.h
@@ -333,6 +333,14 @@ void SANITIZER_CDECL __asan_handle_no_return(void);
 /// trace. Returns 1 if successful, 0 if not.
 int SANITIZER_CDECL __asan_update_allocation_context(void *addr);
 
+/// Suppresses fake stack for the current thread.
+/// Temporarily disables use-after-return detection for current thread.
+void SANITIZER_CDECL __asan_suppress_fake_stack(void);
+
+/// Unsupresses fake stack for the current thread.
+/// Should be paired with a previous __asan_suppress_fake_stack() call.
+void SANITIZER_CDECL __asan_unsuppress_fake_stack(void);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif

diff  --git a/compiler-rt/lib/asan/asan_fake_stack.cpp b/compiler-rt/lib/asan/asan_fake_stack.cpp
index d3fa953f31005..af73d31b8a5dc 100644
--- a/compiler-rt/lib/asan/asan_fake_stack.cpp
+++ b/compiler-rt/lib/asan/asan_fake_stack.cpp
@@ -225,9 +225,23 @@ static void SetTLSFakeStack(FakeStack*) {}
 void ResetTLSFakeStack() {}
 #endif  // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA
 
+static void SuppressFakeStack() {
+  AsanThread* t = GetCurrentThread();
+  if (t) {
+    t->SuppressFakeStack();
+  }
+}
+
+static void UnsuppressFakeStack() {
+  AsanThread* t = GetCurrentThread();
+  if (t) {
+    t->UnsuppressFakeStack();
+  }
+}
+
 static FakeStack* GetFakeStack() {
   AsanThread* t = GetCurrentThread();
-  if (!t)
+  if (!t || t->IsFakeStackSuppressed())
     return nullptr;
   return t->get_or_create_fake_stack();
 }
@@ -362,4 +376,9 @@ void __asan_allocas_unpoison(uptr top, uptr bottom) {
   REAL(memset)(reinterpret_cast<void*>(MemToShadow(top)), 0,
                (bottom - top) / ASAN_SHADOW_GRANULARITY);
 }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_suppress_fake_stack() { return SuppressFakeStack(); }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_unsuppress_fake_stack() { return UnsuppressFakeStack(); }
 }  // extern "C"

diff  --git a/compiler-rt/lib/asan/asan_interface.inc b/compiler-rt/lib/asan/asan_interface.inc
index bfc44b4619623..b465356b1e443 100644
--- a/compiler-rt/lib/asan/asan_interface.inc
+++ b/compiler-rt/lib/asan/asan_interface.inc
@@ -165,12 +165,14 @@ INTERFACE_FUNCTION(__asan_store4_noabort)
 INTERFACE_FUNCTION(__asan_store8_noabort)
 INTERFACE_FUNCTION(__asan_store16_noabort)
 INTERFACE_FUNCTION(__asan_storeN_noabort)
+INTERFACE_FUNCTION(__asan_suppress_fake_stack)
 INTERFACE_FUNCTION(__asan_unpoison_intra_object_redzone)
 INTERFACE_FUNCTION(__asan_unpoison_memory_region)
 INTERFACE_FUNCTION(__asan_unpoison_stack_memory)
 INTERFACE_FUNCTION(__asan_unregister_globals)
 INTERFACE_FUNCTION(__asan_unregister_elf_globals)
 INTERFACE_FUNCTION(__asan_unregister_image_globals)
+INTERFACE_FUNCTION(__asan_unsuppress_fake_stack)
 INTERFACE_FUNCTION(__asan_version_mismatch_check_v8)
 INTERFACE_FUNCTION(__sanitizer_finish_switch_fiber)
 INTERFACE_FUNCTION(__sanitizer_print_stack_trace)

diff  --git a/compiler-rt/lib/asan/asan_thread.cpp b/compiler-rt/lib/asan/asan_thread.cpp
index 0ed58bbe2a73a..32ab723e89001 100644
--- a/compiler-rt/lib/asan/asan_thread.cpp
+++ b/compiler-rt/lib/asan/asan_thread.cpp
@@ -251,6 +251,7 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
 void AsanThread::Init(const InitOptions *options) {
   DCHECK_NE(tid(), kInvalidTid);
   next_stack_top_ = next_stack_bottom_ = 0;
+  fake_stack_suppression_counter_ = 0;
   atomic_store(&stack_switching_, false, memory_order_release);
   CHECK_EQ(this->stack_size(), 0U);
   SetThreadStackAndTls(options);
@@ -404,6 +405,19 @@ bool AsanThread::AddrIsInStack(uptr addr) {
   return addr >= bounds.bottom && addr < bounds.top;
 }
 
+void AsanThread::SuppressFakeStack() {
+  ++fake_stack_suppression_counter_;
+  ResetTLSFakeStack();
+}
+
+void AsanThread::UnsuppressFakeStack() {
+  if (fake_stack_suppression_counter_ == 0) {
+    Report("ERROR: Unmatched call to __asan_unsuppress_fake_stack().\n");
+    Die();
+  }
+  --fake_stack_suppression_counter_;
+}
+
 static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
                                        void *addr) {
   AsanThreadContext *tctx = static_cast<AsanThreadContext *>(tctx_base);

diff  --git a/compiler-rt/lib/asan/asan_thread.h b/compiler-rt/lib/asan/asan_thread.h
index 19b7f342e1712..e9ca6b6a59016 100644
--- a/compiler-rt/lib/asan/asan_thread.h
+++ b/compiler-rt/lib/asan/asan_thread.h
@@ -144,6 +144,12 @@ class AsanThread {
     GetStartData(&data, sizeof(data));
   }
 
+  bool IsFakeStackSuppressed() const {
+    return fake_stack_suppression_counter_ > 0;
+  }
+  void SuppressFakeStack();
+  void UnsuppressFakeStack();
+
  private:
   // NOTE: There is no AsanThread constructor. It is allocated
   // via mmap() and *must* be valid in zero-initialized state.
@@ -179,6 +185,7 @@ class AsanThread {
   DTLS *dtls_;
 
   FakeStack *fake_stack_;
+  int fake_stack_suppression_counter_;
   AsanThreadLocalMallocStorage malloc_storage_;
   AsanStats stats_;
   bool unwinding_;

diff  --git a/compiler-rt/lib/asan_abi/asan_abi.cpp b/compiler-rt/lib/asan_abi/asan_abi.cpp
index cf8663024eb73..6199bbed09dfc 100644
--- a/compiler-rt/lib/asan_abi/asan_abi.cpp
+++ b/compiler-rt/lib/asan_abi/asan_abi.cpp
@@ -73,6 +73,8 @@ void *__asan_abi_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
                                        void **end) {
   return NULL;
 }
+void __asan_abi_suppress_fake_stack(void) {}
+void __asan_abi_unsuppress_fake_stack(void) {}
 
 // Functions concerning poisoning and unpoisoning fake stack alloca
 void __asan_abi_alloca_poison(void *addr, size_t size) {}

diff  --git a/compiler-rt/lib/asan_abi/asan_abi.h b/compiler-rt/lib/asan_abi/asan_abi.h
index 8702bcd133919..cb59976c8506c 100644
--- a/compiler-rt/lib/asan_abi/asan_abi.h
+++ b/compiler-rt/lib/asan_abi/asan_abi.h
@@ -76,6 +76,9 @@ void *__asan_abi_load_cxx_array_cookie(void **p);
 void *__asan_abi_get_current_fake_stack();
 void *__asan_abi_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
                                        void **end);
+void *__asan_abi_suppress_fake_stack();
+void *__asan_abi_unsuppress_fake_stack();
+
 // Functions concerning poisoning and unpoisoning fake stack alloca
 void __asan_abi_alloca_poison(void *addr, size_t size);
 void __asan_abi_allocas_unpoison(void *top, void *bottom);

diff  --git a/compiler-rt/lib/asan_abi/asan_abi_shim.cpp b/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
index 2512abc641250..7ae7e77aea4a2 100644
--- a/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
+++ b/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
@@ -365,6 +365,12 @@ void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
                                    void **end) {
   return __asan_abi_addr_is_in_fake_stack(fake_stack, addr, beg, end);
 }
+void __asan_suppress_fake_stack(void) {
+  return __asan_abi_suppress_fake_stack();
+}
+void __asan_unsuppress_fake_stack(void) {
+  return __asan_abi_unsuppress_fake_stack();
+}
 
 // Functions concerning poisoning and unpoisoning fake stack alloca
 void __asan_alloca_poison(uptr addr, uptr size) {

diff  --git a/compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp b/compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp
new file mode 100644
index 0000000000000..f072c6ad3b034
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp
@@ -0,0 +1,39 @@
+// RUN: %clangxx_asan %s -o %t && env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t
+// RUN: %clangxx_asan %s -mllvm -asan-use-after-return=runtime -o %t && env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t
+// RUN: %clangxx_asan %s -mllvm -asan-use-after-return=always -o %t && %run %t
+
+#include "defines.h"
+
+#include <cassert>
+#include <sanitizer/asan_interface.h>
+
+volatile uintptr_t saved;
+
+ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame,
+                                      uintptr_t var_addr) {
+  uintptr_t this_frame =
+      reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
+  return this_frame <= var_addr && var_addr <= parent_frame;
+}
+
+ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame) {
+  volatile char temp = ' ';
+  saved = reinterpret_cast<uintptr_t>(&temp);
+  return IsOnRealStack(parent_frame, saved);
+}
+
+ATTRIBUTE_NOINLINE bool IsOnRealStack() {
+  return IsOnRealStack(reinterpret_cast<uintptr_t>(__builtin_frame_address(0)));
+}
+
+int main(int argc, char *argv[]) {
+  assert(!IsOnRealStack());
+
+  __asan_suppress_fake_stack();
+  assert(IsOnRealStack());
+
+  __asan_unsuppress_fake_stack();
+  assert(!IsOnRealStack());
+
+  return 0;
+}

diff  --git a/compiler-rt/test/asan/TestCases/suppress_fake_stack_force_disabled.cpp b/compiler-rt/test/asan/TestCases/suppress_fake_stack_force_disabled.cpp
new file mode 100644
index 0000000000000..c549f08a7f0a8
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/suppress_fake_stack_force_disabled.cpp
@@ -0,0 +1,40 @@
+// Check unsuppressing fake stack does not reenable it if disabled via compile or runtime options.
+//
+// RUN: %clangxx_asan %s -mllvm -asan-use-after-return=never -o %t && %run %t
+// RUN: %clangxx_asan %s -mllvm -asan-use-after-return=runtime -o %t && env ASAN_OPTIONS=detect_stack_use_after_return=0 %run %t
+
+#include "defines.h"
+
+#include <cassert>
+#include <sanitizer/asan_interface.h>
+
+volatile uintptr_t saved;
+
+ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame,
+                                      uintptr_t var_addr) {
+  uintptr_t this_frame =
+      reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
+  return this_frame <= var_addr && var_addr <= parent_frame;
+}
+
+ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame) {
+  volatile char temp = ' ';
+  saved = reinterpret_cast<uintptr_t>(&temp);
+  return IsOnRealStack(parent_frame, saved);
+}
+
+ATTRIBUTE_NOINLINE bool IsOnRealStack() {
+  return IsOnRealStack(reinterpret_cast<uintptr_t>(__builtin_frame_address(0)));
+}
+
+int main(int argc, char *argv[]) {
+  assert(IsOnRealStack());
+
+  __asan_suppress_fake_stack();
+  assert(IsOnRealStack());
+
+  __asan_unsuppress_fake_stack();
+  assert(IsOnRealStack());
+
+  return 0;
+}


        


More information about the llvm-commits mailing list