diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp index d0cf40307060..3f0db3630b98 100644 --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -83,6 +83,7 @@ static const char *const kAsanUnpoisonGlobalsName = "__asan_after_dynamic_init"; static const char *const kAsanInitName = "__asan_init_v4"; static const char *const kAsanCovModuleInitName = "__sanitizer_cov_module_init"; static const char *const kAsanCovName = "__sanitizer_cov"; +static const char *const kAsanCovIndirCallName = "__sanitizer_cov_indir_call16"; static const char *const kAsanPtrCmp = "__sanitizer_ptr_cmp"; static const char *const kAsanPtrSub = "__sanitizer_ptr_sub"; static const char *const kAsanHandleNoReturnName = "__asan_handle_no_return"; @@ -136,7 +137,8 @@ static cl::opt ClGlobals("asan-globals", cl::desc("Handle global objects"), cl::Hidden, cl::init(true)); static cl::opt ClCoverage("asan-coverage", cl::desc("ASan coverage. 0: none, 1: entry block, 2: all blocks, " - "3: all blocks and critical edges"), + "3: all blocks and critical edges, " + "4: above plus indirect calls"), cl::Hidden, cl::init(false)); static cl::opt ClCoverageBlockThreshold("asan-coverage-block-threshold", cl::desc("Add coverage instrumentation only to the entry block if there " @@ -387,7 +389,10 @@ struct AddressSanitizer : public FunctionPass { bool LooksLikeCodeInBug11395(Instruction *I); bool GlobalIsLinkerInitialized(GlobalVariable *G); - bool InjectCoverage(Function &F, ArrayRef AllBlocks); + void InjectCoverageForIndirectCalls(Function &F, + ArrayRef IndirCalls); + bool InjectCoverage(Function &F, ArrayRef AllBlocks, + ArrayRef IndirCalls); void InjectCoverageAtBlock(Function &F, BasicBlock &BB); LLVMContext *C; @@ -399,6 +404,7 @@ struct AddressSanitizer : public FunctionPass { Function *AsanInitFunction; Function *AsanHandleNoReturnFunc; Function *AsanCovFunction; + Function *AsanCovIndirCallFunction; Function *AsanPtrCmpFunction, *AsanPtrSubFunction; // This array is indexed by AccessIsWrite and log2(AccessSize). Function *AsanErrorCallback[2][kNumberOfAccessSizes]; @@ -1255,6 +1261,9 @@ void AddressSanitizer::initializeCallbacks(Module &M) { M.getOrInsertFunction(kAsanHandleNoReturnName, IRB.getVoidTy(), NULL)); AsanCovFunction = checkInterfaceFunction(M.getOrInsertFunction( kAsanCovName, IRB.getVoidTy(), NULL)); + AsanCovIndirCallFunction = checkInterfaceFunction(M.getOrInsertFunction( + kAsanCovIndirCallName, IRB.getVoidTy(), IntptrTy, IntptrTy, NULL)); + AsanPtrCmpFunction = checkInterfaceFunction(M.getOrInsertFunction( kAsanPtrCmp, IRB.getVoidTy(), IntptrTy, IntptrTy, NULL)); AsanPtrSubFunction = checkInterfaceFunction(M.getOrInsertFunction( @@ -1368,7 +1377,8 @@ void AddressSanitizer::InjectCoverageAtBlock(Function &F, BasicBlock &BB) { // a) get the functionality to users earlier and // b) collect usage statistics to help improve Clang coverage design. bool AddressSanitizer::InjectCoverage(Function &F, - ArrayRef AllBlocks) { + ArrayRef AllBlocks, + ArrayRef IndirCalls) { if (!ClCoverage) return false; if (ClCoverage == 1 || @@ -1378,9 +1388,36 @@ bool AddressSanitizer::InjectCoverage(Function &F, for (auto BB : AllBlocks) InjectCoverageAtBlock(F, *BB); } + InjectCoverageForIndirectCalls(F, IndirCalls); return true; } +// On every indirect call we call a run-time function +// __sanitizer_cov_indir_call* with two parameters: +// - callee address, +// - global cache array that contains kCacheSize pointers (zero-initialed). +// The cache is used to speed up recording the caller-callee pairs. +// The address of the caller is passed implicitly via caller PC. +// kCacheSize is encoded in the name of the run-time function. +void AddressSanitizer::InjectCoverageForIndirectCalls( + Function &F, ArrayRef IndirCalls) { + if (ClCoverage < 4 || IndirCalls.empty()) return; + const int kCacheSize = 16; + const int kCacheAlignment = 64; // Align for better performance. + Type *Ty = ArrayType::get(IntptrTy, kCacheSize); + GlobalVariable *CalleeCache = + new GlobalVariable(*F.getParent(), Ty, false, GlobalValue::PrivateLinkage, + Constant::getNullValue(Ty), "__asan_gen_callee_cache"); + CalleeCache->setAlignment(kCacheAlignment); + for (auto I : IndirCalls) { + IRBuilder<> IRB(I); + CallSite CS(I); + IRB.CreateCall2(AsanCovIndirCallFunction, + IRB.CreatePointerCast(CS.getCalledValue(), IntptrTy), + IRB.CreatePointerCast(CalleeCache, IntptrTy)); + } +} + bool AddressSanitizer::runOnFunction(Function &F) { if (&F == AsanCtorFunction) return false; if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) return false; @@ -1403,6 +1440,7 @@ bool AddressSanitizer::runOnFunction(Function &F) { SmallVector NoReturnCalls; SmallVector AllBlocks; SmallVector PointerComparisonsOrSubtracts; + SmallVector IndirCalls; int NumAllocas = 0; bool IsWrite; unsigned Alignment; @@ -1435,6 +1473,8 @@ bool AddressSanitizer::runOnFunction(Function &F) { TempsToInstrument.clear(); if (CS.doesNotReturn()) NoReturnCalls.push_back(CS.getInstruction()); + if (ClCoverage >= 4 && !CS.getCalledFunction()) + IndirCalls.push_back(&Inst); } continue; } @@ -1491,7 +1531,7 @@ bool AddressSanitizer::runOnFunction(Function &F) { bool res = NumInstrumented > 0 || ChangedStack || !NoReturnCalls.empty(); - if (InjectCoverage(F, AllBlocks)) + if (InjectCoverage(F, AllBlocks, IndirCalls)) res = true; DEBUG(dbgs() << "ASAN done instrumenting: " << res << " " << F << "\n"); diff --git a/llvm/test/Instrumentation/AddressSanitizer/coverage.ll b/llvm/test/Instrumentation/AddressSanitizer/coverage.ll index 6be4f9ae86ef..d666925d6220 100644 --- a/llvm/test/Instrumentation/AddressSanitizer/coverage.ll +++ b/llvm/test/Instrumentation/AddressSanitizer/coverage.ll @@ -4,6 +4,7 @@ ; RUN: opt < %s -asan -asan-module -asan-coverage=2 -asan-coverage-block-threshold=10 -S | FileCheck %s --check-prefix=CHECK2 ; RUN: opt < %s -asan -asan-module -asan-coverage=2 -asan-coverage-block-threshold=1 -S | FileCheck %s --check-prefix=CHECK1 ; RUN: opt < %s -asan -asan-module -asan-coverage=3 -asan-coverage-block-threshold=10 -S | FileCheck %s --check-prefix=CHECK3 +; RUN: opt < %s -asan -asan-module -asan-coverage=4 -S | FileCheck %s --check-prefix=CHECK4 ; RUN: opt < %s -asan -asan-module -asan-coverage=0 -asan-globals=0 -S | \ ; RUN: FileCheck %s --check-prefix=CHECK0 @@ -44,7 +45,7 @@ entry: ; CHECK1-LABEL: define internal void @asan.module_ctor ; CHECK1-NOT: ret -; CHECK1: call void @__sanitizer_cov_module_init(i64 1) +; CHECK1: call void @__sanitizer_cov_module_init(i64 2) ; CHECK1: ret @@ -57,7 +58,7 @@ entry: ; CHECK2-LABEL: define internal void @asan.module_ctor ; CHECK2-NOT: ret -; CHECK2: call void @__sanitizer_cov_module_init(i64 3) +; CHECK2: call void @__sanitizer_cov_module_init(i64 4) ; CHECK2: ret ; CHECK3-LABEL: define void @foo @@ -68,3 +69,18 @@ entry: ; CHECK3-NOT: call void @__sanitizer_cov ; CHECK3: ret void + +%struct.StructWithVptr = type { i32 (...)** } + +define void @CallViaVptr(%struct.StructWithVptr* %foo) uwtable sanitize_address { +entry: + %0 = bitcast %struct.StructWithVptr* %foo to void (%struct.StructWithVptr*)*** + %vtable = load void (%struct.StructWithVptr*)*** %0, align 8 + %1 = load void (%struct.StructWithVptr*)** %vtable, align 8 + tail call void %1(%struct.StructWithVptr* %foo) + ret void +} + +; CHECK4-LABEL: define void @CallViaVptr +; CHECK4: call void @__sanitizer_cov_indir_call16 +; CHECK4: ret void