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..6f028147c9049 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/basic_exception_handling.cpp @@ -0,0 +1,33 @@ +// 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 +#include + +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; +} diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp index 6e178015f1f55..2646334b55654 100644 --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -844,6 +844,7 @@ struct AddressSanitizer { bool maybeInsertAsanInitAtFunctionEntry(Function &F); bool maybeInsertDynamicShadowAtFunctionEntry(Function &F); void markEscapedLocalAllocas(Function &F); + void markCatchParametersAsUninteresting(Function &F); private: friend struct FunctionStackPoisoner; @@ -2997,6 +2998,22 @@ void AddressSanitizer::markEscapedLocalAllocas(Function &F) { } } } +// Mitigation for https://github.com/google/sanitizers/issues/749 +// We don't instrument Windows catch-block parameters to avoid +// interfering with exception handling assumptions. +void AddressSanitizer::markCatchParametersAsUninteresting(Function &F) { + for (BasicBlock &BB : F) { + for (Instruction &I : BB) { + if (auto *CatchPad = dyn_cast(&I)) { + // Mark the parameters to a catch-block as uninteresting to avoid + // instrumenting them. + for (Value *Operand : CatchPad->arg_operands()) + if (auto *AI = dyn_cast(Operand)) + ProcessedAllocas[AI] = false; + } + } + } +} bool AddressSanitizer::suppressInstrumentationSiteForDebug(int &Instrumented) { bool ShouldInstrument = @@ -3041,6 +3058,9 @@ bool AddressSanitizer::instrumentFunction(Function &F, // can be passed to that intrinsic. markEscapedLocalAllocas(F); + if (TargetTriple.isOSWindows()) + markCatchParametersAsUninteresting(F); + // We want to instrument every address only once per basic block (unless there // are calls between uses). SmallPtrSet TempsToInstrument; diff --git a/llvm/test/Instrumentation/AddressSanitizer/asan-win-dont-instrument-catchpad.ll b/llvm/test/Instrumentation/AddressSanitizer/asan-win-dont-instrument-catchpad.ll new file mode 100644 index 0000000000000..e38da0b5e2fd3 --- /dev/null +++ b/llvm/test/Instrumentation/AddressSanitizer/asan-win-dont-instrument-catchpad.ll @@ -0,0 +1,63 @@ +; RUN: opt < %s -passes=asan -S | FileCheck %s +; CHECK: %ex = alloca i32, align 4 +; CHECK: catchpad within %{{.*}} [ptr @"??_R0H@8", i32 0, ptr %ex] + +; This test ensures that catch parameters are not instrumented on Windows. + +; This file was generated using the following source +; +; ```C++ +; #include +; #include +; +; int main() { +; try { +; throw 1; +; } catch (const int ex) { +; printf("%d\n", ex); +; return -1; +; } +; return 0; +; } +; +; ``` +; then running the following sequence of commands +; +; ``` +; clang.exe -g0 -O0 -emit-llvm -c main.cpp -o main.bc +; llvm-extract.exe -func=main main.bc -o main_func.bc +; llvm-dis.exe main_func.bc -o main_func_dis.ll +; ``` +; and finally manually trimming the resulting `.ll` file to remove +; unnecessary metadata, and manually adding the `sanitize_address` annotation; +; needed for the ASan pass to run. + +target triple = "x86_64-pc-windows-msvc" + +@"??_R0H@8" = external global ptr + +; Function Attrs: sanitize_address +define i32 @main() sanitize_address personality ptr @__CxxFrameHandler3 { +entry: + %ex = alloca i32, align 4 + invoke void @throw() + to label %unreachable unwind label %catch.dispatch + +catch.dispatch: ; preds = %entry + %0 = catchswitch within none [label %catch] unwind to caller + +catch: ; preds = %catch.dispatch + %1 = catchpad within %0 [ptr @"??_R0H@8", i32 0, ptr %ex] + call void @opaque() [ "funclet"(token %1) ] + catchret from %1 to label %return + +return: ; preds = %catch + ret i32 0 + +unreachable: ; preds = %entry + unreachable +} + +declare void @throw() noreturn +declare void @opaque() +declare i32 @__CxxFrameHandler3(...)