From 905e1efbe5b6ba4dc1f928075e08cab63aab144c Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Wed, 14 Aug 2013 18:54:12 +0000 Subject: [PATCH] DataFlowSanitizer: greylist is now ABI list. This replaces the old incomplete greylist functionality with an ABI list, which can provide more detailed information about the ABI and semantics of specific functions. The pass treats every function in the "uninstrumented" category in the ABI list file as conforming to the "native" (i.e. unsanitized) ABI. Unless the ABI list contains additional categories for those functions, a call to one of those functions will produce a warning message, as the labelling behaviour of the function is unknown. The other supported categories are "functional", "discard" and "custom". - "discard" -- This function does not write to (user-accessible) memory, and its return value is unlabelled. - "functional" -- This function does not write to (user-accessible) memory, and the label of its return value is the union of the label of its arguments. - "custom" -- Instead of calling the function, a custom wrapper __dfsw_F is called, where F is the name of the function. This function may wrap the original function or provide its own implementation. Differential Revision: http://llvm-reviews.chandlerc.com/D1345 llvm-svn: 188402 --- include/llvm/Transforms/Instrumentation.h | 8 +- .../Instrumentation/DataFlowSanitizer.cpp | 363 +++++++++++++----- .../DataFlowSanitizer/abilist.ll | 47 +++ .../DataFlowSanitizer/abilist.ll.txt | 11 + 4 files changed, 332 insertions(+), 97 deletions(-) create mode 100644 test/Instrumentation/DataFlowSanitizer/abilist.ll create mode 100644 test/Instrumentation/DataFlowSanitizer/abilist.ll.txt diff --git a/include/llvm/Transforms/Instrumentation.h b/include/llvm/Transforms/Instrumentation.h index ecc470bb1f8..a5cac00e3e7 100644 --- a/include/llvm/Transforms/Instrumentation.h +++ b/include/llvm/Transforms/Instrumentation.h @@ -89,12 +89,14 @@ FunctionPass *createMemorySanitizerPass(bool TrackOrigins = false, FunctionPass *createThreadSanitizerPass(StringRef BlacklistFile = StringRef()); // Insert DataFlowSanitizer (dynamic data flow analysis) instrumentation -ModulePass *createDataFlowSanitizerPass(void *(*getArgTLS)() = 0, +ModulePass *createDataFlowSanitizerPass(StringRef ABIListFile = StringRef(), + void *(*getArgTLS)() = 0, void *(*getRetValTLS)() = 0); #if defined(__GNUC__) && defined(__linux__) -inline ModulePass *createDataFlowSanitizerPassForJIT() { - return createDataFlowSanitizerPass(getDFSanArgTLSPtrForJIT, +inline ModulePass *createDataFlowSanitizerPassForJIT(StringRef ABIListFile = + StringRef()) { + return createDataFlowSanitizerPass(ABIListFile, getDFSanArgTLSPtrForJIT, getDFSanRetValTLSPtrForJIT); } #endif diff --git a/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp b/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp index 29413d57bb3..dd01d83049d 100644 --- a/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp +++ b/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp @@ -76,17 +76,20 @@ static cl::opt ClPreserveAlignment( cl::desc("respect alignment requirements provided by input IR"), cl::Hidden, cl::init(false)); -// The greylist file controls how shadow parameters are passed. -// The program acts as though every function in the greylist is passed -// parameters with zero shadow and that its return value also has zero shadow. -// This avoids the use of TLS or extra function parameters to pass shadow state -// and essentially makes the function conform to the "native" (i.e. unsanitized) -// ABI. -static cl::opt ClGreylistFile( - "dfsan-greylist", - cl::desc("File containing the list of functions with a native ABI"), +// The ABI list file controls how shadow parameters are passed. The pass treats +// every function labelled "uninstrumented" in the ABI list file as conforming +// to the "native" (i.e. unsanitized) ABI. Unless the ABI list contains +// additional annotations for those functions, a call to one of those functions +// will produce a warning message, as the labelling behaviour of the function is +// unknown. The other supported annotations are "functional" and "discard", +// which are described below under DataFlowSanitizer::WrapperKind. +static cl::opt ClABIListFile( + "dfsan-abilist", + cl::desc("File listing native ABI functions and how the pass treats them"), cl::Hidden); +// Controls whether the pass uses IA_Args or IA_TLS as the ABI for instrumented +// functions (see DataFlowSanitizer::InstrumentedABI below). static cl::opt ClArgsABI( "dfsan-args-abi", cl::desc("Use the argument ABI rather than the TLS ABI"), @@ -102,13 +105,42 @@ class DataFlowSanitizer : public ModulePass { ShadowWidth = 16 }; + /// Which ABI should be used for instrumented functions? enum InstrumentedABI { - IA_None, - IA_MemOnly, + /// Argument and return value labels are passed through additional + /// arguments and by modifying the return type. IA_Args, + + /// Argument and return value labels are passed through TLS variables + /// __dfsan_arg_tls and __dfsan_retval_tls. IA_TLS }; + /// How should calls to uninstrumented functions be handled? + enum WrapperKind { + /// This function is present in an uninstrumented form but we don't know + /// how it should be handled. Print a warning and call the function anyway. + /// Don't label the return value. + WK_Warning, + + /// This function does not write to (user-accessible) memory, and its return + /// value is unlabelled. + WK_Discard, + + /// This function does not write to (user-accessible) memory, and the label + /// of its return value is the union of the label of its arguments. + WK_Functional, + + /// Instead of calling the function, a custom wrapper __dfsw_F is called, + /// where F is the name of the function. This function may wrap the + /// original function or provide its own implementation. This is similar to + /// the IA_Args ABI, except that IA_Args uses a struct return type to + /// pass the return value shadow in a register, while WK_Custom uses an + /// extra pointer argument to return the shadow. This allows the wrapped + /// form of the function type to be expressed in C. + WK_Custom + }; + DataLayout *DL; Module *Mod; LLVMContext *Ctx; @@ -126,20 +158,26 @@ class DataFlowSanitizer : public ModulePass { Constant *GetRetvalTLS; FunctionType *DFSanUnionFnTy; FunctionType *DFSanUnionLoadFnTy; + FunctionType *DFSanUnimplementedFnTy; Constant *DFSanUnionFn; Constant *DFSanUnionLoadFn; + Constant *DFSanUnimplementedFn; MDNode *ColdCallWeights; - OwningPtr Greylist; + OwningPtr ABIList; DenseMap UnwrappedFnMap; + AttributeSet ReadOnlyNoneAttrs; Value *getShadowAddress(Value *Addr, Instruction *Pos); Value *combineShadows(Value *V1, Value *V2, Instruction *Pos); - FunctionType *getInstrumentedFunctionType(FunctionType *T); - InstrumentedABI getInstrumentedABI(Function *F); - InstrumentedABI getDefaultInstrumentedABI(); + bool isInstrumented(Function *F); + FunctionType *getArgsFunctionType(FunctionType *T); + FunctionType *getCustomFunctionType(FunctionType *T); + InstrumentedABI getInstrumentedABI(); + WrapperKind getWrapperKind(Function *F); public: - DataFlowSanitizer(void *(*getArgTLS)() = 0, void *(*getRetValTLS)() = 0); + DataFlowSanitizer(StringRef ABIListFile = StringRef(), + void *(*getArgTLS)() = 0, void *(*getRetValTLS)() = 0); static char ID; bool doInitialization(Module &M); bool runOnModule(Module &M); @@ -149,16 +187,19 @@ struct DFSanFunction { DataFlowSanitizer &DFS; Function *F; DataFlowSanitizer::InstrumentedABI IA; + bool IsNativeABI; Value *ArgTLSPtr; Value *RetvalTLSPtr; + AllocaInst *LabelReturnAlloca; DenseMap ValShadowMap; DenseMap AllocaShadowMap; std::vector > PHIFixups; DenseSet SkipInsts; - DFSanFunction(DataFlowSanitizer &DFS, Function *F) - : DFS(DFS), F(F), IA(DFS.getInstrumentedABI(F)), ArgTLSPtr(0), - RetvalTLSPtr(0) {} + DFSanFunction(DataFlowSanitizer &DFS, Function *F, bool IsNativeABI) + : DFS(DFS), F(F), IA(DFS.getInstrumentedABI()), + IsNativeABI(IsNativeABI), ArgTLSPtr(0), RetvalTLSPtr(0), + LabelReturnAlloca(0) {} Value *getArgTLSPtr(); Value *getArgTLS(unsigned Index, Instruction *Pos); Value *getRetvalTLS(); @@ -203,17 +244,21 @@ char DataFlowSanitizer::ID; INITIALIZE_PASS(DataFlowSanitizer, "dfsan", "DataFlowSanitizer: dynamic data flow analysis.", false, false) -ModulePass *llvm::createDataFlowSanitizerPass(void *(*getArgTLS)(), +ModulePass *llvm::createDataFlowSanitizerPass(StringRef ABIListFile, + void *(*getArgTLS)(), void *(*getRetValTLS)()) { - return new DataFlowSanitizer(getArgTLS, getRetValTLS); + return new DataFlowSanitizer(ABIListFile, getArgTLS, getRetValTLS); } -DataFlowSanitizer::DataFlowSanitizer(void *(*getArgTLS)(), +DataFlowSanitizer::DataFlowSanitizer(StringRef ABIListFile, + void *(*getArgTLS)(), void *(*getRetValTLS)()) : ModulePass(ID), GetArgTLSPtr(getArgTLS), GetRetvalTLSPtr(getRetValTLS), - Greylist(SpecialCaseList::createOrDie(ClGreylistFile)) {} + ABIList(SpecialCaseList::createOrDie(ABIListFile.empty() ? ClABIListFile + : ABIListFile)) { +} -FunctionType *DataFlowSanitizer::getInstrumentedFunctionType(FunctionType *T) { +FunctionType *DataFlowSanitizer::getArgsFunctionType(FunctionType *T) { llvm::SmallVector ArgTypes; std::copy(T->param_begin(), T->param_end(), std::back_inserter(ArgTypes)); for (unsigned i = 0, e = T->getNumParams(); i != e; ++i) @@ -226,6 +271,18 @@ FunctionType *DataFlowSanitizer::getInstrumentedFunctionType(FunctionType *T) { return FunctionType::get(RetType, ArgTypes, T->isVarArg()); } +FunctionType *DataFlowSanitizer::getCustomFunctionType(FunctionType *T) { + assert(!T->isVarArg()); + llvm::SmallVector ArgTypes; + std::copy(T->param_begin(), T->param_end(), std::back_inserter(ArgTypes)); + for (unsigned i = 0, e = T->getNumParams(); i != e; ++i) + ArgTypes.push_back(ShadowTy); + Type *RetType = T->getReturnType(); + if (!RetType->isVoidTy()) + ArgTypes.push_back(ShadowPtrTy); + return FunctionType::get(T->getReturnType(), ArgTypes, false); +} + bool DataFlowSanitizer::doInitialization(Module &M) { DL = getAnalysisIfAvailable(); if (!DL) @@ -246,6 +303,8 @@ bool DataFlowSanitizer::doInitialization(Module &M) { Type *DFSanUnionLoadArgs[2] = { ShadowPtrTy, IntptrTy }; DFSanUnionLoadFnTy = FunctionType::get(ShadowTy, DFSanUnionLoadArgs, /*isVarArg=*/ false); + DFSanUnimplementedFnTy = FunctionType::get( + Type::getVoidTy(*Ctx), Type::getInt8PtrTy(*Ctx), /*isVarArg=*/false); if (GetArgTLSPtr) { Type *ArgTLSTy = ArrayType::get(ShadowTy, 64); @@ -267,23 +326,32 @@ bool DataFlowSanitizer::doInitialization(Module &M) { return true; } -DataFlowSanitizer::InstrumentedABI -DataFlowSanitizer::getInstrumentedABI(Function *F) { - if (Greylist->isIn(*F)) - return IA_MemOnly; - else - return getDefaultInstrumentedABI(); +bool DataFlowSanitizer::isInstrumented(Function *F) { + return !ABIList->isIn(*F, "uninstrumented"); } -DataFlowSanitizer::InstrumentedABI -DataFlowSanitizer::getDefaultInstrumentedABI() { +DataFlowSanitizer::InstrumentedABI DataFlowSanitizer::getInstrumentedABI() { return ClArgsABI ? IA_Args : IA_TLS; } +DataFlowSanitizer::WrapperKind DataFlowSanitizer::getWrapperKind(Function *F) { + if (ABIList->isIn(*F, "functional")) + return WK_Functional; + if (ABIList->isIn(*F, "discard")) + return WK_Discard; + if (ABIList->isIn(*F, "custom")) + return WK_Custom; + + return WK_Warning; +} + bool DataFlowSanitizer::runOnModule(Module &M) { if (!DL) return false; + if (ABIList->isIn(M, "skip")) + return false; + if (!GetArgTLSPtr) { Type *ArgTLSTy = ArrayType::get(ShadowTy, 64); ArgTLS = Mod->getOrInsertGlobal("__dfsan_arg_tls", ArgTLSTy); @@ -308,33 +376,44 @@ bool DataFlowSanitizer::runOnModule(Module &M) { if (Function *F = dyn_cast(DFSanUnionLoadFn)) { F->addAttribute(AttributeSet::ReturnIndex, Attribute::ZExt); } + DFSanUnimplementedFn = + Mod->getOrInsertFunction("__dfsan_unimplemented", DFSanUnimplementedFnTy); std::vector FnsToInstrument; + llvm::SmallPtrSet FnsWithNativeABI; for (Module::iterator i = M.begin(), e = M.end(); i != e; ++i) { - if (!i->isIntrinsic() && i != DFSanUnionFn && i != DFSanUnionLoadFn) + if (!i->isIntrinsic() && + i != DFSanUnionFn && + i != DFSanUnionLoadFn && + i != DFSanUnimplementedFn) FnsToInstrument.push_back(&*i); } - // First, change the ABI of every function in the module. Greylisted + AttrBuilder B; + B.addAttribute(Attribute::ReadOnly).addAttribute(Attribute::ReadNone); + ReadOnlyNoneAttrs = AttributeSet::get(*Ctx, AttributeSet::FunctionIndex, B); + + // First, change the ABI of every function in the module. ABI-listed // functions keep their original ABI and get a wrapper function. for (std::vector::iterator i = FnsToInstrument.begin(), e = FnsToInstrument.end(); i != e; ++i) { Function &F = **i; - FunctionType *FT = F.getFunctionType(); - FunctionType *NewFT = getInstrumentedFunctionType(FT); - // If the function types are the same (i.e. void()), we don't need to do - // anything here. - if (FT != NewFT) { - switch (getInstrumentedABI(&F)) { - case IA_Args: { + + if (FT->getNumParams() == 0 && !FT->isVarArg() && + FT->getReturnType()->isVoidTy()) + continue; + + if (isInstrumented(&F)) { + if (getInstrumentedABI() == IA_Args) { + FunctionType *NewFT = getArgsFunctionType(FT); Function *NewF = Function::Create(NewFT, F.getLinkage(), "", &M); - NewF->setCallingConv(F.getCallingConv()); - NewF->setAttributes(F.getAttributes().removeAttributes( - *Ctx, AttributeSet::ReturnIndex, + NewF->copyAttributesFrom(&F); + NewF->removeAttributes( + AttributeSet::ReturnIndex, AttributeFuncs::typeIncompatible(NewFT->getReturnType(), - AttributeSet::ReturnIndex))); + AttributeSet::ReturnIndex)); for (Function::arg_iterator FArg = F.arg_begin(), NewFArg = NewF->arg_begin(), FArgEnd = F.arg_end(); @@ -358,41 +437,63 @@ bool DataFlowSanitizer::runOnModule(Module &M) { NewF->takeName(&F); F.eraseFromParent(); *i = NewF; - break; } - case IA_MemOnly: { - assert(!FT->isVarArg() && "varargs not handled here yet"); - assert(getDefaultInstrumentedABI() == IA_Args); - Function *NewF = - Function::Create(NewFT, GlobalValue::LinkOnceODRLinkage, - std::string("dfsw$") + F.getName(), &M); - NewF->setCallingConv(F.getCallingConv()); - NewF->setAttributes(F.getAttributes()); + // Hopefully, nobody will try to indirectly call a vararg + // function... yet. + } else if (FT->isVarArg()) { + UnwrappedFnMap[&F] = &F; + *i = 0; + } else { + // Build a wrapper function for F. The wrapper simply calls F, and is + // added to FnsToInstrument so that any instrumentation according to its + // WrapperKind is done in the second pass below. + FunctionType *NewFT = getInstrumentedABI() == IA_Args + ? getArgsFunctionType(FT) + : FT; + Function *NewF = + Function::Create(NewFT, GlobalValue::LinkOnceODRLinkage, + std::string("dfsw$") + F.getName(), &M); + NewF->copyAttributesFrom(&F); + NewF->removeAttributes( + AttributeSet::ReturnIndex, + AttributeFuncs::typeIncompatible(NewFT->getReturnType(), + AttributeSet::ReturnIndex)); + if (getInstrumentedABI() == IA_TLS) + NewF->removeAttributes(AttributeSet::FunctionIndex, + ReadOnlyNoneAttrs); - BasicBlock *BB = BasicBlock::Create(*Ctx, "entry", NewF); - std::vector Args; - unsigned n = FT->getNumParams(); - for (Function::arg_iterator i = NewF->arg_begin(); n != 0; ++i, --n) - Args.push_back(&*i); - CallInst *CI = CallInst::Create(&F, Args, "", BB); - if (FT->getReturnType()->isVoidTy()) - ReturnInst::Create(*Ctx, BB); - else { - Value *InsVal = InsertValueInst::Create( - UndefValue::get(NewFT->getReturnType()), CI, 0, "", BB); - Value *InsShadow = - InsertValueInst::Create(InsVal, ZeroShadow, 1, "", BB); - ReturnInst::Create(*Ctx, InsShadow, BB); - } + BasicBlock *BB = BasicBlock::Create(*Ctx, "entry", NewF); + std::vector Args; + unsigned n = FT->getNumParams(); + for (Function::arg_iterator ai = NewF->arg_begin(); n != 0; ++ai, --n) + Args.push_back(&*ai); + CallInst *CI = CallInst::Create(&F, Args, "", BB); + if (FT->getReturnType()->isVoidTy()) + ReturnInst::Create(*Ctx, BB); + else + ReturnInst::Create(*Ctx, CI, BB); - Value *WrappedFnCst = - ConstantExpr::getBitCast(NewF, PointerType::getUnqual(FT)); - F.replaceAllUsesWith(WrappedFnCst); - UnwrappedFnMap[WrappedFnCst] = &F; - break; - } - default: - break; + Value *WrappedFnCst = + ConstantExpr::getBitCast(NewF, PointerType::getUnqual(FT)); + F.replaceAllUsesWith(WrappedFnCst); + UnwrappedFnMap[WrappedFnCst] = &F; + *i = NewF; + + if (!F.isDeclaration()) { + // This function is probably defining an interposition of an + // uninstrumented function and hence needs to keep the original ABI. + // But any functions it may call need to use the instrumented ABI, so + // we instrument it in a mode which preserves the original ABI. + FnsWithNativeABI.insert(&F); + + // This code needs to rebuild the iterators, as they may be invalidated + // by the push_back, taking care that the new range does not include + // any functions added by this code. + size_t N = i - FnsToInstrument.begin(), + Count = e - FnsToInstrument.begin(); + FnsToInstrument.push_back(&F); + i = FnsToInstrument.begin() + N; + e = FnsToInstrument.begin() + Count; } } } @@ -400,12 +501,12 @@ bool DataFlowSanitizer::runOnModule(Module &M) { for (std::vector::iterator i = FnsToInstrument.begin(), e = FnsToInstrument.end(); i != e; ++i) { - if ((*i)->isDeclaration()) + if (!*i || (*i)->isDeclaration()) continue; removeUnreachableBlocks(**i); - DFSanFunction DFSF(*this, *i); + DFSanFunction DFSF(*this, *i, FnsWithNativeABI.count(*i)); // DFSanVisitor may create new basic blocks, which confuses df_iterator. // Build a copy of the list before iterating over it. @@ -433,6 +534,10 @@ bool DataFlowSanitizer::runOnModule(Module &M) { } } + // We will not necessarily be able to compute the shadow for every phi node + // until we have visited every block. Therefore, the code that handles phi + // nodes adds them to the PHIFixups list so that they can be properly + // handled here. for (std::vector >::iterator i = DFSF.PHIFixups.begin(), e = DFSF.PHIFixups.end(); @@ -479,6 +584,8 @@ Value *DFSanFunction::getShadow(Value *V) { Value *&Shadow = ValShadowMap[V]; if (!Shadow) { if (Argument *A = dyn_cast(V)) { + if (IsNativeABI) + return DFS.ZeroShadow; switch (IA) { case DataFlowSanitizer::IA_TLS: { Value *ArgTLSPtr = getArgTLSPtr(); @@ -495,11 +602,9 @@ Value *DFSanFunction::getShadow(Value *V) { while (ArgIdx--) ++i; Shadow = i; + assert(Shadow->getType() == DFS.ShadowTy); break; } - default: - Shadow = DFS.ZeroShadow; - break; } } else { Shadow = DFS.ZeroShadow; @@ -866,7 +971,7 @@ void DFSanVisitor::visitMemTransferInst(MemTransferInst &I) { } void DFSanVisitor::visitReturnInst(ReturnInst &RI) { - if (RI.getReturnValue()) { + if (!DFSF.IsNativeABI && RI.getReturnValue()) { switch (DFSF.IA) { case DataFlowSanitizer::IA_TLS: { Value *S = DFSF.getShadow(RI.getReturnValue()); @@ -884,8 +989,6 @@ void DFSanVisitor::visitReturnInst(ReturnInst &RI) { RI.setOperand(0, InsShadow); break; } - default: - break; } } } @@ -897,19 +1000,91 @@ void DFSanVisitor::visitCallSite(CallSite CS) { return; } + IRBuilder<> IRB(CS.getInstruction()); + DenseMap::iterator i = DFSF.DFS.UnwrappedFnMap.find(CS.getCalledValue()); if (i != DFSF.DFS.UnwrappedFnMap.end()) { - CS.setCalledFunction(i->second); - DFSF.setShadow(CS.getInstruction(), DFSF.DFS.ZeroShadow); - return; - } + Function *F = i->second; + switch (DFSF.DFS.getWrapperKind(F)) { + case DataFlowSanitizer::WK_Warning: { + CS.setCalledFunction(F); + IRB.CreateCall(DFSF.DFS.DFSanUnimplementedFn, + IRB.CreateGlobalStringPtr(F->getName())); + DFSF.setShadow(CS.getInstruction(), DFSF.DFS.ZeroShadow); + return; + } + case DataFlowSanitizer::WK_Discard: { + CS.setCalledFunction(F); + DFSF.setShadow(CS.getInstruction(), DFSF.DFS.ZeroShadow); + return; + } + case DataFlowSanitizer::WK_Functional: { + CS.setCalledFunction(F); + visitOperandShadowInst(*CS.getInstruction()); + return; + } + case DataFlowSanitizer::WK_Custom: { + // Don't try to handle invokes of custom functions, it's too complicated. + // Instead, invoke the dfsw$ wrapper, which will in turn call the __dfsw_ + // wrapper. + if (CallInst *CI = dyn_cast(CS.getInstruction())) { + FunctionType *FT = F->getFunctionType(); + FunctionType *CustomFT = DFSF.DFS.getCustomFunctionType(FT); + std::string CustomFName = "__dfsw_"; + CustomFName += F->getName(); + Constant *CustomF = + DFSF.DFS.Mod->getOrInsertFunction(CustomFName, CustomFT); + if (Function *CustomFn = dyn_cast(CustomF)) { + CustomFn->copyAttributesFrom(F); - IRBuilder<> IRB(CS.getInstruction()); + // Custom functions returning non-void will write to the return label. + if (!FT->getReturnType()->isVoidTy()) { + CustomFn->removeAttributes(AttributeSet::FunctionIndex, + DFSF.DFS.ReadOnlyNoneAttrs); + } + } + + std::vector Args; + + CallSite::arg_iterator i = CS.arg_begin(); + for (unsigned n = FT->getNumParams(); n != 0; ++i, --n) + Args.push_back(*i); + + i = CS.arg_begin(); + for (unsigned n = FT->getNumParams(); n != 0; ++i, --n) + Args.push_back(DFSF.getShadow(*i)); + + if (!FT->getReturnType()->isVoidTy()) { + if (!DFSF.LabelReturnAlloca) { + DFSF.LabelReturnAlloca = + new AllocaInst(DFSF.DFS.ShadowTy, "labelreturn", + DFSF.F->getEntryBlock().begin()); + } + Args.push_back(DFSF.LabelReturnAlloca); + } + + CallInst *CustomCI = IRB.CreateCall(CustomF, Args); + CustomCI->setCallingConv(CI->getCallingConv()); + CustomCI->setAttributes(CI->getAttributes()); + + if (!FT->getReturnType()->isVoidTy()) { + LoadInst *LabelLoad = IRB.CreateLoad(DFSF.LabelReturnAlloca); + DFSF.setShadow(CustomCI, LabelLoad); + } + + CI->replaceAllUsesWith(CustomCI); + CI->eraseFromParent(); + return; + } + break; + } + } + } FunctionType *FT = cast( CS.getCalledValue()->getType()->getPointerElementType()); - if (DFSF.DFS.getDefaultInstrumentedABI() == DataFlowSanitizer::IA_TLS) { + if (DFSF.DFS.getInstrumentedABI() == DataFlowSanitizer::IA_TLS) { for (unsigned i = 0, n = FT->getNumParams(); i != n; ++i) { IRB.CreateStore(DFSF.getShadow(CS.getArgument(i)), DFSF.getArgTLS(i, CS.getInstruction())); @@ -930,7 +1105,7 @@ void DFSanVisitor::visitCallSite(CallSite CS) { Next = CS->getNextNode(); } - if (DFSF.DFS.getDefaultInstrumentedABI() == DataFlowSanitizer::IA_TLS) { + if (DFSF.DFS.getInstrumentedABI() == DataFlowSanitizer::IA_TLS) { IRBuilder<> NextIRB(Next); LoadInst *LI = NextIRB.CreateLoad(DFSF.getRetvalTLS()); DFSF.SkipInsts.insert(LI); @@ -940,8 +1115,8 @@ void DFSanVisitor::visitCallSite(CallSite CS) { // Do all instrumentation for IA_Args down here to defer tampering with the // CFG in a way that SplitEdge may be able to detect. - if (DFSF.DFS.getDefaultInstrumentedABI() == DataFlowSanitizer::IA_Args) { - FunctionType *NewFT = DFSF.DFS.getInstrumentedFunctionType(FT); + if (DFSF.DFS.getInstrumentedABI() == DataFlowSanitizer::IA_Args) { + FunctionType *NewFT = DFSF.DFS.getArgsFunctionType(FT); Value *Func = IRB.CreateBitCast(CS.getCalledValue(), PointerType::getUnqual(NewFT)); std::vector Args; diff --git a/test/Instrumentation/DataFlowSanitizer/abilist.ll b/test/Instrumentation/DataFlowSanitizer/abilist.ll new file mode 100644 index 00000000000..d8bce82ce84 --- /dev/null +++ b/test/Instrumentation/DataFlowSanitizer/abilist.ll @@ -0,0 +1,47 @@ +; RUN: opt < %s -dfsan -dfsan-args-abi -dfsan-abilist=%s.txt -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" + +; CHECK: i32 @discard(i32 %a, i32 %b) +define i32 @discard(i32 %a, i32 %b) { + ret i32 0 +} + +; CHECK: i32 @functional(i32 %a, i32 %b) +define i32 @functional(i32 %a, i32 %b) { + %c = add i32 %a, %b + ret i32 %c +} + +declare void @custom1(i32 %a, i32 %b) + +declare i32 @custom2(i32 %a, i32 %b) + +; CHECK: @f +define void @f() { + ; CHECK: %[[LABELRETURN:.*]] = alloca i16 + + ; CHECK: call void @__dfsw_custom1(i32 1, i32 2, i16 0, i16 0) + call void @custom1(i32 1, i32 2) + + ; CHECK: call i32 @__dfsw_custom2(i32 1, i32 2, i16 0, i16 0, i16* %[[LABELRETURN]]) + call i32 @custom2(i32 1, i32 2) + + ret void +} + +; CHECK: define linkonce_odr { i32, i16 } @"dfsw$custom2"(i32, i32, i16, i16) +; CHECK: %[[LABELRETURN2:.*]] = alloca i16 +; CHECK: %[[RV:.*]] = call i32 @__dfsw_custom2 +; CHECK: %[[RVSHADOW:.*]] = load i16* %[[LABELRETURN2]] +; CHECK: insertvalue {{.*}}[[RV]], 0 +; CHECK: insertvalue {{.*}}[[RVSHADOW]], 1 +; CHECK: ret { i32, i16 } + +; CHECK: @g +define i32 (i32, i32)* @g() { + ; CHECK: ret {{.*}} @"dfsw$custom2" + ret i32 (i32, i32)* @custom2 +} + +; CHECK: declare void @__dfsw_custom1(i32, i32, i16, i16) +; CHECK: declare i32 @__dfsw_custom2(i32, i32, i16, i16, i16*) diff --git a/test/Instrumentation/DataFlowSanitizer/abilist.ll.txt b/test/Instrumentation/DataFlowSanitizer/abilist.ll.txt new file mode 100644 index 00000000000..fec2a509e0a --- /dev/null +++ b/test/Instrumentation/DataFlowSanitizer/abilist.ll.txt @@ -0,0 +1,11 @@ +fun:discard=uninstrumented +fun:discard=discard + +fun:functional=uninstrumented +fun:functional=functional + +fun:custom1=uninstrumented +fun:custom1=custom + +fun:custom2=uninstrumented +fun:custom2=custom