[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