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

via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 25 07:34:55 PST 2025


https://github.com/happyCoder92 updated https://github.com/llvm/llvm-project/pull/160135

>From 4e728017929bfa33edb2bd0a6c82f8f25468c22c Mon Sep 17 00:00:00 2001
From: Wiktor Garbacz <wiktorg at google.com>
Date: Tue, 16 Sep 2025 13:51:08 +0200
Subject: [PATCH 01/10] Add API to temporalily disable usage of ASAN's fake
 stack

Intended use-case is for threads that use (or switch to) stack
with special properties e.g. backed by MADV_DONTDUMP memory.
---
 .../include/sanitizer/asan_interface.h        |  7 ++++
 compiler-rt/lib/asan/asan_fake_stack.cpp      | 39 +++++++++++++++----
 compiler-rt/lib/asan/asan_fake_stack.h        |  4 +-
 compiler-rt/lib/asan/asan_interface.inc       |  2 +
 compiler-rt/lib/asan/asan_thread.cpp          |  7 ++--
 compiler-rt/lib/asan/asan_thread.h            | 11 +++++-
 compiler-rt/lib/asan_abi/asan_abi.cpp         |  2 +
 compiler-rt/lib/asan_abi/asan_abi.h           |  3 ++
 compiler-rt/lib/asan_abi/asan_abi_shim.cpp    |  2 +
 .../asan/TestCases/disable_fake_stack.cpp     | 28 +++++++++++++
 10 files changed, 93 insertions(+), 12 deletions(-)
 create mode 100644 compiler-rt/test/asan/TestCases/disable_fake_stack.cpp

diff --git a/compiler-rt/include/sanitizer/asan_interface.h b/compiler-rt/include/sanitizer/asan_interface.h
index 37b6d08f4db19..a6e8fcd76348e 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 for the current thread.
+/// Temporarily disables use-after-return detection for current thread.
+void SANITIZER_CDECL __asan_disable_fake_stack(void);
+
+/// (Re)enables fake stack for the current thread.
+void SANITIZER_CDECL __asan_enable_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 c3ed2526f0ed4..dc73c8ee7d4e6 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->IsFakeStackEnabled()) {
+    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 DisableFakeStack() {
   AsanThread *t = GetCurrentThread();
-  if (!t) return nullptr;
+  if (t) {
+    t->SetFakeStackEnabled(false);
+  }
+}
+
+static void EnableFakeStack() {
+  AsanThread* t = GetCurrentThread();
+  if (t) {
+    t->SetFakeStackEnabled(true);
+  }
+}
+
+static FakeStack* GetFakeStack(bool for_allocation = true) {
+  AsanThread* t = GetCurrentThread();
+  if (!t || (for_allocation && !t->IsFakeStackEnabled()))
+    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,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() {
+  return GetFakeStackFast(/*for_allocation=*/false);
+}
 
 SANITIZER_INTERFACE_ATTRIBUTE
 void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
@@ -349,4 +369,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() { return DisableFakeStack(); }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_enable_fake_stack() { return EnableFakeStack(); }
 } // extern "C"
diff --git a/compiler-rt/lib/asan/asan_fake_stack.h b/compiler-rt/lib/asan/asan_fake_stack.h
index 50706e6e5876c..1399ef1380ad5 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..1f128b7720841 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)
+INTERFACE_FUNCTION(__asan_enable_fake_stack)
 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..44088b971dc49 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_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..cb6b3e31b41d6 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,14 @@ class AsanThread {
     GetStartData(&data, sizeof(data));
   }
 
+  bool IsFakeStackEnabled() const { return fake_stack_enabled_; }
+  void SetFakeStackEnabled(bool enabled) {
+    fake_stack_enabled_ = enabled;
+    if (!enabled) {
+      SetTLSFakeStack(this, nullptr);
+    }
+  }
+
  private:
   // NOTE: There is no AsanThread constructor. It is allocated
   // via mmap() and *must* be valid in zero-initialized state.
