From: Peter Collingbourne Date: Thu, 15 Aug 2013 18:51:12 +0000 (+0000) Subject: DataFlowSanitizer: Add a debugging feature to help us track nonzero labels. X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;h=a77d9f726a7e3c51f04d1d74d091ae1a87d63544;p=oota-llvm.git DataFlowSanitizer: Add a debugging feature to help us track nonzero labels. Summary: When the -dfsan-debug-nonzero-labels parameter is supplied, the code is instrumented such that when a call parameter, return value or load produces a nonzero label, the function __dfsan_nonzero_label is called. The idea is that a debugger breakpoint can be set on this function in a nominally label-free program to help identify any bugs in the instrumentation pass causing labels to be introduced. Reviewers: eugenis CC: llvm-commits Differential Revision: http://llvm-reviews.chandlerc.com/D1405 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@188472 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp b/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp index b645ab8870b..7159cc04994 100644 --- a/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp +++ b/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp @@ -95,6 +95,12 @@ static cl::opt ClArgsABI( cl::desc("Use the argument ABI rather than the TLS ABI"), cl::Hidden); +static cl::opt ClDebugNonzeroLabels( + "dfsan-debug-nonzero-labels", + cl::desc("Insert calls to __dfsan_nonzero_label on observing a parameter, " + "load or return with a nonzero label"), + cl::Hidden); + namespace { class DataFlowSanitizer : public ModulePass { @@ -160,10 +166,12 @@ class DataFlowSanitizer : public ModulePass { FunctionType *DFSanUnionLoadFnTy; FunctionType *DFSanUnimplementedFnTy; FunctionType *DFSanSetLabelFnTy; + FunctionType *DFSanNonzeroLabelFnTy; Constant *DFSanUnionFn; Constant *DFSanUnionLoadFn; Constant *DFSanUnimplementedFn; Constant *DFSanSetLabelFn; + Constant *DFSanNonzeroLabelFn; MDNode *ColdCallWeights; OwningPtr ABIList; DenseMap UnwrappedFnMap; @@ -197,6 +205,7 @@ struct DFSanFunction { DenseMap AllocaShadowMap; std::vector > PHIFixups; DenseSet SkipInsts; + DenseSet NonZeroChecks; DFSanFunction(DataFlowSanitizer &DFS, Function *F, bool IsNativeABI) : DFS(DFS), F(F), IA(DFS.getInstrumentedABI()), @@ -311,6 +320,8 @@ bool DataFlowSanitizer::doInitialization(Module &M) { Type *DFSanSetLabelArgs[3] = { ShadowTy, Type::getInt8PtrTy(*Ctx), IntptrTy }; DFSanSetLabelFnTy = FunctionType::get(Type::getVoidTy(*Ctx), DFSanSetLabelArgs, /*isVarArg=*/false); + DFSanNonzeroLabelFnTy = FunctionType::get( + Type::getVoidTy(*Ctx), ArrayRef(), /*isVarArg=*/false); if (GetArgTLSPtr) { Type *ArgTLSTy = ArrayType::get(ShadowTy, 64); @@ -389,6 +400,8 @@ bool DataFlowSanitizer::runOnModule(Module &M) { if (Function *F = dyn_cast(DFSanSetLabelFn)) { F->addAttribute(1, Attribute::ZExt); } + DFSanNonzeroLabelFn = + Mod->getOrInsertFunction("__dfsan_nonzero_label", DFSanNonzeroLabelFnTy); std::vector FnsToInstrument; llvm::SmallPtrSet FnsWithNativeABI; @@ -397,7 +410,8 @@ bool DataFlowSanitizer::runOnModule(Module &M) { i != DFSanUnionFn && i != DFSanUnionLoadFn && i != DFSanUnimplementedFn && - i != DFSanSetLabelFn) + i != DFSanSetLabelFn && + i != DFSanNonzeroLabelFn) FnsToInstrument.push_back(&*i); } @@ -560,6 +574,31 @@ bool DataFlowSanitizer::runOnModule(Module &M) { val, DFSF.getShadow(i->first->getIncomingValue(val))); } } + + // -dfsan-debug-nonzero-labels will split the CFG in all kinds of crazy + // places (i.e. instructions in basic blocks we haven't even begun visiting + // yet). To make our life easier, do this work in a pass after the main + // instrumentation. + if (ClDebugNonzeroLabels) { + for (DenseSet::iterator i = DFSF.NonZeroChecks.begin(), + e = DFSF.NonZeroChecks.end(); + i != e; ++i) { + Instruction *Pos; + if (Instruction *I = dyn_cast(*i)) + Pos = I->getNextNode(); + else + Pos = DFSF.F->getEntryBlock().begin(); + while (isa(Pos) || isa(Pos)) + Pos = Pos->getNextNode(); + IRBuilder<> IRB(Pos); + Instruction *NeInst = cast( + IRB.CreateICmpNE(*i, DFSF.DFS.ZeroShadow)); + BranchInst *BI = cast(SplitBlockAndInsertIfThen( + NeInst, /*Unreachable=*/ false, ColdCallWeights)); + IRBuilder<> ThenIRB(BI); + ThenIRB.CreateCall(DFSF.DFS.DFSanNonzeroLabelFn); + } + } } return false; @@ -618,6 +657,7 @@ Value *DFSanFunction::getShadow(Value *V) { break; } } + NonZeroChecks.insert(Shadow); } else { Shadow = DFS.ZeroShadow; } @@ -814,7 +854,11 @@ void DFSanVisitor::visitLoadInst(LoadInst &LI) { Value *LoadedShadow = DFSF.loadShadow(LI.getPointerOperand(), Size, Align, &LI); Value *PtrShadow = DFSF.getShadow(LI.getPointerOperand()); - DFSF.setShadow(&LI, DFSF.DFS.combineShadows(LoadedShadow, PtrShadow, &LI)); + Value *CombinedShadow = DFSF.DFS.combineShadows(LoadedShadow, PtrShadow, &LI); + if (CombinedShadow != DFSF.DFS.ZeroShadow) + DFSF.NonZeroChecks.insert(CombinedShadow); + + DFSF.setShadow(&LI, CombinedShadow); } void DFSanFunction::storeShadow(Value *Addr, uint64_t Size, uint64_t Align, @@ -1131,6 +1175,7 @@ void DFSanVisitor::visitCallSite(CallSite CS) { LoadInst *LI = NextIRB.CreateLoad(DFSF.getRetvalTLS()); DFSF.SkipInsts.insert(LI); DFSF.setShadow(CS.getInstruction(), LI); + DFSF.NonZeroChecks.insert(LI); } } @@ -1184,6 +1229,7 @@ void DFSanVisitor::visitCallSite(CallSite CS) { ExtractValueInst::Create(NewCS.getInstruction(), 1, "", Next); DFSF.SkipInsts.insert(ExShadow); DFSF.setShadow(ExVal, ExShadow); + DFSF.NonZeroChecks.insert(ExShadow); CS.getInstruction()->replaceAllUsesWith(ExVal); } diff --git a/test/Instrumentation/DataFlowSanitizer/debug-nonzero-labels.ll b/test/Instrumentation/DataFlowSanitizer/debug-nonzero-labels.ll new file mode 100644 index 00000000000..4329fd112d7 --- /dev/null +++ b/test/Instrumentation/DataFlowSanitizer/debug-nonzero-labels.ll @@ -0,0 +1,23 @@ +; RUN: opt < %s -dfsan -dfsan-args-abi -dfsan-debug-nonzero-labels -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" + +declare i32 @g() + +; CHECK: define { i32, i16 } @f(i32, i16) +define i32 @f(i32) { + ; CHECK: [[LOCALLABELALLOCA:%.*]] = alloca i16 + ; CHECK: [[ARGCMP:%.*]] = icmp ne i16 %1, 0 + ; CHECK: br i1 [[ARGCMP]] + %i = alloca i32 + store i32 %0, i32* %i + ; CHECK: [[CALL:%.*]] = call { i32, i16 } @g() + ; CHECK: [[CALLLABEL:%.*]] = extractvalue { i32, i16 } [[CALL]], 1 + ; CHECK: [[CALLCMP:%.*]] = icmp ne i16 [[CALLLABEL]], 0 + ; CHECK: br i1 [[CALLCMP]] + %call = call i32 @g() + ; CHECK: [[LOCALLABEL:%.*]] = load i16* [[LOCALLABELALLOCA]] + ; CHECK: [[LOCALCMP:%.*]] = icmp ne i16 [[LOCALLABEL]], 0 + ; CHECK: br i1 [[LOCALCMP]] + %load = load i32* %i + ret i32 %load +}