[compiler-rt] [llvm] [ASan] Do not instrument catch block parameters on Windows (PR #159618)

via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 18 11:00:57 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: David Justo (davidmrdavid)

<details>
<summary>Changes</summary>

**Mitigation for:** https://github.com/google/sanitizers/issues/749

**Disclosure:** I'm not an ASan compiler expert yet (I'm trying to learn!), I primarily work in the runtime. Some of this PR was developed with the help of AI tools (primarily as a "fuzzy `grep` engine"), but I've manually refined and tested the output, can speak for every line. In general, I used it only to orient myself and for "rubberducking". 

All text in the PR and in this description is written by me.

**Context:**

The msvc ASan team (👋 ) has received an internal request to improve clang's exception handling under ASan for Windows. Namely, we're interested in **mitigating** this bug: https://github.com/google/sanitizers/issues/749

To summarize, today, clang + ASan produces a false-positive error for this program:

```C++
#include <cstdio>
#include <exception>
int main()
{
	try	{
		throw std::exception("test");
	}catch (const std::exception& ex){
		puts(ex.what());
	}
	return 0;
}
```

The error reads as such:


```
C:\Users\dajusto\source\repros\upstream>type main.cpp
#include <cstdio>
#include <exception>
int main()
{
        try     {
                throw std::exception("test");
        }catch (const std::exception& ex){
                puts(ex.what());
        }
        return 0;
}
C:\Users\dajusto\source\repros\upstream>"C:\Users\dajusto\source\repos\llvm-project\build.runtimes\bin\clang.exe" -fsanitize=address -g -O0 main.cpp

C:\Users\dajusto\source\repros\upstream>a.exe
=================================================================
==19112==ERROR: AddressSanitizer: access-violation on unknown address 0x000000000000 (pc 0x7ff72c7c11d9 bp 0x0080000ff960 sp 0x0080000fcf50 T0)
==19112==The signal is caused by a READ memory access.
==19112==Hint: address points to the zero page.
    #<!-- -->0 0x7ff72c7c11d8 in main C:\Users\dajusto\source\repros\upstream\main.cpp:8
    #<!-- -->1 0x7ff72c7d479f in _CallSettingFrame C:\repos\msvc\src\vctools\crt\vcruntime\src\eh\amd64\handlers.asm:49
    #<!-- -->2 0x7ff72c7c8944 in __FrameHandler3::CxxCallCatchBlock(struct _EXCEPTION_RECORD *) C:\repos\msvc\src\vctools\crt\vcruntime\src\eh\frame.cpp:1567
    #<!-- -->3 0x7ffb4a90e3e5  (C:\WINDOWS\SYSTEM32\ntdll.dll+0x18012e3e5)
    #<!-- -->4 0x7ff72c7c1128 in main C:\Users\dajusto\source\repros\upstream\main.cpp:6
    #<!-- -->5 0x7ff72c7c33db in invoke_main C:\repos\msvc\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
    #<!-- -->6 0x7ff72c7c33db in __scrt_common_main_seh C:\repos\msvc\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    #<!-- -->7 0x7ffb49b05c06  (C:\WINDOWS\System32\KERNEL32.DLL+0x180035c06)
    #<!-- -->8 0x7ffb4a8455ef  (C:\WINDOWS\SYSTEM32\ntdll.dll+0x1800655ef)

==19112==Register values:
rax = 0  rbx = 80000ff8e0  rcx = 27d76d00000  rdx = 80000ff8e0
rdi = 80000fdd50  rsi = 80000ff6a0  rbp = 80000ff960  rsp = 80000fcf50
r8  = 100  r9  = 19930520  r10 = 8000503a90  r11 = 80000fd540
r12 = 80000fd020  r13 = 0  r14 = 80000fdeb8  r15 = 0
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: access-violation C:\Users\dajusto\source\repros\upstream\main.cpp:8 in main
==19112==ABORTING
```