@@ -179,6 +187,7 @@ class AsanThread {
   DTLS *dtls_;
 
   FakeStack *fake_stack_;
+  bool fake_stack_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..1e0126fcdceea 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(void) {}
+void __asan_abi_enable_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..e8b8cd06926dd 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();
+void *__asan_abi_enable_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..eea415294ca77 100644
--- a/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
+++ b/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
@@ -365,6 +365,8 @@ 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(void) { return __asan_abi_disable_fake_stack(); }
+void __asan_enable_fake_stack(void) { return __asan_abi_enable_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/disable_fake_stack.cpp b/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
new file mode 100644
index 0000000000000..a7e2cb4ca44b8
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
@@ -0,0 +1,28 @@
+// 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();
+  if (!IsOnStack()) {
+    return 1;
+  }
+  __asan_enable_fake_stack();
+  if (IsOnStack()) {
+    return 2;
+  }
+  return 0;
+}

>From c10e56827560a4c2b13f9aae54d8be025373fd38 Mon Sep 17 00:00:00 2001
From: Wiktor Garbacz <wiktorg at google.com>
Date: Tue, 16 Sep 2025 13:51:08 +0200
Subject: [PATCH 02/10] Add API to temporalily disable usage of ASAN's fake
 stack

Intended use-case is for threads that use (or switch to) stack
with special properties e.g. backed by MADV_DONTDUMP memory.
---
 .../include/sanitizer/asan_interface.h        |  7 +++++
 compiler-rt/lib/asan/asan_fake_stack.cpp      | 27 ++++++++++++++++--
 compiler-rt/lib/asan/asan_interface.inc       |  2 ++
 compiler-rt/lib/asan/asan_thread.cpp          |  1 +
 compiler-rt/lib/asan/asan_thread.h            |  7 +++++
 compiler-rt/lib/asan_abi/asan_abi.cpp         |  2 ++
 compiler-rt/lib/asan_abi/asan_abi.h           |  3 ++
 compiler-rt/lib/asan_abi/asan_abi_shim.cpp    |  2 ++
 .../asan/TestCases/disable_fake_stack.cpp     | 28 +++++++++++++++++++
 9 files changed, 76 insertions(+), 3 deletions(-)
 create mode 100644 compiler-rt/test/asan/TestCases/disable_fake_stack.cpp

diff --git a/compiler-rt/include/sanitizer/asan_interface.h b/compiler-rt/include/sanitizer/asan_interface.h
index 37b6d08f4db19..a6e8fcd76348e 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 for the current thread.
+/// Temporarily disables use-after-return detection for current thread.
+void SANITIZER_CDECL __asan_disable_fake_stack(void);
+
+/// (Re)enables fake stack for the current thread.
+void SANITIZER_CDECL __asan_enable_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..ad738b0d8168c 100644
--- a/compiler-rt/lib/asan/asan_fake_stack.cpp
+++ b/compiler-rt/lib/asan/asan_fake_stack.cpp
@@ -217,17 +217,33 @@ void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void* arg) {
 static THREADLOCAL FakeStack* fake_stack_tls;
 
 static FakeStack* GetTLSFakeStack() { return fake_stack_tls; }
-static void SetTLSFakeStack(FakeStack* fs) { fake_stack_tls = fs; }
+static void SetTLSFakeStack(FakeStack* fs) {
+  fake_stack_tls = fs;
+}
 void ResetTLSFakeStack() { fake_stack_tls = nullptr; }
 #else
 static FakeStack* GetTLSFakeStack() { return nullptr; }
-static void SetTLSFakeStack(FakeStack*) {}
+static void SetTLSFakeStack(FakeStack* fs) {}
 void ResetTLSFakeStack() {}
 #endif  // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA
 
+static void DisableFakeStack() {
+  AsanThread* t = GetCurrentThread();
+  if (t) {
+    t->SetFakeStackEnabled(false);
+  }
+}
+
+static void EnableFakeStack() {
+  AsanThread* t = GetCurrentThread();
+  if (t) {
+    t->SetFakeStackEnabled(true);
+  }
+}
+
 static FakeStack* GetFakeStack() {
   AsanThread* t = GetCurrentThread();
-  if (!t)
+  if (!t || !t->IsFakeStackEnabled())
     return nullptr;
   return t->get_or_create_fake_stack();
 }
@@ -362,4 +378,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_disable_fake_stack() { return DisableFakeStack(); }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_enable_fake_stack() { return EnableFakeStack(); }
 }  // extern "C"
diff --git a/compiler-rt/lib/asan/asan_interface.inc b/compiler-rt/lib/asan/asan_interface.inc
index bfc44b4619623..1f128b7720841 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)
+INTERFACE_FUNCTION(__asan_enable_fake_stack)
 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 0ed58bbe2a73a..17836c8c4b404 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_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 19b7f342e1712..1c951b757ba00 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 IsFakeStackEnabled() const { return fake_stack_enabled_; }
