Skip to content

Conversation

happyCoder92
Copy link
Contributor

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

@llvmbot
Copy link
Member

llvmbot commented Sep 22, 2025

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: None (happyCoder92)

Changes

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


Full diff: https://github.com/llvm/llvm-project/pull/160135.diff

10 Files Affected:

  • (modified) compiler-rt/include/sanitizer/asan_interface.h (+7)
  • (modified) compiler-rt/lib/asan/asan_fake_stack.cpp (+30-7)
  • (modified) compiler-rt/lib/asan/asan_fake_stack.h (+3-1)
  • (modified) compiler-rt/lib/asan/asan_interface.inc (+2)
  • (modified) compiler-rt/lib/asan/asan_thread.cpp (+4-3)
  • (modified) compiler-rt/lib/asan/asan_thread.h (+7-1)
  • (modified) compiler-rt/lib/asan_abi/asan_abi.cpp (+2)
  • (modified) compiler-rt/lib/asan_abi/asan_abi.h (+3)
  • (modified) compiler-rt/lib/asan_abi/asan_abi_shim.cpp (+6)
  • (added) compiler-rt/test/asan/TestCases/disable_fake_stack.cpp (+27)
diff --git a/compiler-rt/include/sanitizer/asan_interface.h b/compiler-rt/include/sanitizer/asan_interface.h
index 37b6d08f4db19..06deb9116709f 100644
--- a/compiler-rt/include/sanitizer/asan_interface.h
+++ b/compiler-rt/include/sanitizer/asan_interface.h
@@ -333,6 +333,13 @@ 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);
 
