From 87d03450a5a25a75b2c22862ec438ad4d916bd50 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Mon, 20 Jul 2015 22:49:44 +0000 Subject: [PATCH] Don't try to instrument allocas used by outlined SEH funclets Summary: Arguments to llvm.localescape must be static allocas. They must be at some statically known offset from the frame or stack pointer so that other functions can access them with localrecover. If we ever want to instrument these, we can use more indirection to recover the addresses of these local variables. We can do it during clang irgen or with the asan module pass. Reviewers: eugenis Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D11307 llvm-svn: 242726 --- .../Instrumentation/AddressSanitizer.cpp | 40 ++++++++++++ .../AddressSanitizer/localescape.ll | 73 ++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 llvm/test/Instrumentation/AddressSanitizer/localescape.ll diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp index 52b2290..0c0280a 100644 --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -439,6 +439,7 @@ struct AddressSanitizer : public FunctionPass { Value *memToShadow(Value *Shadow, IRBuilder<> &IRB); bool runOnFunction(Function &F) override; bool maybeInsertAsanInitAtFunctionEntry(Function &F); + void markEscapedLocalAllocas(Function &F); bool doInitialization(Module &M) override; static char ID; // Pass identification, replacement for typeid @@ -548,6 +549,7 @@ struct FunctionStackPoisoner : public InstVisitor { SmallVector DynamicAllocaVec; SmallVector StackRestoreVec; AllocaInst *DynamicAllocaLayout = nullptr; + IntrinsicInst *LocalEscapeCall = nullptr; // Maps Value to an AllocaInst from which the Value is originated. typedef DenseMap AllocaForValueMapTy; @@ -645,6 +647,7 @@ struct FunctionStackPoisoner : public InstVisitor { void visitIntrinsicInst(IntrinsicInst &II) { Intrinsic::ID ID = II.getIntrinsicID(); if (ID == Intrinsic::stackrestore) StackRestoreVec.push_back(&II); + if (ID == Intrinsic::localescape) LocalEscapeCall = &II; if (!ClCheckLifetime) return; if (ID != Intrinsic::lifetime_start && ID != Intrinsic::lifetime_end) return; @@ -1479,6 +1482,34 @@ bool AddressSanitizer::maybeInsertAsanInitAtFunctionEntry(Function &F) { return false; } +void AddressSanitizer::markEscapedLocalAllocas(Function &F) { + // Find the one possible call to llvm.localescape and pre-mark allocas passed + // to it as uninteresting. This assumes we haven't started processing allocas + // yet. This check is done up front because iterating the use list in + // isInterestingAlloca would be algorithmically slower. + assert(ProcessedAllocas.empty() && "must process localescape before allocas"); + + // Try to get the declaration of llvm.localescape. If it's not in the module, + // we can exit early. + if (!F.getParent()->getFunction("llvm.localescape")) return; + + // Look for a call to llvm.localescape call in the entry block. It can't be in + // any other block. + for (Instruction &I : F.getEntryBlock()) { + IntrinsicInst *II = dyn_cast(&I); + if (II && II->getIntrinsicID() == Intrinsic::localescape) { + // We found a call. Mark all the allocas passed in as uninteresting. + for (Value *Arg : II->arg_operands()) { + AllocaInst *AI = dyn_cast(Arg->stripPointerCasts()); + assert(AI && AI->isStaticAlloca() && + "non-static alloca arg to localescape"); + ProcessedAllocas[AI] = false; + } + break; + } + } +} + bool AddressSanitizer::runOnFunction(Function &F) { if (&F == AsanCtorFunction) return false; if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) return false; @@ -1494,6 +1525,10 @@ bool AddressSanitizer::runOnFunction(Function &F) { if (!ClDebugFunc.empty() && ClDebugFunc != F.getName()) return false; + // We can't instrument allocas used with llvm.localescape. Only static allocas + // can be passed to that intrinsic. + markEscapedLocalAllocas(F); + // We want to instrument every address only once per basic block (unless there // are calls between uses). SmallSet TempsToInstrument; @@ -1584,6 +1619,8 @@ bool AddressSanitizer::runOnFunction(Function &F) { DEBUG(dbgs() << "ASAN done instrumenting: " << res << " " << F << "\n"); + ProcessedAllocas.clear(); + return res; } @@ -1745,6 +1782,9 @@ void FunctionStackPoisoner::poisonStack() { // treated as regular stack slots. for (auto *AI : NonInstrumentedStaticAllocaVec) AI->moveBefore(InsBefore); + // If we have a call to llvm.localescape, keep it in the entry block. + if (LocalEscapeCall) LocalEscapeCall->moveBefore(InsBefore); + SmallVector SVD; SVD.reserve(AllocaVec.size()); for (AllocaInst *AI : AllocaVec) { diff --git a/llvm/test/Instrumentation/AddressSanitizer/localescape.ll b/llvm/test/Instrumentation/AddressSanitizer/localescape.ll new file mode 100644 index 0000000..bb89ce3 --- /dev/null +++ b/llvm/test/Instrumentation/AddressSanitizer/localescape.ll @@ -0,0 +1,73 @@ +; RUN: opt < %s -asan -asan-module -asan-use-after-return -asan-stack-dynamic-alloca -S | FileCheck %s +; RUN: opt < %s -asan -asan-module -asan-use-after-return=0 -asan-stack-dynamic-alloca=0 -S | FileCheck %s + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.10.0" + +declare i32 @llvm.eh.typeid.for(i8*) #2 +declare i8* @llvm.frameaddress(i32) +declare i8* @llvm.x86.seh.recoverfp(i8*, i8*) +declare i8* @llvm.localrecover(i8*, i8*, i32) +declare void @llvm.localescape(...) #1 + +declare i32 @_except_handler3(...) +declare void @may_throw(i32* %r) + +define i32 @main() sanitize_address personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) { +entry: + %r = alloca i32, align 4 + %__exception_code = alloca i32, align 4 + call void (...) @llvm.localescape(i32* nonnull %__exception_code) + %0 = bitcast i32* %r to i8* + store i32 0, i32* %r, align 4 + invoke void @may_throw(i32* nonnull %r) #4 + to label %__try.cont unwind label %lpad + +lpad: ; preds = %entry + %1 = landingpad { i8*, i32 } + catch i8* bitcast (i32 ()* @"\01?filt$0@0@main@@" to i8*) + %2 = extractvalue { i8*, i32 } %1, 1 + %3 = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @"\01?filt$0@0@main@@" to i8*)) #1 + %matches = icmp eq i32 %2, %3 + br i1 %matches, label %__except, label %eh.resume + +__except: ; preds = %lpad + store i32 1, i32* %r, align 4 + br label %__try.cont + +__try.cont: ; preds = %entry, %__except + %4 = load i32, i32* %r, align 4 + ret i32 %4 + +eh.resume: ; preds = %lpad + resume { i8*, i32 } %1 +} + +; Check that the alloca remains static and the localescape call remains in the +; entry block. + +; CHECK-LABEL: define i32 @main() +; CHECK-NOT: br {{.*}}label +; CHECK: %__exception_code = alloca i32, align 4 +; CHECK-NOT: br {{.*}}label +; CHECK: call void (...) @llvm.localescape(i32* nonnull %__exception_code) + +; Function Attrs: nounwind +define internal i32 @"\01?filt$0@0@main@@"() #1 { +entry: + %0 = tail call i8* @llvm.frameaddress(i32 1) + %1 = tail call i8* @llvm.x86.seh.recoverfp(i8* bitcast (i32 ()* @main to i8*), i8* %0) + %2 = tail call i8* @llvm.localrecover(i8* bitcast (i32 ()* @main to i8*), i8* %1, i32 0) + %__exception_code = bitcast i8* %2 to i32* + %3 = getelementptr inbounds i8, i8* %0, i32 -20 + %4 = bitcast i8* %3 to { i32*, i8* }** + %5 = load { i32*, i8* }*, { i32*, i8* }** %4, align 4 + %6 = getelementptr inbounds { i32*, i8* }, { i32*, i8* }* %5, i32 0, i32 0 + %7 = load i32*, i32** %6, align 4 + %8 = load i32, i32* %7, align 4 + store i32 %8, i32* %__exception_code, align 4 + ret i32 1 +} + +; CHECK-LABEL: define internal i32 @"\01?filt$0@0@main@@"() +; CHECK: tail call i8* @llvm.localrecover(i8* bitcast (i32 ()* @main to i8*), i8* {{.*}}, i32 0) -- 2.7.4