+  void SetFakeStackEnabled(bool enabled) {
+    fake_stack_enabled_ = enabled;
+    ResetTLSFakeStack();
+  }
+
  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_;
+  bool fake_stack_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..1e0126fcdceea 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(void) {}
+void __asan_abi_enable_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..e8b8cd06926dd 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();
+void *__asan_abi_enable_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..eea415294ca77 100644
--- a/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
+++ b/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
@@ -365,6 +365,8 @@ 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(void) { return __asan_abi_disable_fake_stack(); }
+void __asan_enable_fake_stack(void) { return __asan_abi_enable_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/disable_fake_stack.cpp b/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
new file mode 100644
index 0000000000000..a7e2cb4ca44b8
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
@@ -0,0 +1,28 @@
+// 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();
+  if (!IsOnStack()) {
+    return 1;
+  }
+  __asan_enable_fake_stack();
+  if (IsOnStack()) {
+    return 2;
+  }
+  return 0;
+}

>From 0769e3d420a364d6ed9661d6a3533333104ba24d Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Wed, 15 Oct 2025 19:02:01 -0700
Subject: [PATCH 03/10] Revert "Add API to temporalily disable usage of ASAN's
 fake stack"

This reverts commit 4e728017929bfa33edb2bd0a6c82f8f25468c22c.
---
 .../include/sanitizer/asan_interface.h        |  7 ----
 compiler-rt/lib/asan/asan_fake_stack.cpp      | 39 ++++---------------
 compiler-rt/lib/asan/asan_fake_stack.h        |  4 +-
 compiler-rt/lib/asan/asan_interface.inc       |  2 -
 compiler-rt/lib/asan/asan_thread.cpp          |  7 ++--
 compiler-rt/lib/asan/asan_thread.h            | 11 +-----
 compiler-rt/lib/asan_abi/asan_abi.cpp         |  2 -
 compiler-rt/lib/asan_abi/asan_abi.h           |  3 --
 compiler-rt/lib/asan_abi/asan_abi_shim.cpp    |  2 -
 .../asan/TestCases/disable_fake_stack.cpp     | 28 -------------
 10 files changed, 12 insertions(+), 93 deletions(-)
 delete mode 100644 compiler-rt/test/asan/TestCases/disable_fake_stack.cpp

diff --git a/compiler-rt/include/sanitizer/asan_interface.h b/compiler-rt/include/sanitizer/asan_interface.h
index a6e8fcd76348e..37b6d08f4db19 100644
--- a/compiler-rt/include/sanitizer/asan_interface.h
+++ b/compiler-rt/include/sanitizer/asan_interface.h
@@ -333,13 +333,6 @@ 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 for the current thread.
-/// Temporarily disables use-after-return detection for current thread.
-void SANITIZER_CDECL __asan_disable_fake_stack(void);
-
-/// (Re)enables fake stack for the current thread.
-void SANITIZER_CDECL __asan_enable_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 dc73c8ee7d4e6..c3ed2526f0ed4 100644
--- a/compiler-rt/lib/asan/asan_fake_stack.cpp
+++ b/compiler-rt/lib/asan/asan_fake_stack.cpp
@@ -217,44 +217,26 @@ static THREADLOCAL FakeStack *fake_stack_tls;
 FakeStack *GetTLSFakeStack() {
   return fake_stack_tls;
 }
