Skip to content

Commit

Permalink
Reapply "[asan] Poor man's coverage that works with ASan"
Browse files Browse the repository at this point in the history
I was able to successfully run a bootstrapped LTO build of clang with
r194701, so this change does not seem to be the cause of our failing
buildbots.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@194789 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
bob-wilson committed Nov 15, 2013
1 parent 728eb5f commit 4b89914
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 0 deletions.
52 changes: 52 additions & 0 deletions lib/Transforms/Instrumentation/AddressSanitizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ static const char *const kAsanUnregisterGlobalsName =
static const char *const kAsanPoisonGlobalsName = "__asan_before_dynamic_init";
static const char *const kAsanUnpoisonGlobalsName = "__asan_after_dynamic_init";
static const char *const kAsanInitName = "__asan_init_v3";
static const char *const kAsanCovName = "__sanitizer_cov";
static const char *const kAsanHandleNoReturnName = "__asan_handle_no_return";
static const char *const kAsanMappingOffsetName = "__asan_mapping_offset";
static const char *const kAsanMappingScaleName = "__asan_mapping_scale";
Expand Down Expand Up @@ -134,6 +135,8 @@ static cl::opt<bool> ClUseAfterReturn("asan-use-after-return",
// This flag may need to be replaced with -f[no]asan-globals.
static cl::opt<bool> ClGlobals("asan-globals",
cl::desc("Handle global objects"), cl::Hidden, cl::init(true));
static cl::opt<bool> ClCoverage("asan-coverage",
cl::desc("ASan coverage"), cl::Hidden, cl::init(false));
static cl::opt<bool> ClInitializers("asan-initialization-order",
cl::desc("Handle C++ initializer order"), cl::Hidden, cl::init(false));
static cl::opt<bool> ClMemIntrin("asan-memintrin",
Expand Down Expand Up @@ -324,6 +327,7 @@ struct AddressSanitizer : public FunctionPass {
bool LooksLikeCodeInBug11395(Instruction *I);
void FindDynamicInitializers(Module &M);
bool GlobalIsLinkerInitialized(GlobalVariable *G);
bool InjectCoverage(Function &F);

bool CheckInitOrder;
bool CheckUseAfterReturn;
Expand All @@ -339,6 +343,7 @@ struct AddressSanitizer : public FunctionPass {
Function *AsanCtorFunction;
Function *AsanInitFunction;
Function *AsanHandleNoReturnFunc;
Function *AsanCovFunction;
OwningPtr<SpecialCaseList> BL;
// This array is indexed by AccessIsWrite and log2(AccessSize).
Function *AsanErrorCallback[2][kNumberOfAccessSizes];
Expand Down Expand Up @@ -1085,6 +1090,8 @@ void AddressSanitizer::initializeCallbacks(Module &M) {

AsanHandleNoReturnFunc = checkInterfaceFunction(M.getOrInsertFunction(
kAsanHandleNoReturnName, IRB.getVoidTy(), NULL));
AsanCovFunction = checkInterfaceFunction(M.getOrInsertFunction(
kAsanCovName, IRB.getVoidTy(), IntptrTy, NULL));
// We insert an empty inline asm after __asan_report* to avoid callback merge.
EmptyAsm = InlineAsm::get(FunctionType::get(IRB.getVoidTy(), false),
StringRef(""), StringRef(""),
Expand Down Expand Up @@ -1156,6 +1163,47 @@ 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;
IRBuilder<> IRB(F.getEntryBlock().getFirstInsertionPt());
Type *Int8Ty = IRB.getInt8Ty();
GlobalVariable *Guard = new GlobalVariable(
*F.getParent(), Int8Ty, false, F.getLinkage(),
Constant::getNullValue(Int8Ty), "__asan_gen_cov_" + F.getName());
LoadInst *Load = IRB.CreateLoad(Guard);
Load->setAtomic(Monotonic);
Load->setAlignment(1);
Value *Cmp = IRB.CreateICmpEQ(Constant::getNullValue(Int8Ty), Load);
Instruction *Ins = SplitBlockAndInsertIfThen(cast<Instruction>(Cmp), false);
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));
StoreInst *Store = IRB.CreateStore(ConstantInt::get(Int8Ty, 1), Guard);
Store->setAtomic(Monotonic);
Store->setAlignment(1);
return true;
}

bool AddressSanitizer::runOnFunction(Function &F) {
if (BL->isIn(F)) return false;
if (&F == AsanCtorFunction) return false;
Expand Down Expand Up @@ -1251,6 +1299,10 @@ bool AddressSanitizer::runOnFunction(Function &F) {
}

bool res = NumInstrumented > 0 || ChangedStack || !NoReturnCalls.empty();

if (InjectCoverage(F))
res = true;

DEBUG(dbgs() << "ASAN done instrumenting: " << res << " " << F << "\n");

if (ClKeepUninstrumented) {
Expand Down
13 changes: 13 additions & 0 deletions test/Instrumentation/AddressSanitizer/coverage.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
; RUN: opt < %s -asan -asan-coverage=1 -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"
target triple = "x86_64-unknown-linux-gnu"
define i32 @foo(i32* %a) sanitize_address {
entry:
ret i32 0
}
; 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

0 comments on commit 4b89914

Please sign in to comment.