Skip to content

Adjust 'suppress_fake_stack.cpp' and 'suppress_fake_stack_force_disabled.cpp' to work with MSVC's ASan#172707

Draft
davidmrdavid wants to merge 1 commit intollvm:mainfrom
davidmrdavid:main
Draft

Adjust 'suppress_fake_stack.cpp' and 'suppress_fake_stack_force_disabled.cpp' to work with MSVC's ASan#172707
davidmrdavid wants to merge 1 commit intollvm:mainfrom
davidmrdavid:main

Conversation

@davidmrdavid
Copy link
Contributor

Follow-up to: #160135

Context: The aforementioned PR introduced the ability to suppress ASan's 'fake stack' on demand, and tests this via 2 new tests: suppress_fake_stack.cpp and suppress_fake_stack_force_disabled.cpp. These tests fail to run in MSVC's ASan (a fork of LLVM's ASan) for 2 reasons:

(1) The tests invoke __builtin_frame_address, a built-in that does not exist in MSVC.
(2) MSVC has slightly different 'stack-use-after-return' enablement flags, as per this documentation. The short story is that MSVC's 'stack-use-after-return' behavior is a more constrained.

This PR addresses these issues by:

(A) Using _AddressOfReturnAddress instead of __builtin_frame_address when MSVC is detected (i.e. _MSC_VER is defined)
(B) Using conditionals to split the // RUN: commands into MSVC specific commands, and everything else.

@llvmbot
Copy link
Member

llvmbot commented Dec 17, 2025

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

Author: David Justo (davidmrdavid)

Changes

Follow-up to: #160135

Context: The aforementioned PR introduced the ability to suppress ASan's 'fake stack' on demand, and tests this via 2 new tests: suppress_fake_stack.cpp and suppress_fake_stack_force_disabled.cpp. These tests fail to run in MSVC's ASan (a fork of LLVM's ASan) for 2 reasons:

(1) The tests invoke __builtin_frame_address, a built-in that does not exist in MSVC.
(2) MSVC has slightly different 'stack-use-after-return' enablement flags, as per this documentation. The short story is that MSVC's 'stack-use-after-return' behavior is a more constrained.

This PR addresses these issues by:

(A) Using _AddressOfReturnAddress instead of __builtin_frame_address when MSVC is detected (i.e. _MSC_VER is defined)
(B) Using conditionals to split the // RUN: commands into MSVC specific commands, and everything else.


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

2 Files Affected:

  • (modified) compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp (+22-8)
  • (modified) compiler-rt/test/asan/TestCases/suppress_fake_stack_force_disabled.cpp (+18-8)
diff --git a/compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp b/compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp
index f072c6ad3b034..bd35df99ab705 100644
--- a/compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp
+++ b/compiler-rt/test/asan/TestCases/suppress_fake_stack.cpp
@@ -1,6 +1,14 @@
-// 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
+// This file tests the suppression of fake stack frames in ASan.
+
+// We need to separate the MSVC and non-MSVC tests because MSVC because the MSVC enablement of 'stack-use-after-return' detection is different.
+
+// MSVC tests
+// RUN: %if target={{.*-windows-msvc.*}} %{ %clangxx_asan -fsanitize-address-use-after-return %s -o %t && env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t %}
+
+// Non-MSVC tests
+// RUN: %if !target={{.*-windows-msvc.*}} %{ %clangxx_asan %s -o %t && env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t %}
+// RUN: %if !target={{.*-windows-msvc.*}} %{ %clangxx_asan %s -mllvm -asan-use-after-return=runtime -o %t && env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t %}
+// RUN: %if !target={{.*-windows-msvc.*}} %{ %clangxx_asan %s -mllvm -asan-use-after-return=always -o %t && %run %t %}
 
 #include "defines.h"
 
@@ -9,10 +17,18 @@
 
 volatile uintptr_t saved;
 
+// Use FRAME_ADDRESS macro to get the current frame address
+#ifdef _MSC_VER
+// MSVC does not have a 'builtin_frame_address' equivalent.
+// However, for the purposes of this test, its `_AddressOfReturnAddress` built-in suffices
+#  define FRAME_ADDRESS reinterpret_cast<uintptr_t>(_AddressOfReturnAddress())
+#else
+#  define FRAME_ADDRESS reinterpret_cast<uintptr_t>(__builtin_frame_address(0))
+#endif
+
 ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame,
                                       uintptr_t var_addr) {
-  uintptr_t this_frame =
-      reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
+  uintptr_t this_frame = FRAME_ADDRESS;
   return this_frame <= var_addr && var_addr <= parent_frame;
 }
 
@@ -22,9 +38,7 @@ ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame) {
   return IsOnRealStack(parent_frame, saved);
 }
 