-void SetTLSFakeStack(AsanThread* t, FakeStack* fs) {
-  if (fs && !t->IsFakeStackEnabled()) {
-    return;
-  }
+void SetTLSFakeStack(FakeStack *fs) {
   fake_stack_tls = fs;
 }
 #else
 FakeStack *GetTLSFakeStack() { return 0; }
-void SetTLSFakeStack(AsanThread* t, FakeStack* fs) {}
+void SetTLSFakeStack(FakeStack *fs) { }
 #endif  // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA
 
-static void DisableFakeStack() {
+static FakeStack *GetFakeStack() {
   AsanThread *t = GetCurrentThread();
-  if (t) {
-    t->SetFakeStackEnabled(false);
-  }
-}
-
-static void EnableFakeStack() {
-  AsanThread* t = GetCurrentThread();
-  if (t) {
-    t->SetFakeStackEnabled(true);
-  }
-}
-
-static FakeStack* GetFakeStack(bool for_allocation = true) {
-  AsanThread* t = GetCurrentThread();
-  if (!t || (for_allocation && !t->IsFakeStackEnabled()))
-    return nullptr;
+  if (!t) return nullptr;
   return t->get_or_create_fake_stack();
 }
 
-static FakeStack* GetFakeStackFast(bool for_allocation = true) {
+static FakeStack *GetFakeStackFast() {
   if (FakeStack *fs = GetTLSFakeStack())
     return fs;
   if (!__asan_option_detect_stack_use_after_return)
     return nullptr;
-  return GetFakeStack(for_allocation);
+  return GetFakeStack();
 }
 
 static FakeStack *GetFakeStackFastAlways() {
@@ -329,9 +311,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(/*for_allocation=*/false);
-}
+void *__asan_get_current_fake_stack() { return GetFakeStackFast(); }
 
 SANITIZER_INTERFACE_ATTRIBUTE
 void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
@@ -369,9 +349,4 @@ 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() { return DisableFakeStack(); }
-SANITIZER_INTERFACE_ATTRIBUTE
-void __asan_enable_fake_stack() { return EnableFakeStack(); }
 } // extern "C"
diff --git a/compiler-rt/lib/asan/asan_fake_stack.h b/compiler-rt/lib/asan/asan_fake_stack.h
index 1399ef1380ad5..50706e6e5876c 100644
--- a/compiler-rt/lib/asan/asan_fake_stack.h
+++ b/compiler-rt/lib/asan/asan_fake_stack.h
@@ -18,8 +18,6 @@
 
 namespace __asan {
 
-class AsanThread;
-
 // Fake stack frame contains local variables of one function.
 struct FakeFrame {
   uptr magic;  // Modified by the instrumented code.
@@ -198,7 +196,7 @@ class FakeStack {
 };
 
 FakeStack *GetTLSFakeStack();
-void SetTLSFakeStack(AsanThread* t, FakeStack* fs);
+void SetTLSFakeStack(FakeStack *fs);
 
 }  // namespace __asan
 
diff --git a/compiler-rt/lib/asan/asan_interface.inc b/compiler-rt/lib/asan/asan_interface.inc
index 1f128b7720841..bfc44b4619623 100644
--- a/compiler-rt/lib/asan/asan_interface.inc
+++ b/compiler-rt/lib/asan/asan_interface.inc
@@ -15,8 +15,6 @@ 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)
-INTERFACE_FUNCTION(__asan_enable_fake_stack)
 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 44088b971dc49..2627ae1289012 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(this, nullptr);
+  SetTLSFakeStack(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(this, fake_stack_save);
+    SetTLSFakeStack(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(this, fake_stack_);
+    SetTLSFakeStack(fake_stack_);
     return fake_stack_;
   }
   return nullptr;
@@ -251,7 +251,6 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
 void AsanThread::Init(const InitOptions *options) {
   DCHECK_NE(tid(), kInvalidTid);
   next_stack_top_ = next_stack_bottom_ = 0;
-  fake_stack_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 cb6b3e31b41d6..12f0cc7a62dae 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(this, nullptr);
+    SetTLSFakeStack(nullptr);
     t->Destroy(tid);
   }
 
@@ -144,14 +144,6 @@ class AsanThread {
     GetStartData(&data, sizeof(data));
   }
 
-  bool IsFakeStackEnabled() const { return fake_stack_enabled_; }
-  void SetFakeStackEnabled(bool enabled) {
-    fake_stack_enabled_ = enabled;
-    if (!enabled) {
-      SetTLSFakeStack(this, nullptr);
-    }
-  }
-
  private:
   // NOTE: There is no AsanThread constructor. It is allocated
   // via mmap() and *must* be valid in zero-initialized state.
@@ -187,7 +179,6 @@ class AsanThread {
   DTLS *dtls_;
 
   FakeStack *fake_stack_;
-  bool fake_stack_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 1e0126fcdceea..cf8663024eb73 100644
--- a/compiler-rt/lib/asan_abi/asan_abi.cpp
+++ b/compiler-rt/lib/asan_abi/asan_abi.cpp
@@ -73,8 +73,6 @@ 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(void) {}
-void __asan_abi_enable_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 e8b8cd06926dd..8702bcd133919 100644
--- a/compiler-rt/lib/asan_abi/asan_abi.h
+++ b/compiler-rt/lib/asan_abi/asan_abi.h
@@ -76,9 +76,6 @@ 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();
-void *__asan_abi_enable_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 eea415294ca77..2512abc641250 100644
--- a/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
+++ b/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
@@ -365,8 +365,6 @@ 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(void) { return __asan_abi_disable_fake_stack(); }
-void __asan_enable_fake_stack(void) { return __asan_abi_enable_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/disable_fake_stack.cpp b/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
deleted file mode 100644
index a7e2cb4ca44b8..0000000000000
--- a/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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();
-  if (!IsOnStack()) {
-    return 1;
-  }
-  __asan_enable_fake_stack();
-  if (IsOnStack()) {
-    return 2;
-  }
-  return 0;
-}

>From 765703093e7d16e3dd52e2009d614aacb996e557 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Wed, 15 Oct 2025 19:29:42 -0700
Subject: [PATCH 04/10] Update test

---
 .../asan/TestCases/disable_fake_stack.cpp     | 27 ++++++++++---------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp b/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
index a7e2cb4ca44b8..1646658302c2e 100644
--- a/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
+++ b/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
@@ -2,27 +2,30 @@
 
 #include "defines.h"
 
+#include <cassert>
 #include <sanitizer/asan_interface.h>
 
-volatile char *saved;
+volatile uintptr_t saved;
+
+ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t caller_frame) {
+  uintptr_t this_frame = reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
+  return this_frame <= saved && saved <= caller_frame;
+}
 
 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;
+  saved = reinterpret_cast<uintptr_t>(&temp);
+  return IsOnRealStack(reinterpret_cast<uintptr_t>(__builtin_frame_address(0)));
 }
 
 int main(int argc, char *argv[]) {
+  assert(!IsOnStack());
+
   __asan_disable_fake_stack();
-  if (!IsOnStack()) {
-    return 1;
-  }
+  assert(IsOnStack());
+
   __asan_enable_fake_stack();
-  if (IsOnStack()) {
-    return 2;
-  }
+  assert(!IsOnStack());
+
   return 0;
 }

>From f56cd996ec35e6d876a0aed396f4643fbdf2f4c6 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Wed, 15 Oct 2025 19:32:40 -0700
Subject: [PATCH 05/10] format

---
 compiler-rt/lib/asan/asan_fake_stack.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_fake_stack.cpp b/compiler-rt/lib/asan/asan_fake_stack.cpp
index ad738b0d8168c..cbd1ee1f531c4 100644
--- a/compiler-rt/lib/asan/asan_fake_stack.cpp
+++ b/compiler-rt/lib/asan/asan_fake_stack.cpp
@@ -217,13 +217,11 @@ void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void* arg) {
 static THREADLOCAL FakeStack* fake_stack_tls;
 
 static FakeStack* GetTLSFakeStack() { return fake_stack_tls; }
-static void SetTLSFakeStack(FakeStack* fs) {
-  fake_stack_tls = fs;
-}
+static void SetTLSFakeStack(FakeStack* fs) { fake_stack_tls = fs; }
 void ResetTLSFakeStack() { fake_stack_tls = nullptr; }
 #else
 static FakeStack* GetTLSFakeStack() { return nullptr; }
-static void SetTLSFakeStack(FakeStack* fs) {}
+static void SetTLSFakeStack(FakeStack *fs) {}
 void ResetTLSFakeStack() {}
 #endif  // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA
 

>From 710eccb708dec3ea2033fc250243c03d24759c0d Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Wed, 15 Oct 2025 19:33:04 -0700
Subject: [PATCH 06/10] format

---
 compiler-rt/lib/asan/asan_fake_stack.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler-rt/lib/asan/asan_fake_stack.cpp b/compiler-rt/lib/asan/asan_fake_stack.cpp
index cbd1ee1f531c4..f3d5f938778b3 100644
--- a/compiler-rt/lib/asan/asan_fake_stack.cpp
+++ b/compiler-rt/lib/asan/asan_fake_stack.cpp
@@ -221,7 +221,7 @@ static void SetTLSFakeStack(FakeStack* fs) { fake_stack_tls = fs; }
 void ResetTLSFakeStack() { fake_stack_tls = nullptr; }
 #else
 static FakeStack* GetTLSFakeStack() { return nullptr; }
-static void SetTLSFakeStack(FakeStack *fs) {}
+static void SetTLSFakeStack(FakeStack*) {}
 void ResetTLSFakeStack() {}
 #endif  // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA
 

>From 29622a99827879c6a753a02528340ddb391f3c4d Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Wed, 15 Oct 2025 19:35:19 -0700
Subject: [PATCH 07/10] format

---
 compiler-rt/test/asan/TestCases/disable_fake_stack.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp b/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
index 1646658302c2e..c7480d1001095 100644
--- a/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
+++ b/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
@@ -8,7 +8,8 @@
 volatile uintptr_t saved;
 
 ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t caller_frame) {
-  uintptr_t this_frame = reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
+  uintptr_t this_frame =
+      reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
   return this_frame <= saved && saved <= caller_frame;
 }
 

>From 42891461fba449cc8203c05ea18653abbee05cf4 Mon Sep 17 00:00:00 2001
From: Wiktor Garbacz <wiktorg at google.com>
Date: Tue, 25 Nov 2025 15:45:32 +0100
Subject: [PATCH 08/10] Add thorough tests

---
 .../asan/TestCases/disable_fake_stack.cpp     |  2 ++
 .../disable_fake_stack_force_disabled.cpp     | 33 +++++++++++++++++++
 2 files changed, 35 insertions(+)
 create mode 100644 compiler-rt/test/asan/TestCases/disable_fake_stack_force_disabled.cpp

diff --git a/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp b/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
index c7480d1001095..5bfc98314703f 100644
--- a/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
+++ b/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
@@ -1,4 +1,6 @@
 // RUN: %clangxx_asan %s -o %t && %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"
 
diff --git a/compiler-rt/test/asan/TestCases/disable_fake_stack_force_disabled.cpp b/compiler-rt/test/asan/TestCases/disable_fake_stack_force_disabled.cpp
new file mode 100644
index 0000000000000..91f37e59abff4
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/disable_fake_stack_force_disabled.cpp
@@ -0,0 +1,33 @@
+// 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 caller_frame) {
+  uintptr_t this_frame =
+      reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
+  return this_frame <= saved && saved <= caller_frame;
+}
+
+ATTRIBUTE_NOINLINE bool IsOnStack() {
+  volatile char temp = ' ';
+  saved = reinterpret_cast<uintptr_t>(&temp);
+  return IsOnRealStack(reinterpret_cast<uintptr_t>(__builtin_frame_address(0)));
+}
+
+int main(int argc, char *argv[]) {
+  assert(IsOnStack());
+
+  __asan_disable_fake_stack();
+  assert(IsOnStack());
+
+  __asan_enable_fake_stack();
+  assert(IsOnStack());
+
+  return 0;
+}