The root of the issue _appears to be_ that ASan's instrumentation is incompatible with Window's assumptions for instantiating `catch`-block's parameters (`ex` in the snippet above).

The nitty gritty details are lost on me, but I understand that to make this work without loss of ASan coverage, a "serious" refactoring is needed. In the meantime, users risk false positive errors when pairing ASan + catch-block parameters on Windows.

**To mitigate this** I think we should avoid instrumenting catch-block parameters on Windows. It appears to me this is as "simple" as marking catch block parameters as "uninteresting" in `AddressSanitizer::isInterestingAlloca`. My manual tests seem to confirm this.

I believe this is strictly better than today's status quo, where the runtime generates false positives. Although we're now explicitly choosing to instrument less, the benefit is that now more programs can run with ASan without _funky_ macros that disable ASan on exception blocks.

**This PR:** implements the mitigation above, and creates a simple new test for it.

_Thanks!_

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


2 Files Affected:

- (added) compiler-rt/test/asan/TestCases/Windows/basic_exception_handling.cpp (+36) 
- (modified) llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp (+15-1) 


``````````diff
diff --git a/compiler-rt/test/asan/TestCases/Windows/basic_exception_handling.cpp b/compiler-rt/test/asan/TestCases/Windows/basic_exception_handling.cpp
new file mode 100644
index 0000000000000..94ca4b9bf2df0
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/basic_exception_handling.cpp
@@ -0,0 +1,36 @@
+// RUN: %clangxx_asan %s -o %t
+// RUN: %run %t | FileCheck %s
+
+// This test tests that declaring a parameter in a catch-block does not produce a false positive
+// ASan error on Windows.
+
+// This code is based on the repro in https://github.com/google/sanitizers/issues/749
+#include <cstdio>
+#include <exception>
+
+void throwInFunction(){
+    throw std::exception("test2");
+}
+
+int main()
+{
+    // case 1: direct throw
+	try	{
+		throw std::exception("test1");
+	} catch (const std::exception& ex){
+		puts(ex.what());
+        // CHECK: test1
+	}
+
+    // case 2: throw in function
+    try {
+        throwInFunction();
+    } catch (const std::exception& ex){
+        puts(ex.what());
+        // CHECK: test2
+    }
+
+    printf("Success!\n");
+    // CHECK: Success!
+	return 0;
+}
\ No newline at end of file
diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
index 42c3d4a4f4c46..986d3c2861af0 100644
--- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
@@ -1397,6 +1397,16 @@ void AddressSanitizer::instrumentMemIntrinsic(MemIntrinsic *MI,
   MI->eraseFromParent();
 }
 
+// Check if an alloca is a catch block parameter
+static bool isCatchParameter(const AllocaInst &AI) {
+  for (const Use &U : AI.uses()) {
+    if (isa<CatchPadInst>(U.getUser())) {
+      return true;
+    }
+  }
+  return false;
+}
+
 /// Check if we want (and can) handle this alloca.
 bool AddressSanitizer::isInterestingAlloca(const AllocaInst &AI) {
   auto [It, Inserted] = ProcessedAllocas.try_emplace(&AI);
@@ -1417,7 +1427,11 @@ bool AddressSanitizer::isInterestingAlloca(const AllocaInst &AI) {
        // swifterror allocas are register promoted by ISel
        !AI.isSwiftError() &&
        // safe allocas are not interesting
-       !(SSGI && SSGI->isSafe(AI)));
+       !(SSGI && SSGI->isSafe(AI)) &&
+       // Mitigation for https://github.com/google/sanitizers/issues/749
+       // We don't instrument Windows catch-block parameters to avoid
+       // interfering with exception handling assumptions.
+       !(TargetTriple.isOSWindows() && isCatchParameter(AI)));
 
   It->second = IsInteresting;
   return IsInteresting;

``````````

</details>


https://github.com/llvm/llvm-project/pull/159618


More information about the llvm-commits mailing list