-ATTRIBUTE_NOINLINE bool IsOnRealStack() {
-  return IsOnRealStack(reinterpret_cast<uintptr_t>(__builtin_frame_address(0)));
-}
+ATTRIBUTE_NOINLINE bool IsOnRealStack() { return IsOnRealStack(FRAME_ADDRESS); }
 
 int main(int argc, char *argv[]) {
   assert(!IsOnRealStack());
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
index c549f08a7f0a8..f92a42381e0a3 100644
--- a/compiler-rt/test/asan/TestCases/suppress_fake_stack_force_disabled.cpp
+++ b/compiler-rt/test/asan/TestCases/suppress_fake_stack_force_disabled.cpp
@@ -1,7 +1,11 @@
 // 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
+
+// MSVC tests, where disablement is only at runtime.
+// RUN: %if target={{.*-windows-msvc.*}} %{ %clangxx_asan -fsanitize-address-use-after-return %s -o %t && env ASAN_OPTIONS=detect_stack_use_after_return=0 %run %t %}
+
+// Non-MSVC tests
+// RUN: %if !target={{.*-windows-msvc.*}} %{ %clangxx_asan %s -mllvm -asan-use-after-return=never -o %t && %run %t %}
+// RUN: %if !target={{.*-windows-msvc.*}} %{ %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"
 
@@ -10,10 +14,18 @@
 
 volatile uintptr_t saved;
 
+// Use FRAME_ADDRESS macro to get the current frame address
+#ifdef _MSC_VER
+// MSVC does not have a 'builtin_frame_address' equivalent.
+// However, for the purposes of this test, its `_AddressOfReturnAddress` built-in suffices
+#  define FRAME_ADDRESS reinterpret_cast<uintptr_t>(_AddressOfReturnAddress())
+#else
+#  define FRAME_ADDRESS reinterpret_cast<uintptr_t>(__builtin_frame_address(0))
+#endif
+
 ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame,
                                       uintptr_t var_addr) {
-  uintptr_t this_frame =
-      reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
+  uintptr_t this_frame = FRAME_ADDRESS;
   return this_frame <= var_addr && var_addr <= parent_frame;
 }
 
@@ -23,9 +35,7 @@ ATTRIBUTE_NOINLINE bool IsOnRealStack(uintptr_t parent_frame) {
   return IsOnRealStack(parent_frame, saved);
 }
 
