Index: lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -13,6 +13,9 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Analysis/MemoryBuiltins.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/Transforms/Instrumentation.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" @@ -181,6 +184,9 @@ cl::init(true)); static cl::opt ClOptGlobals("asan-opt-globals", cl::desc("Don't instrument scalar globals"), cl::Hidden, cl::init(true)); +static cl::opt ClOptStack("asan-opt-stack", + cl::desc("Don't instrument scalar stack variables"), cl::Hidden, + cl::init(true)); static cl::opt ClCheckLifetime("asan-check-lifetime", cl::desc("Use llvm.lifetime intrinsics to insert extra checks"), @@ -207,10 +213,10 @@ STATISTIC(NumInstrumentedWrites, "Number of instrumented writes"); STATISTIC(NumInstrumentedDynamicAllocas, "Number of instrumented dynamic allocas"); -STATISTIC(NumOptimizedAccessesToGlobalArray, - "Number of optimized accesses to global arrays"); STATISTIC(NumOptimizedAccessesToGlobalVar, "Number of optimized accesses to global vars"); +STATISTIC(NumOptimizedAccessesToStackVar, + "Number of optimized accesses to stack vars"); namespace { /// Frontend-provided metadata for source location. @@ -371,8 +377,11 @@ } void getAnalysisUsage(AnalysisUsage &AU) const override { AU.addRequired(); + AU.addRequired(); + AU.addRequired(); } - void instrumentMop(Instruction *I, bool UseCalls); + void instrumentMop(ObjectSizeOffsetEvaluator &ObjSizeEval, Instruction *I, + bool UseCalls); void instrumentPointerComparisonOrSubtraction(Instruction *I); void instrumentAddress(Instruction *OrigIns, Instruction *InsertBefore, Value *Addr, uint32_t TypeSize, bool IsWrite, @@ -396,6 +405,7 @@ bool LooksLikeCodeInBug11395(Instruction *I); bool GlobalIsLinkerInitialized(GlobalVariable *G); + bool isInboundsAccess(ObjectSizeOffsetEvaluator &ObjSizeEval, Value *Addr); LLVMContext *C; const DataLayout *DL; @@ -851,28 +861,31 @@ IRB.CreateCall2(F, Param[0], Param[1]); } -void AddressSanitizer::instrumentMop(Instruction *I, bool UseCalls) { +void AddressSanitizer::instrumentMop(ObjectSizeOffsetEvaluator &ObjSizeEval, + Instruction *I, bool UseCalls) { bool IsWrite = false; unsigned Alignment = 0; Value *Addr = isInterestingMemoryAccess(I, &IsWrite, &Alignment); assert(Addr); + if (ClOpt && ClOptGlobals) { - if (GlobalVariable *G = dyn_cast(Addr)) { - // If initialization order checking is disabled, a simple access to a - // dynamically initialized global is always valid. - if (!ClInitializers || GlobalIsLinkerInitialized(G)) { - NumOptimizedAccessesToGlobalVar++; - return; - } + // If initialization order checking is disabled, a simple access to a + // dynamically initialized global is always valid. + GlobalVariable *G = dyn_cast(GetUnderlyingObject(Addr, + nullptr)); + if (G != NULL && (!ClInitializers || GlobalIsLinkerInitialized(G)) && + isInboundsAccess(ObjSizeEval, Addr)) { + NumOptimizedAccessesToGlobalVar++; + return; } - ConstantExpr *CE = dyn_cast(Addr); - if (CE && CE->isGEPWithNoNotionalOverIndexing()) { - if (GlobalVariable *G = dyn_cast(CE->getOperand(0))) { - if (CE->getOperand(1)->isNullValue() && GlobalIsLinkerInitialized(G)) { - NumOptimizedAccessesToGlobalArray++; - return; - } - } + } + + if (ClOpt && ClOptStack) { + // A direct inbounds access to a stack variable is always valid. + if (isa(GetUnderlyingObject(Addr, nullptr)) && + isInboundsAccess(ObjSizeEval, Addr)) { + NumOptimizedAccessesToStackVar++; + return; } } @@ -1484,13 +1497,17 @@ ToInstrument.size() > (unsigned)ClInstrumentationWithCallsThreshold) UseCalls = true; + const TargetLibraryInfo *TLI = &getAnalysis(). + getTLI(); + ObjectSizeOffsetEvaluator ObjSizeEval(DL, TLI, F.getContext(), true); + // Instrument. int NumInstrumented = 0; for (auto Inst : ToInstrument) { if (ClDebugMin < 0 || ClDebugMax < 0 || (NumInstrumented >= ClDebugMin && NumInstrumented <= ClDebugMax)) { if (isInterestingMemoryAccess(Inst, &IsWrite, &Alignment)) - instrumentMop(Inst, UseCalls); + instrumentMop(ObjSizeEval, Inst, UseCalls); else instrumentMemIntrinsic(cast(Inst)); } @@ -2023,3 +2040,20 @@ AI->eraseFromParent(); NumInstrumentedDynamicAllocas++; } + +// isInboundsAccess returns true if Addr is always inbounds with respect to its +// base object. For example, it is a field access or an array access with +// constant inbounds index. +bool AddressSanitizer::isInboundsAccess(ObjectSizeOffsetEvaluator &ObjSizeEval, + Value *Addr) { + SizeOffsetEvalType SizeOffset = ObjSizeEval.compute(Addr); + if (!ObjSizeEval.bothKnown(SizeOffset) || + !isa(SizeOffset.first) || + !isa(SizeOffset.second)) + return false; + Type *OrigTy = cast(Addr->getType())->getElementType(); + uint32_t TypeSize = DL->getTypeStoreSizeInBits(OrigTy); + int64_t Size = dyn_cast(SizeOffset.first)->getSExtValue(); + int64_t Offset = dyn_cast(SizeOffset.second)->getSExtValue(); + return Offset >= 0 && Offset + TypeSize / 8 <= Size; +} Index: test/Instrumentation/AddressSanitizer/instrument-stack.ll =================================================================== --- test/Instrumentation/AddressSanitizer/instrument-stack.ll +++ test/Instrumentation/AddressSanitizer/instrument-stack.ll @@ -0,0 +1,46 @@ +; This test checks that we are not instrumenting direct inbound stack accesses. +; RUN: opt < %s -asan -asan-module -S | FileCheck %s + +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +;@sink = global i32* null, align 4 + +define void @foo() uwtable sanitize_address { +entry: + %a = alloca i32, align 4 + store i32 42, i32* %a, align 4 + ret void +; CHECK-LABEL: define void @foo +; CHECK-NOT: __asan_report +; CHECK: ret void +} + +define void @baz(i64 %i) sanitize_address { +entry: + %a = alloca [10 x i32], align 4 + %e = getelementptr inbounds [10 x i32]* %a, i32 0, i64 %i + store i32 42, i32* %e, align 4 + ret void +; CHECK-LABEL: define void @baz +; CHECK: __asan_report +; CHECK: ret void +} + +define void @bar() sanitize_address { +entry: + %a = alloca [10 x i32], align 4 + %e = getelementptr inbounds [10 x i32]* %a, i32 0, i64 12 + store i32 42, i32* %e, align 4 + ret void +; CHECK-LABEL: define void @bar +; CHECK: __asan_report +; CHECK: ret void +} + +define void @endoftests() sanitize_address { +entry: + ret void +; CHECK-LABEL: define void @endoftests +} +