diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_coverage.cc b/compiler-rt/lib/sanitizer_common/sanitizer_coverage.cc index 0e928e0cba65..352f851749a7 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_coverage.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_coverage.cc @@ -11,15 +11,15 @@ // This file implements run-time support for a poor man's coverage tool. // // Compiler instrumentation: -// For every function F the compiler injects the following code: +// For every interesting basic block the compiler injects the following code: // if (*Guard) { -// __sanitizer_cov(&F); +// __sanitizer_cov(); // *Guard = 1; // } -// It's fine to call __sanitizer_cov more than once for a given function. +// It's fine to call __sanitizer_cov more than once for a given block. // // Run-time: -// - __sanitizer_cov(pc): record that we've executed a given PC. +// - __sanitizer_cov(): record that we've executed the PC (GET_CALLER_PC). // - __sanitizer_cov_dump: dump the coverage data to disk. // For every module of the current process that has coverage data // this will create a file module_name.PID.sancov. The file format is simple: @@ -37,6 +37,7 @@ #include "sanitizer_libc.h" #include "sanitizer_mutex.h" #include "sanitizer_procmaps.h" +#include "sanitizer_stacktrace.h" #include "sanitizer_flags.h" struct CovData { @@ -105,8 +106,8 @@ void CovDump() { } // namespace __sanitizer extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(void *pc) { - CovAdd(reinterpret_cast(pc)); +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov() { + CovAdd(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC())); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); } } // extern "C" diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h b/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h index b6dce2cea9ee..6519f4214ac3 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h @@ -99,7 +99,7 @@ extern "C" { void __sanitizer_report_error_summary(const char *error_summary); SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); - SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(void *pc); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(); SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_annotate_contiguous_container(const void *beg, const void *end, diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp index c03075c9ea06..fe875192f581 100644 --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -33,6 +33,7 @@ #include "llvm/IR/InlineAsm.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/MDBuilder.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" #include "llvm/InstVisitor.h" @@ -130,8 +131,9 @@ static cl::opt ClUseAfterReturn("asan-use-after-return", // This flag may need to be replaced with -f[no]asan-globals. 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"), cl::Hidden, cl::init(false)); +static cl::opt ClCoverage("asan-coverage", + cl::desc("ASan coverage. 0: none, 1: entry block, 2: all blocks"), + cl::Hidden, cl::init(false)); static cl::opt ClInitializers("asan-initialization-order", cl::desc("Handle C++ initializer order"), cl::Hidden, cl::init(false)); static cl::opt ClMemIntrin("asan-memintrin", @@ -320,7 +322,8 @@ struct AddressSanitizer : public FunctionPass { bool LooksLikeCodeInBug11395(Instruction *I); void FindDynamicInitializers(Module &M); bool GlobalIsLinkerInitialized(GlobalVariable *G); - bool InjectCoverage(Function &F); + bool InjectCoverage(Function &F, const ArrayRef AllBlocks); + void InjectCoverageAtBlock(Function &F, BasicBlock &BB); bool CheckInitOrder; bool CheckUseAfterReturn; @@ -1076,7 +1079,7 @@ void AddressSanitizer::initializeCallbacks(Module &M) { AsanHandleNoReturnFunc = checkInterfaceFunction(M.getOrInsertFunction( kAsanHandleNoReturnName, IRB.getVoidTy(), NULL)); AsanCovFunction = checkInterfaceFunction(M.getOrInsertFunction( - kAsanCovName, IRB.getVoidTy(), IntptrTy, NULL)); + kAsanCovName, IRB.getVoidTy(), NULL)); // We insert an empty inline asm after __asan_report* to avoid callback merge. EmptyAsm = InlineAsm::get(FunctionType::get(IRB.getVoidTy(), false), StringRef(""), StringRef(""), @@ -1148,33 +1151,11 @@ bool AddressSanitizer::maybeInsertAsanInitAtFunctionEntry(Function &F) { return false; } -// Poor man's coverage that works with ASan. -// We create a Guard boolean variable with the same linkage -// as the function and inject this code into the entry block: -// if (*Guard) { -// __sanitizer_cov(&F); -// *Guard = 1; -// } -// The accesses to Guard are atomic. The rest of the logic is -// in __sanitizer_cov (it's fine to call it more than once). -// -// This coverage implementation provides very limited data: -// it only tells if a given function was ever executed. -// No counters, no per-basic-block or per-edge data. -// But for many use cases this is what we need and the added slowdown -// is negligible. This simple implementation will probably be obsoleted -// by the upcoming Clang-based coverage implementation. -// By having it here and now we hope to -// a) get the functionality to users earlier and -// b) collect usage statistics to help improve Clang coverage design. -bool AddressSanitizer::InjectCoverage(Function &F) { - if (!ClCoverage) return false; - +void AddressSanitizer::InjectCoverageAtBlock(Function &F, BasicBlock &BB) { + BasicBlock::iterator IP = BB.getFirstInsertionPt(), BE = BB.end(); // Skip static allocas at the top of the entry block so they don't become // dynamic when we split the block. If we used our optimized stack layout, // then there will only be one alloca and it will come first. - BasicBlock &Entry = F.getEntryBlock(); - BasicBlock::iterator IP = Entry.getFirstInsertionPt(), BE = Entry.end(); for (; IP != BE; ++IP) { AllocaInst *AI = dyn_cast(IP); if (!AI || !AI->isStaticAlloca()) @@ -1190,14 +1171,48 @@ bool AddressSanitizer::InjectCoverage(Function &F) { Load->setAtomic(Monotonic); Load->setAlignment(1); Value *Cmp = IRB.CreateICmpEQ(Constant::getNullValue(Int8Ty), Load); - Instruction *Ins = SplitBlockAndInsertIfThen(Cmp, IP, false); + Instruction *Ins = SplitBlockAndInsertIfThen( + Cmp, IP, false, MDBuilder(*C).createBranchWeights(1, 100000)); IRB.SetInsertPoint(Ins); // We pass &F to __sanitizer_cov. We could avoid this and rely on // GET_CALLER_PC, but having the PC of the first instruction is just nice. - IRB.CreateCall(AsanCovFunction, IRB.CreatePointerCast(&F, IntptrTy)); + Instruction *Call = IRB.CreateCall(AsanCovFunction); + Call->setDebugLoc(IP->getDebugLoc()); StoreInst *Store = IRB.CreateStore(ConstantInt::get(Int8Ty, 1), Guard); Store->setAtomic(Monotonic); Store->setAlignment(1); +} + +// Poor man's coverage that works with ASan. +// We create a Guard boolean variable with the same linkage +// as the function and inject this code into the entry block (-asan-coverage=1) +// or all blocks (-asan-coverage=2): +// if (*Guard) { +// __sanitizer_cov(&F); +// *Guard = 1; +// } +// The accesses to Guard are atomic. The rest of the logic is +// in __sanitizer_cov (it's fine to call it more than once). +// +// This coverage implementation provides very limited data: +// it only tells if a given function (block) was ever executed. +// No counters, no per-edge data. +// But for many use cases this is what we need and the added slowdown +// is negligible. This simple implementation will probably be obsoleted +// by the upcoming Clang-based coverage implementation. +// By having it here and now we hope to +// a) get the functionality to users earlier and +// b) collect usage statistics to help improve Clang coverage design. +bool AddressSanitizer::InjectCoverage(Function &F, + const ArrayRef AllBlocks) { + if (!ClCoverage) return false; + + if (ClCoverage == 1) { + InjectCoverageAtBlock(F, F.getEntryBlock()); + } else { + for (size_t i = 0, n = AllBlocks.size(); i < n; i++) + InjectCoverageAtBlock(F, *AllBlocks[i]); + } return true; } @@ -1222,12 +1237,14 @@ bool AddressSanitizer::runOnFunction(Function &F) { SmallSet TempsToInstrument; SmallVector ToInstrument; SmallVector NoReturnCalls; + SmallVector AllBlocks; int NumAllocas = 0; bool IsWrite; // Fill the set of memory operations to instrument. for (Function::iterator FI = F.begin(), FE = F.end(); FI != FE; ++FI) { + AllBlocks.push_back(FI); TempsToInstrument.clear(); int NumInsnsPerBB = 0; for (BasicBlock::iterator BI = FI->begin(), BE = FI->end(); @@ -1297,7 +1314,7 @@ bool AddressSanitizer::runOnFunction(Function &F) { bool res = NumInstrumented > 0 || ChangedStack || !NoReturnCalls.empty(); - if (InjectCoverage(F)) + if (InjectCoverage(F, AllBlocks)) 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 47a54c0ef85e..6697f4b0f24a 100644 --- a/llvm/test/Instrumentation/AddressSanitizer/coverage.ll +++ b/llvm/test/Instrumentation/AddressSanitizer/coverage.ll @@ -1,13 +1,30 @@ -; RUN: opt < %s -asan -asan-coverage=1 -S | FileCheck %s +; RUN: opt < %s -asan -asan-coverage=1 -S | FileCheck %s --check-prefix=CHECK1 +; RUN: opt < %s -asan -asan-coverage=2 -S | FileCheck %s --check-prefix=CHECK2 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" target triple = "x86_64-unknown-linux-gnu" -define i32 @foo(i32* %a) sanitize_address { +define void @foo(i32* %a) sanitize_address { entry: - ret i32 0 + %tobool = icmp eq i32* %a, null + br i1 %tobool, label %if.end, label %if.then + + if.then: ; preds = %entry + store i32 0, i32* %a, align 4 + br label %if.end + + if.end: ; preds = %entry, %if.then + ret void } -; CHECK: define i32 @foo(i32* %a) #0 { -; CHECK: %0 = load atomic i8* @__asan_gen_cov_foo monotonic, align 1 -; CHECK: %1 = icmp eq i8 0, %0 -; CHECK: br i1 %1, label %2, label %3 -; CHECK: call void @__sanitizer_cov(i64 ptrtoint (i32 (i32*)* @foo to i64)) -; CHECK: store atomic i8 1, i8* @__asan_gen_cov_foo monotonic, align 1 +; CHECK1-LABEL: define void @foo +; CHECK1: %0 = load atomic i8* @__asan_gen_cov_foo monotonic, align 1 +; CHECK1: %1 = icmp eq i8 0, %0 +; CHECK1: br i1 %1, label %2, label %3 +; CHECK1: call void @__sanitizer_cov +; CHECK1-NOT: call void @__sanitizer_cov +; CHECK1: store atomic i8 1, i8* @__asan_gen_cov_foo monotonic, align 1 + +; CHECK2-LABEL: define void @foo +; CHECK2: call void @__sanitizer_cov +; CHECK2: call void @__sanitizer_cov +; CHECK2: call void @__sanitizer_cov +; CHECK2-NOT: call void @__sanitizer_cov +; CHECK2: ret void