>From 7296b02aae1022414296abbceea5b1d92620402c Mon Sep 17 00:00:00 2001
From: Wiktor Garbacz <wiktorg at google.com>
Date: Tue, 25 Nov 2025 16:18:10 +0100
Subject: [PATCH 09/10] Rename the functions & use counter

---
 compiler-rt/include/sanitizer/asan_interface.h    |  9 +++++----
 compiler-rt/lib/asan/asan_fake_stack.cpp          | 14 +++++++-------
 compiler-rt/lib/asan/asan_interface.inc           |  4 ++--
 compiler-rt/lib/asan/asan_thread.cpp              | 15 ++++++++++++++-
 compiler-rt/lib/asan/asan_thread.h                | 10 ++++------
 compiler-rt/lib/asan_abi/asan_abi.cpp             |  4 ++--
 compiler-rt/lib/asan_abi/asan_abi.h               |  4 ++--
 compiler-rt/lib/asan_abi/asan_abi_shim.cpp        |  4 ++--
 ...ble_fake_stack.cpp => suppress_fake_stack.cpp} |  4 ++--
 ...cpp => suppress_fake_stack_force_disabled.cpp} |  6 ++++--
 10 files changed, 44 insertions(+), 30 deletions(-)
 rename compiler-rt/test/asan/TestCases/{disable_fake_stack.cpp => suppress_fake_stack.cpp} (93%)
 rename compiler-rt/test/asan/TestCases/{disable_fake_stack_force_disabled.cpp => suppress_fake_stack_force_disabled.cpp} (83%)