-ATTRIBUTE_NOINLINE bool IsOnRealStack() {
-  return IsOnRealStack(reinterpret_cast<uintptr_t>(__builtin_frame_address(0)));
-}
+ATTRIBUTE_NOINLINE bool IsOnRealStack() { return IsOnRealStack(FRAME_ADDRESS); }
 
 int main(int argc, char *argv[]) {
   assert(IsOnRealStack());

@github-actions
Copy link

🪟 Windows x64 Test Results

  • 1499 tests passed
  • 1205 tests skipped
  • 4 tests failed

Failed Tests

(click on a test name to see its output)

AddressSanitizer-x86_64-windows

AddressSanitizer-x86_64-windows.TestCases/suppress_fake_stack.cpp
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 6
C:/_work/llvm-project/llvm-project/build/./bin/clang.exe  -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only -gcodeview -gcolumn-info   -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta   -fsanitize-address-use-after-return C:\_work\llvm-project\llvm-project\compiler-rt\test\asan\TestCases\suppress_fake_stack.cpp -o C:\_work\llvm-project\llvm-project\build\runtimes\runtimes-bins\compiler-rt\test\asan\X86_64WindowsConfig\TestCases\Output\suppress_fake_stack.cpp.tmp && env ASAN_OPTIONS=detect_stack_use_after_return=1  C:\_work\llvm-project\llvm-project\build\runtimes\runtimes-bins\compiler-rt\test\asan\X86_64WindowsConfig\TestCases\Output\suppress_fake_stack.cpp.tmp
# executed command: C:/_work/llvm-project/llvm-project/build/./bin/clang.exe -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only -gcodeview -gcolumn-info -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta -fsanitize-address-use-after-return 'C:\_work\llvm-project\llvm-project\compiler-rt\test\asan\TestCases\suppress_fake_stack.cpp' -o 'C:\_work\llvm-project\llvm-project\build\runtimes\runtimes-bins\compiler-rt\test\asan\X86_64WindowsConfig\TestCases\Output\suppress_fake_stack.cpp.tmp'
# .---command stderr------------
# | clang: error: unknown argument: '-fsanitize-address-use-after-return'
# `-----------------------------
# error: command failed with exit status: 1

--

AddressSanitizer-x86_64-windows.TestCases/suppress_fake_stack_force_disabled.cpp
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 4
C:/_work/llvm-project/llvm-project/build/./bin/clang.exe  -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only -gcodeview -gcolumn-info   -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta   -fsanitize-address-use-after-return C:\_work\llvm-project\llvm-project\compiler-rt\test\asan\TestCases\suppress_fake_stack_force_disabled.cpp -o C:\_work\llvm-project\llvm-project\build\runtimes\runtimes-bins\compiler-rt\test\asan\X86_64WindowsConfig\TestCases\Output\suppress_fake_stack_force_disabled.cpp.tmp && env ASAN_OPTIONS=detect_stack_use_after_return=0  C:\_work\llvm-project\llvm-project\build\runtimes\runtimes-bins\compiler-rt\test\asan\X86_64WindowsConfig\TestCases\Output\suppress_fake_stack_force_disabled.cpp.tmp
# executed command: C:/_work/llvm-project/llvm-project/build/./bin/clang.exe -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only -gcodeview -gcolumn-info -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta -fsanitize-address-use-after-return 'C:\_work\llvm-project\llvm-project\compiler-rt\test\asan\TestCases\suppress_fake_stack_force_disabled.cpp' -o 'C:\_work\llvm-project\llvm-project\build\runtimes\runtimes-bins\compiler-rt\test\asan\X86_64WindowsConfig\TestCases\Output\suppress_fake_stack_force_disabled.cpp.tmp'
# .---command stderr------------
# | clang: error: unknown argument: '-fsanitize-address-use-after-return'
# `-----------------------------
# error: command failed with exit status: 1

--

AddressSanitizer-x86_64-windows-dynamic

AddressSanitizer-x86_64-windows-dynamic.TestCases/suppress_fake_stack.cpp
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 6
C:/_work/llvm-project/llvm-project/build/./bin/clang.exe  -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only -gcodeview -gcolumn-info   -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta   -shared-libasan -D_MT -D_DLL -Wl,-nodefaultlib:libcmt,-defaultlib:msvcrt,-defaultlib:oldnames -fsanitize-address-use-after-return C:\_work\llvm-project\llvm-project\compiler-rt\test\asan\TestCases\suppress_fake_stack.cpp -o C:\_work\llvm-project\llvm-project\build\runtimes\runtimes-bins\compiler-rt\test\asan\X86_64WindowsDynamicConfig\TestCases\Output\suppress_fake_stack.cpp.tmp && env ASAN_OPTIONS=detect_stack_use_after_return=1  C:\_work\llvm-project\llvm-project\build\runtimes\runtimes-bins\compiler-rt\test\asan\X86_64WindowsDynamicConfig\TestCases\Output\suppress_fake_stack.cpp.tmp
# executed command: C:/_work/llvm-project/llvm-project/build/./bin/clang.exe -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only -gcodeview -gcolumn-info -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta -shared-libasan -D_MT -D_DLL -Wl,-nodefaultlib:libcmt,-defaultlib:msvcrt,-defaultlib:oldnames -fsanitize-address-use-after-return 'C:\_work\llvm-project\llvm-project\compiler-rt\test\asan\TestCases\suppress_fake_stack.cpp' -o 'C:\_work\llvm-project\llvm-project\build\runtimes\runtimes-bins\compiler-rt\test\asan\X86_64WindowsDynamicConfig\TestCases\Output\suppress_fake_stack.cpp.tmp'
# .---command stderr------------
# | clang: error: unknown argument: '-fsanitize-address-use-after-return'
# `-----------------------------
# error: command failed with exit status: 1

--

AddressSanitizer-x86_64-windows-dynamic.TestCases/suppress_fake_stack_force_disabled.cpp
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 4
C:/_work/llvm-project/llvm-project/build/./bin/clang.exe  -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only -gcodeview -gcolumn-info   -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta   -shared-libasan -D_MT -D_DLL -Wl,-nodefaultlib:libcmt,-defaultlib:msvcrt,-defaultlib:oldnames -fsanitize-address-use-after-return C:\_work\llvm-project\llvm-project\compiler-rt\test\asan\TestCases\suppress_fake_stack_force_disabled.cpp -o C:\_work\llvm-project\llvm-project\build\runtimes\runtimes-bins\compiler-rt\test\asan\X86_64WindowsDynamicConfig\TestCases\Output\suppress_fake_stack_force_disabled.cpp.tmp && env ASAN_OPTIONS=detect_stack_use_after_return=0  C:\_work\llvm-project\llvm-project\build\runtimes\runtimes-bins\compiler-rt\test\asan\X86_64WindowsDynamicConfig\TestCases\Output\suppress_fake_stack_force_disabled.cpp.tmp
# executed command: C:/_work/llvm-project/llvm-project/build/./bin/clang.exe -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only -gcodeview -gcolumn-info -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta -shared-libasan -D_MT -D_DLL -Wl,-nodefaultlib:libcmt,-defaultlib:msvcrt,-defaultlib:oldnames -fsanitize-address-use-after-return 'C:\_work\llvm-project\llvm-project\compiler-rt\test\asan\TestCases\suppress_fake_stack_force_disabled.cpp' -o 'C:\_work\llvm-project\llvm-project\build\runtimes\runtimes-bins\compiler-rt\test\asan\X86_64WindowsDynamicConfig\TestCases\Output\suppress_fake_stack_force_disabled.cpp.tmp'
# .---command stderr------------
# | clang: error: unknown argument: '-fsanitize-address-use-after-return'
# `-----------------------------
# error: command failed with exit status: 1

--

If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the infrastructure label.

@ndrewh
Copy link
Contributor

ndrewh commented Dec 18, 2025

I don't feel very strongly about this, but I wonder if you should just mark this test unsupported in your configuration and make a separate test in TestCases/Windows/msvc/. I'm not seeing a ton of tests that split out the CHECK lines by platform like this.

@davidmrdavid
Copy link
Contributor Author

Thanks @ndrewh - yeah I think that's valid, and in our fork that's pretty much what we've done to pull in the latest changes.

That said, I'm interested in paving the way for MSVC to work directly upstream, some I'd love to use this PR to learn how to properly exclude tests from running in MSVC (while still allowing them to run w/ clang) when they're incompatible. The failing tests here reveal that the conditional %if target={{.*-windows-msvc.*}} runs on clang (I guess it's when relying on the VS tools for windows?) so I'd like to iterate here until I find the right target string :-).

I'm going to be away for ~2 weeks due to the holidays, so I'll mark this as draft in the meantime. If you have any suggestions on how to exclude tests from running under the clang driver, I'm all ears. Thanks!

@davidmrdavid davidmrdavid marked this pull request as draft December 19, 2025 20:41
@davidmrdavid
Copy link
Contributor Author

Actually, this PR gives the tools I need to be able to discern between clang and MSVC. I'll make a follow up commit in early 2026 incorporating some tricks from there. Thanks!

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.

3 participants