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..f8dd49e64c760 --- /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; +} \ No newline at end of file diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp index 860f8f78e709c..1a4d98b17ba72 100644 --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -848,6 +848,7 @@ struct AddressSanitizer { bool maybeInsertAsanInitAtFunctionEntry(Function &F); bool maybeInsertDynamicShadowAtFunctionEntry(Function &F); void markEscapedLocalAllocas(Function &F); + void markCatchParametersAsUninteresting(Function &F); private: friend struct FunctionStackPoisoner; @@ -3001,6 +3002,24 @@ 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 = @@ -3045,6 +3064,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..52b14908ab4f2 --- /dev/null +++ b/llvm/test/Instrumentation/AddressSanitizer/asan-win-dont-instrument-catchpad.ll @@ -0,0 +1,90 @@ +; 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. + +; RUN: opt < %s -passes=asan -S | FileCheck %s +; CHECK: %ex = alloca i32, align 4 +; CHECK: catchpad within %{{.*}} [ptr @"??_R0H@8", i32 0, ptr %ex] + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { ptr, ptr, [3 x i8] } +%eh.ThrowInfo = type { i32, i32, i32, i32 } + +@"??_R0H@8" = external global %rtti.TypeDescriptor2 +@_TI1H = external unnamed_addr constant %eh.ThrowInfo, section ".xdata" +@"??_C@_03PMGGPEJJ@?$CFd?6?$AA@" = external dso_local unnamed_addr constant [4 x i8], align 1 + +; Function Attrs: mustprogress noinline norecurse optnone uwtable sanitize_address +define dso_local noundef i32 @main() #0 personality ptr @__CxxFrameHandler3 { +entry: + %retval = alloca i32, align 4 + %tmp = alloca i32, align 4 + %ex = alloca i32, align 4 + store i32 0, ptr %retval, align 4 + store i32 1, ptr %tmp, align 4 + invoke void @_CxxThrowException(ptr %tmp, ptr @_TI1H) #2 + 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] + %2 = load i32, ptr %ex, align 4 + %call = call i32 (ptr, ...) @printf(ptr noundef @"??_C@_03PMGGPEJJ@?$CFd?6?$AA@", i32 noundef %2) [ "funclet"(token %1) ] + store i32 -1, ptr %retval, align 4 + catchret from %1 to label %catchret.dest + +catchret.dest: ; preds = %catch + br label %return + +try.cont: ; No predecessors! + store i32 0, ptr %retval, align 4 + br label %return + +return: ; preds = %try.cont, %catchret.dest + %3 = load i32, ptr %retval, align 4 + ret i32 %3 + +unreachable: ; preds = %entry + unreachable +} + +declare dso_local void @_CxxThrowException(ptr, ptr) + +declare dso_local i32 @__CxxFrameHandler3(...) + +; Function Attrs: mustprogress noinline optnone uwtable +declare dso_local i32 @printf(ptr noundef, ...) #1 + +attributes #0 = { mustprogress noinline norecurse optnone uwtable sanitize_address } +attributes #1 = { mustprogress noinline optnone uwtable } +attributes #2 = { noreturn } \ No newline at end of file