diff --git a/compiler-rt/include/sanitizer/asan_interface.h b/compiler-rt/include/sanitizer/asan_interface.h
index a6e8fcd76348e..544f2e4b32687 100644
--- a/compiler-rt/include/sanitizer/asan_interface.h
+++ b/compiler-rt/include/sanitizer/asan_interface.h
@@ -333,12 +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 for the current thread.
+/// Suppresses fake stack for the current thread.
 /// Temporarily disables use-after-return detection for current thread.
-void SANITIZER_CDECL __asan_disable_fake_stack(void);
+void SANITIZER_CDECL __asan_suppress_fake_stack(void);
 
-/// (Re)enables fake stack for the current thread.
-void SANITIZER_CDECL __asan_enable_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"
diff --git a/compiler-rt/lib/asan/asan_fake_stack.cpp b/compiler-rt/lib/asan/asan_fake_stack.cpp
index f3d5f938778b3..af73d31b8a5dc 100644
--- a/compiler-rt/lib/asan/asan_fake_stack.cpp
+++ b/compiler-rt/lib/asan/asan_fake_stack.cpp
@@ -225,23 +225,23 @@ static void SetTLSFakeStack(FakeStack*) {}
 void ResetTLSFakeStack() {}
 #endif  // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA
 
-static void DisableFakeStack() {
+static void SuppressFakeStack() {
   AsanThread* t = GetCurrentThread();
   if (t) {
-    t->SetFakeStackEnabled(false);
+    t->SuppressFakeStack();
   }
 }
 
-static void EnableFakeStack() {
+static void UnsuppressFakeStack() {
   AsanThread* t = GetCurrentThread();
   if (t) {
-    t->SetFakeStackEnabled(true);
+    t->UnsuppressFakeStack();
   }
 }
 
 static FakeStack* GetFakeStack() {
   AsanThread* t = GetCurrentThread();
-  if (!t || !t->IsFakeStackEnabled())
+  if (!t || t->IsFakeStackSuppressed())
     return nullptr;
   return t->get_or_create_fake_stack();
 }
@@ -378,7 +378,7 @@ void __asan_allocas_unpoison(uptr top, uptr bottom) {
 }
 
 SANITIZER_INTERFACE_ATTRIBUTE
-void __asan_disable_fake_stack() { return DisableFakeStack(); }
+void __asan_suppress_fake_stack() { return SuppressFakeStack(); }
 SANITIZER_INTERFACE_ATTRIBUTE
-void __asan_enable_fake_stack() { return EnableFakeStack(); }
+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 1f128b7720841..b465356b1e443 100644
--- a/compiler-rt/lib/asan/asan_interface.inc
+++ b/compiler-rt/lib/asan/asan_interface.inc
@@ -15,8 +15,6 @@ 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)
-INTERFACE_FUNCTION(__asan_enable_fake_stack)
 INTERFACE_FUNCTION(__asan_exp_load1)
 INTERFACE_FUNCTION(__asan_exp_load2)
 INTERFACE_FUNCTION(__asan_exp_load4)
@@ -167,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 17836c8c4b404..32ab723e89001 100644
--- a/compiler-rt/lib/asan/asan_thread.cpp
+++ b/compiler-rt/lib/asan/asan_thread.cpp
@@ -251,7 +251,7 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
 void AsanThread::Init(const InitOptions *options) {
   DCHECK_NE(tid(), kInvalidTid);
   next_stack_top_ = next_stack_bottom_ = 0;
-  fake_stack_enabled_ = true;
+  fake_stack_suppression_counter_ = 0;
   atomic_store(&stack_switching_, false, memory_order_release);
   CHECK_EQ(this->stack_size(), 0U);
   SetThreadStackAndTls(options);
@@ -405,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 1c951b757ba00..4a863bd89055b 100644
--- a/compiler-rt/lib/asan/asan_thread.h
+++ b/compiler-rt/lib/asan/asan_thread.h
@@ -144,11 +144,9 @@ class AsanThread {
     GetStartData(&data, sizeof(data));
   }
 
-  bool IsFakeStackEnabled() const { return fake_stack_enabled_; }
-  void SetFakeStackEnabled(bool enabled) {
-    fake_stack_enabled_ = enabled;
-    ResetTLSFakeStack();
-  }
+  bool IsFakeStackSuppressed() const { return fake_stack_suppression_counter_ > 0; }
+  void SuppressFakeStack();
+  void UnsuppressFakeStack();
 
  private:
   // NOTE: There is no AsanThread constructor. It is allocated
@@ -185,7 +183,7 @@ class AsanThread {
   DTLS *dtls_;
 
   FakeStack *fake_stack_;
-  bool fake_stack_enabled_;
+  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 1e0126fcdceea..6199bbed09dfc 100644
--- a/compiler-rt/lib/asan_abi/asan_abi.cpp
+++ b/compiler-rt/lib/asan_abi/asan_abi.cpp
@@ -73,8 +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(void) {}
-void __asan_abi_enable_fake_stack(void) {}
+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 e8b8cd06926dd..cb59976c8506c 100644
--- a/compiler-rt/lib/asan_abi/asan_abi.h
+++ b/compiler-rt/lib/asan_abi/asan_abi.h
@@ -76,8 +76,8 @@ 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();
-void *__asan_abi_enable_fake_stack();
+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);
diff --git a/compiler-rt/lib/asan_abi/asan_abi_shim.cpp b/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
index eea415294ca77..66d2ada45fe70 100644
--- a/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
+++ b/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
@@ -365,8 +365,8 @@ 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(void) { return __asan_abi_disable_fake_stack(); }
-void __asan_enable_fake_stack(void) { return __asan_abi_enable_fake_stack(); }
+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/disable_fake_stack.cpp b/compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp
similarity index 93%
rename from compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
rename to compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp
index 5bfc98314703f..c9c1401c59cd9 100644
--- a/compiler-rt/test/asan/TestCases/disable_fake_stack.cpp
+++ b/compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp
@@ -24,10 +24,10 @@ ATTRIBUTE_NOINLINE bool IsOnStack() {
 int main(int argc, char *argv[]) {
   assert(!IsOnStack());
 
-  __asan_disable_fake_stack();
+  __asan_suppress_fake_stack();
   assert(IsOnStack());
 
-  __asan_enable_fake_stack();
+  __asan_unsuppress_fake_stack();
   assert(!IsOnStack());
 
   return 0;
diff --git a/compiler-rt/test/asan/TestCases/disable_fake_stack_force_disabled.cpp b/compiler-rt/test/asan/TestCases/suppress_fake_stack_force_disabled.cpp
similarity index 83%
rename from compiler-rt/test/asan/TestCases/disable_fake_stack_force_disabled.cpp
rename to compiler-rt/test/asan/TestCases/suppress_fake_stack_force_disabled.cpp
index 91f37e59abff4..34d31de01c113 100644
--- a/compiler-rt/test/asan/TestCases/disable_fake_stack_force_disabled.cpp
+++ b/compiler-rt/test/asan/TestCases/suppress_fake_stack_force_disabled.cpp
@@ -1,3 +1,5 @@
+// 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
 
@@ -23,10 +25,10 @@ ATTRIBUTE_NOINLINE bool IsOnStack() {
 int main(int argc, char *argv[]) {
   assert(IsOnStack());
 
-  __asan_disable_fake_stack();
+  __asan_suppress_fake_stack();
   assert(IsOnStack());
 
-  __asan_enable_fake_stack();
+  __asan_unsuppress_fake_stack();
   assert(IsOnStack());
 
   return 0;

>From 423730616ade1d80cb75a098dc4b9cff1fb21b38 Mon Sep 17 00:00:00 2001
From: Wiktor Garbacz <wiktorg at google.com>
Date: Tue, 25 Nov 2025 16:33:52 +0100
Subject: [PATCH 10/10] Code formatting

---
 compiler-rt/lib/asan/asan_thread.h         | 4 +++-
 compiler-rt/lib/asan_abi/asan_abi_shim.cpp | 8 ++++++--
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_thread.h b/compiler-rt/lib/asan/asan_thread.h
index 4a863bd89055b..e9ca6b6a59016 100644
--- a/compiler-rt/lib/asan/asan_thread.h
+++ b/compiler-rt/lib/asan/asan_thread.h
@@ -144,7 +144,9 @@ class AsanThread {
     GetStartData(&data, sizeof(data));
   }
 
-  bool IsFakeStackSuppressed() const { return fake_stack_suppression_counter_ > 0; }
+  bool IsFakeStackSuppressed() const {
+    return fake_stack_suppression_counter_ > 0;
+  }
   void SuppressFakeStack();
   void UnsuppressFakeStack();
 
diff --git a/compiler-rt/lib/asan_abi/asan_abi_shim.cpp b/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
index 66d2ada45fe70..7ae7e77aea4a2 100644
--- a/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
+++ b/compiler-rt/lib/asan_abi/asan_abi_shim.cpp
@@ -365,8 +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(); }
+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) {



More information about the llvm-commits mailing list