+/// Disables fake stack usage for the current thread.
+/// Temporarily disables use-after-return detection for current thread.
+void SANITIZER_CDECL __asan_disable_fake_stack_usage(void);
+
+/// (Re)enables fake stack usage for the current thread.
+void SANITIZER_CDECL __asan_enable_fake_stack_usage(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 c3ed2526f0ed4..e1772997d681f 100644
--- a/compiler-rt/lib/asan/asan_fake_stack.cpp
+++ b/compiler-rt/lib/asan/asan_fake_stack.cpp
@@ -217,26 +217,44 @@ static THREADLOCAL FakeStack *fake_stack_tls;
 FakeStack *GetTLSFakeStack() {
   return fake_stack_tls;
 }
-void SetTLSFakeStack(FakeStack *fs) {
+void SetTLSFakeStack(AsanThread* t, FakeStack *fs) {
+  if (fs && !t->fake_stack_usage_enabled()) {
+    return;
+  }
   fake_stack_tls = fs;
 }
 #else
 FakeStack *GetTLSFakeStack() { return 0; }
-void SetTLSFakeStack(FakeStack *fs) { }
+void SetTLSFakeStack(AsanThread* t, FakeStack *fs) { }
 #endif  // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA
 
-static FakeStack *GetFakeStack() {
+static void DisableFakeStackUsage() {
   AsanThread *t = GetCurrentThread();
-  if (!t) return nullptr;
+  if (t) {
+    t->set_fake_stack_usage_enabled(false);
+  }
+  SetTLSFakeStack(t, nullptr);
+}
+
+static void EnableFakeStackUsage() {
+  AsanThread *t = GetCurrentThread();
+  if (t) {
+    t->set_fake_stack_usage_enabled(true);
+  }
+}
+
+static FakeStack *GetFakeStack(bool for_allocation = true) {
+  AsanThread *t = GetCurrentThread();
+  if (!t || (for_allocation && !t->fake_stack_usage_enabled())) return nullptr;
   return t->get_or_create_fake_stack();
 }
 
-static FakeStack *GetFakeStackFast() {
+static FakeStack *GetFakeStackFast(bool for_allocation = true) {
   if (FakeStack *fs = GetTLSFakeStack())
     return fs;
   if (!__asan_option_detect_stack_use_after_return)
     return nullptr;
-  return GetFakeStack();
+  return GetFakeStack(for_allocation);
 }
 
 static FakeStack *GetFakeStackFastAlways() {
@@ -311,7 +329,7 @@ extern "C" {
 // -asan-use-after-return=never, after modal UAR flag lands
 // (https://github.com/google/sanitizers/issues/1394)
 SANITIZER_INTERFACE_ATTRIBUTE
-void *__asan_get_current_fake_stack() { return GetFakeStackFast(); }
+void *__asan_get_current_fake_stack() { return GetFakeStackFast(/*for_allocation=*/false); }
 
 SANITIZER_INTERFACE_ATTRIBUTE
 void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
@@ -349,4 +367,9 @@ void __asan_allocas_unpoison(uptr top, uptr bottom) {
   (reinterpret_cast<void *>(MemToShadow(top)), 0,
    (bottom - top) / ASAN_SHADOW_GRANULARITY);
 }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_disable_fake_stack_usage() { return DisableFakeStackUsage(); }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_enable_fake_stack_usage() { return EnableFakeStackUsage(); }
 } // extern "C"
diff --git a/compiler-rt/lib/asan/asan_fake_stack.h b/compiler-rt/lib/asan/asan_fake_stack.h
index 50706e6e5876c..9f1f452834b97 100644
--- a/compiler-rt/lib/asan/asan_fake_stack.h
+++ b/compiler-rt/lib/asan/asan_fake_stack.h
@@ -18,6 +18,8 @@
 
 namespace __asan {
 
+class AsanThread;
+
 // Fake stack frame contains local variables of one function.
 struct FakeFrame {
   uptr magic;  // Modified by the instrumented code.
@@ -196,7 +198,7 @@ class FakeStack {
 };
 
 FakeStack *GetTLSFakeStack();
-void SetTLSFakeStack(FakeStack *fs);
+void SetTLSFakeStack(AsanThread* t, FakeStack *fs);
 
 }  // namespace __asan
 
diff --git a/compiler-rt/lib/asan/asan_interface.inc b/compiler-rt/lib/asan/asan_interface.inc
index bfc44b4619623..a279ae834549a 100644
--- a/compiler-rt/lib/asan/asan_interface.inc
+++ b/compiler-rt/lib/asan/asan_interface.inc
@@ -15,6 +15,8 @@ INTERFACE_FUNCTION(__asan_alloca_poison)
 INTERFACE_FUNCTION(__asan_allocas_unpoison)
 INTERFACE_FUNCTION(__asan_before_dynamic_init)
 INTERFACE_FUNCTION(__asan_describe_address)
+INTERFACE_FUNCTION(__asan_disable_fake_stack_usage)
+INTERFACE_FUNCTION(__asan_enable_fake_stack_usage)
 INTERFACE_FUNCTION(__asan_exp_load1)
 INTERFACE_FUNCTION(__asan_exp_load2)
 INTERFACE_FUNCTION(__asan_exp_load4)
diff --git a/compiler-rt/lib/asan/asan_thread.cpp b/compiler-rt/lib/asan/asan_thread.cpp
index 2627ae1289012..f6f54022bf377 100644
--- a/compiler-rt/lib/asan/asan_thread.cpp
+++ b/compiler-rt/lib/asan/asan_thread.cpp
@@ -163,7 +163,7 @@ void AsanThread::StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom,
   if (fake_stack_save)
     *fake_stack_save = fake_stack_;
   fake_stack_ = nullptr;
-  SetTLSFakeStack(nullptr);
+  SetTLSFakeStack(this, nullptr);
   // if fake_stack_save is null, the fiber will die, delete the fakestack
   if (!fake_stack_save && current_fake_stack)
     current_fake_stack->Destroy(this->tid());
@@ -177,7 +177,7 @@ void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save, uptr *bottom_old,
   }
 
   if (fake_stack_save) {
-    SetTLSFakeStack(fake_stack_save);
+    SetTLSFakeStack(this, fake_stack_save);
     fake_stack_ = fake_stack_save;
   }
 
@@ -242,7 +242,7 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
         Max(stack_size_log, static_cast<uptr>(flags()->min_uar_stack_size_log));
     fake_stack_ = FakeStack::Create(stack_size_log);
     DCHECK_EQ(GetCurrentThread(), this);
-    SetTLSFakeStack(fake_stack_);
+    SetTLSFakeStack(this, fake_stack_);
     return fake_stack_;
   }
   return nullptr;
@@ -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_usage_enabled_ = true;
   atomic_store(&stack_switching_, false, memory_order_release);
   CHECK_EQ(this->stack_size(), 0U);
   SetThreadStackAndTls(options);
diff --git a/compiler-rt/lib/asan/asan_thread.h b/compiler-rt/lib/asan/asan_thread.h
index 12f0cc7a62dae..c54bbb1c64604 100644
--- a/compiler-rt/lib/asan/asan_thread.h
+++ b/compiler-rt/lib/asan/asan_thread.h
@@ -104,7 +104,7 @@ class AsanThread {
     if (!fake_stack_) return;
     FakeStack *t = fake_stack_;
     fake_stack_ = nullptr;
-    SetTLSFakeStack(nullptr);
+    SetTLSFakeStack(this, nullptr);
     t->Destroy(tid);
   }
 
@@ -144,6 +144,11 @@ class AsanThread {
     GetStartData(&data, sizeof(data));
   }
 
+  bool fake_stack_usage_enabled() const { return fake_stack_usage_enabled_; }
+  void set_fake_stack_usage_enabled(bool enabled) {
+    fake_stack_usage_enabled_ = enabled;
+  }
+
  private:
   // NOTE: There is no AsanThread constructor. It is allocated
   // via mmap() and *must* be valid in zero-initialized state.
@@ -179,6 +184,7 @@ class AsanThread {
   DTLS *dtls_;
 
   FakeStack *fake_stack_;
+  bool fake_stack_usage_enabled_;
   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..c2bf2f52dd4e5 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_disable_fake_stack_usage(void) {}
+void __asan_abi_enable_fake_stack_usage(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..372e44e755cc5 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_disable_fake_stack_usage();
+void *__asan_abi_enable_fake_stack_usage();
+
 // 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..0b4a170015b61 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_disable_fake_stack_usage(void) {
+  return __asan_abi_disable_fake_stack_usage();
+}
+void __asan_enable_fake_stack_usage(void) {
+  return __asan_abi_enable_fake_stack_usage();
+}
 
 // Functions concerning poisoning and unpoisoning fake stack alloca
 void __asan_alloca_poison(uptr addr, uptr size) {
diff --git a/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp b/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
new file mode 100644
index 0000000000000..b1ee8d72e2b58
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
@@ -0,0 +1,27 @@
+// RUN: %clangxx_asan %s -o %t && %run %t
+
+#include "defines.h"
+
+#include <sanitizer/asan_interface.h>
+
+volatile char *saved;
+
+ATTRIBUTE_NOINLINE bool IsOnStack() {
+  volatile char temp = ' ';
+  void* fake_stack = __asan_get_current_fake_stack();
+  void* real = __asan_addr_is_in_fake_stack(fake_stack, const_cast<char*>(&temp), nullptr, nullptr);
+  saved = &temp;
+  return real == nullptr;
+}
+
+int main(int argc, char *argv[]) {
+  __asan_disable_fake_stack_usage();
+  if (!IsOnStack()) {
+    return 1;
+  }
+  __asan_enable_fake_stack_usage();
+  if (IsOnStack()) {
+    return 2;
+  }
+  return 0;
+}

Copy link

github-actions bot commented Sep 22, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@vitalybuka
Copy link
Collaborator

I thought StartSwitchFiber was added for that

I am not sure I understand what is special with MADV_DONTDUMP
Can you add a test which essentially reproduces the issue?
You don't need to remove the existing test.

Copy link
Collaborator

@vitalybuka vitalybuka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comments above

@happyCoder92
Copy link
Contributor Author

Addressed the comments.
We switch to a MADV_DONTDUMP stack and don't want any of the local variable contents to end up in a core dump.
As fake stack is not allocated with the same flags, its usage will result in those variables ending up in the core dump.

@@ -311,7 +329,9 @@ extern "C" {
// -asan-use-after-return=never, after modal UAR flag lands
// (https://github.com/google/sanitizers/issues/1394)
SANITIZER_INTERFACE_ATTRIBUTE
void *__asan_get_current_fake_stack() { return GetFakeStackFast(); }
void* __asan_get_current_fake_stack() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is TODO for to remove this one
if this is just for test, can we instead rely on __asan_get_current_fake_stack
Use __builtin_frame_address

__builtin_frame_address always return real stack address

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the argument and adjusted the test.

Copy link
Collaborator

@vitalybuka vitalybuka Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you forget to upload the patch?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have rebased and updated it

@@ -217,26 +217,44 @@ static THREADLOCAL FakeStack *fake_stack_tls;
FakeStack *GetTLSFakeStack() {
return fake_stack_tls;
}
void SetTLSFakeStack(FakeStack *fs) {
void SetTLSFakeStack(AsanThread* t, FakeStack* fs) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will #163481 help to simplify this patch?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slightly. By only calling SetTLSFakeStack from GetFakeStack #163481 also indirectly ensures the tls var won't be set if fake stack is disabled. I added the thread arg and put the logic into SetTLSFakeStack to make it more explicit.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PTAL

Intended use-case is for threads that use (or switch to) stack
with special properties e.g. backed by MADV_DONTDUMP memory.
vitalybuka added a commit that referenced this pull request Oct 16, 2025
vitalybuka added a commit that referenced this pull request Oct 16, 2025
…163674)

To simplify implementation of
#160135

To keep the logic of figuring out what should be in TLS to one place.
The rest of the code should just reset it and rely on
GetFakeStackFast()/GetFakeStackFastAlways().
happyCoder92 and others added 6 commits October 15, 2025 18:58
@vitalybuka
Copy link
Collaborator

vitalybuka commented Oct 16, 2025

@happyCoder92 can you please elaborate description message?

llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Oct 16, 2025
…SFakeStack (#163674)

To simplify implementation of
llvm/llvm-project#160135

To keep the logic of figuring out what should be in TLS to one place.
The rest of the code should just reset it and rely on
GetFakeStackFast()/GetFakeStackFastAlways().
Copy link
Contributor

@thurstond thurstond left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the intended interaction of this API with -mllvm -asan-use-after-return={never,runtime,always} and ASAN_OPTIONS=detect_stack_use_after_return={0,1}? Please update the description and TestCases/disable_fake_stack.cpp to clarify.

Also, if the behavior of __asan_disable_fake_stack()/__asan_enable_fake_stack() depends on those flags, the naming could be confusing.

@happyCoder92
Copy link
Contributor Author

The behavior does depend on the flags. Would __asan_suppress_fake_stack and __asan_unsuppress_fake_stack be less confusing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants