// // Method: getDSNodeHandle() // // Description: // This method looks up the DSNodeHandle for a given LLVM value. The context // of the value is the specified function, although if it is a global value, // the DSNodeHandle may exist within the global DSGraph. // // Return value: // A DSNodeHandle for the value is returned. This DSNodeHandle could either // be in the function's DSGraph or from the GlobalsGraph. Note that the // DSNodeHandle may represent a NULL DSNode. // DSNodeHandle CompleteChecks::getDSNodeHandle (const Value * V, const Function * F) { // // Get access to the points-to results. // EQTDDataStructures & dsaPass = getAnalysis<EQTDDataStructures>(); // // Ensure that the function has a DSGraph // assert (dsaPass.hasDSGraph(*F) && "No DSGraph for function!\n"); // // Lookup the DSNode for the value in the function's DSGraph. // DSGraph * TDG = dsaPass.getDSGraph(*F); DSNodeHandle DSH = TDG->getNodeForValue(V); // // If the value wasn't found in the function's DSGraph, then maybe we can // find the value in the globals graph. // if ((DSH.isNull()) && (isa<GlobalValue>(V))) { // // Try looking up this DSNode value in the globals graph. Note that // globals are put into equivalence classes; we may need to first find the // equivalence class to which our global belongs, find the global that // represents all globals in that equivalence class, and then look up the // DSNode Handle for *that* global. // DSGraph * GlobalsGraph = TDG->getGlobalsGraph (); DSH = GlobalsGraph->getNodeForValue(V); if (DSH.isNull()) { // // DSA does not currently handle global aliases. // if (!isa<GlobalAlias>(V)) { // // We have to dig into the globalEC of the DSGraph to find the DSNode. // const GlobalValue * GV = dyn_cast<GlobalValue>(V); const GlobalValue * Leader; Leader = GlobalsGraph->getGlobalECs().getLeaderValue(GV); DSH = GlobalsGraph->getNodeForValue(Leader); } } } return DSH; }
// // Method: getUnsafeAllocsFromABC() // // Description: // Find all memory objects that are both allocated on the stack and are not // proven to be indexed in a type-safe manner according to the static array // bounds checking pass. // // Notes: // This method saves its results be remembering the set of DSNodes which are // both on the stack and potentially indexed in a type-unsafe manner. // // FIXME: // This method only considers unsafe GEP instructions; it does not consider // unsafe call instructions or other instructions deemed unsafe by the array // bounds checking pass. // void ConvertUnsafeAllocas::getUnsafeAllocsFromABC(Module & M) { UnsafeAllocaNodeListBuilder Builder(budsPass, unsafeAllocaNodes); Builder.visit(M); #if 0 // Haohui: Disable it right now since nobody using the code std::map<BasicBlock *,std::set<Instruction*>*> UnsafeGEPMap= abcPass->UnsafeGetElemPtrs; std::map<BasicBlock *,std::set<Instruction*>*>::const_iterator bCurrent = UnsafeGEPMap.begin(), bEnd = UnsafeGEPMap.end(); for (; bCurrent != bEnd; ++bCurrent) { std::set<Instruction *> * UnsafeGetElemPtrs = bCurrent->second; std::set<Instruction *>::const_iterator iCurrent = UnsafeGetElemPtrs->begin(), iEnd = UnsafeGetElemPtrs->end(); for (; iCurrent != iEnd; ++iCurrent) { if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(*iCurrent)) { Value *pointerOperand = GEP->getPointerOperand(); DSGraph * TDG = budsPass->getDSGraph(*(GEP->getParent()->getParent())); DSNode *DSN = TDG->getNodeForValue(pointerOperand).getNode(); //FIXME DO we really need this ? markReachableAllocas(DSN); if (DSN && DSN->isAllocaNode() && !DSN->isNodeCompletelyFolded()) { unsafeAllocaNodes.push_back(DSN); } } else { //call instruction add the corresponding *iCurrent->dump(); //FIXME abort(); } } } #endif }
void visitGetElementPtrInst(GetElementPtrInst &GEP) { Value *pointerOperand = GEP.getPointerOperand(); DSGraph * TDG = budsPass->getDSGraph(*(GEP.getParent()->getParent())); DSNode *DSN = TDG->getNodeForValue(pointerOperand).getNode(); //FIXME DO we really need this ? markReachableAllocas(DSN); if (DSN && DSN->isAllocaNode() && !DSN->isNodeCompletelyFolded()) { unsafeAllocaNodes.push_back(DSN); } }
void PoolRegisterElimination::removeTypeSafeRegistrations (const char * name) { // // Scan through all uses of the registration function and see if it can be // safely removed. If so, schedule it for removal. // std::vector<CallInst*> toBeRemoved; Function * F = intrinsic->getIntrinsic(name).F; // // Look for and record all registrations that can be deleted. // for (Value::use_iterator UI=F->use_begin(), UE=F->use_end(); UI != UE; ++UI) { // // Get the pointer to the registered object. // CallInst * CI = cast<CallInst>(*UI); Value * Ptr = intrinsic->getValuePointer(CI); // Lookup the DSNode for the value in the function's DSGraph. // DSGraph * TDG = dsaPass->getDSGraph(*(CI->getParent()->getParent())); DSNodeHandle DSH = TDG->getNodeForValue(Ptr); assert ((!(DSH.isNull())) && "No DSNode for Value!\n"); // // If the DSNode is type-safe and is never used as an array, then there // will never be a need to look it up in a splay tree, so remove its // registration. // DSNode * N = DSH.getNode(); if(!N->isArrayNode() && TS->isTypeSafe(Ptr, F)){ toBeRemoved.push_back(CI); } } // // Update the statistics. // if (toBeRemoved.size()) { RemovedRegistration += toBeRemoved.size(); TypeSafeRegistrations += toBeRemoved.size(); } // // Remove the unnecesary registrations. // std::vector<CallInst*>::iterator it, end; for (it = toBeRemoved.begin(), end = toBeRemoved.end(); it != end; ++it) { (*it)->eraseFromParent(); } }
// // Method: insertHardDanglingPointers() // // Description: // Insert dangling pointer dereferences into the code. This is done by // finding instructions that store pointers to memory and free'ing those // pointers before the store. Subsequent loads and uses of the pointer will // cause a dangling pointer dereference. // // Return value: // true - The module was modified. // false - The module was left unmodified. // // Notes: // This code utilizes DSA to ensure that the pointer can point to heap // memory (although the pointer is allowed to alias global and stack memory). // bool FaultInjector::insertHardDanglingPointers (Function & F) { // // Ensure that we can get analysis information for this function. // if (!(TDPass->hasDSGraph(F))) return false; // // Scan through each instruction of the function looking for store // instructions that store a pointer to memory. Free the pointer right // before the store instruction. // DSGraph * DSG = TDPass->getDSGraph(F); for (Function::iterator fI = F.begin(), fE = F.end(); fI != fE; ++fI) { BasicBlock & BB = *fI; for (BasicBlock::iterator bI = BB.begin(), bE = BB.end(); bI != bE; ++bI) { Instruction * I = bI; // // Look to see if there is an instruction that stores a pointer to // memory. If so, then free the pointer before the store. // if (StoreInst * SI = dyn_cast<StoreInst>(I)) { if (isa<PointerType>(SI->getOperand(0)->getType())) { Value * Pointer = SI->getOperand(0); // // Check to ensure that the pointer aliases with the heap. If so, go // ahead and add the free. Note that we may introduce an invalid // free, but we're injecting errors, so I think that's okay. // DSNode * Node = DSG->getNodeForValue(Pointer).getNode(); if (Node && (Node->isHeapNode())) { // Skip if we should not insert a fault. if (!doFault()) continue; // // Print information about where the fault is being inserted. // printSourceInfo ("Hard dangling pointer", I); CallInst::Create (Free, Pointer, "", I); ++DPFaults; } } } } } return (DPFaults > 0); }
// // Function: processRuntimeCheck() // // Description: // Modify a run-time check so that its return value has the same DSNode as the // checked pointer. // // Inputs: // M - The module in which calls to the function live. // name - The name of the function for which direct calls should be processed. // arg - The argument index that contains the pointer which the run-time // check returns. // void StdLibDataStructures::processRuntimeCheck (Module & M, std::string name, unsigned arg) { // // Get a pointer to the function. // Function* F = M.getFunction (name); // // If the function doesn't exist, then there is no work to do. // if (!F) return; // // Scan through all direct calls to the function (there should only be direct // calls) and process each one. // for (Value::use_iterator ii = F->use_begin(), ee = F->use_end(); ii != ee; ++ii) { if (CallInst* CI = dyn_cast<CallInst>(*ii)) { if (CI->getCalledValue() == F) { DSGraph* Graph = getDSGraph(*CI->getParent()->getParent()); DSNodeHandle & RetNode = Graph->getNodeForValue(CI); DSNodeHandle & ArgNode = Graph->getNodeForValue(CI->getArgOperand(arg)); RetNode.mergeWith(ArgNode); } } } // // Erase the DSCallSites for this function. This should prevent other DSA // passes from making the DSNodes passed to/returned from the function // from becoming Incomplete or External. // eraseCallsTo (F); return; }
void initialize(const Module *M, const DataStructures *DS) { parseValue(M); assert(V && "Failed to parse value?"); if (isa<GlobalValue>(V)) { DSGraph *G = DS->getGlobalsGraph(); assert(G->hasNodeForValue(V) && "Node not in specified graph!"); NH = G->getNodeForValue(V); } else { assert(F && "No function?"); DSGraph *G = DS->getDSGraph(*F); assert(G->hasNodeForValue(V) && "Node not in specified graph!"); NH = G->getNodeForValue(V); } // Handle offsets, if any // For each offset in the offsets vector, follow the link at that offset for (OffsetVectorTy::const_iterator I = offsets.begin(), E = offsets.end(); I != E; ++I ) { assert(!NH.isNull() && "Null NodeHandle?"); assert(NH.hasLink(*I) && "Handle doesn't have link?"); // Follow the offset NH = NH.getLink(*I); } }
bool CSDataRando::runOnModule(Module &M) { DSA = &getAnalysis<BUMarkDoNotEncrypt>(); MaskTy = TypeBuilder<mask_t, false>::get(M.getContext()); FunctionWrappers &FW = getAnalysis<FunctionWrappers>(); { // Gather statistics on the globals DenseMap<const DSNode*, unsigned int> GlobalClassSizes; DSGraph *GG = DSA->getGlobalsGraph(); for (GlobalVariable &GV : M.getGlobalList()) { if (!(GV.isDeclaration() || PointerEquivalenceAnalysis::shouldIgnoreGlobal(GV))) { GlobalClassSizes[GG->getNodeForValue(&GV).getNode()] += 1; } } NumGlobalECs = GlobalClassSizes.size(); for (auto i : GlobalClassSizes) { if (i.second > MaxSizeGlobalEC) { MaxSizeGlobalEC = i.second; } } } findGlobalNodes(M); findArgNodes(M); // Find which functions may need cloning. If we have a DSGraph for the // function, consider it a candidate for cloning. std::vector<Function *> OriginalFunctions; for (Function &F : M) { if (!F.isDeclaration() && DSA->hasDSGraph(F)) { OriginalFunctions.push_back(&F); } } // Perform cloning of the original functions Function *Main = M.getFunction("main"); for (Function *Original : OriginalFunctions) { // Handle the main function if (Main && Original == Main) { // Never clone main OldToNewFuncMap[Original] = nullptr; // If main has no uses then we can encrypt the arguments to main. To allow // the arg nodes to be encrypted we clear ArgNodes. if (Original->uses().begin() == Original->uses().end()) { FunctionInfo[Original].ArgNodes.clear(); } continue; } // Maybe make a clone, if a clone was not made nullptr is returned. OldToNewFuncMap[Original] = makeFunctionClone(Original); } // File to potentially print diagnostic information std::unique_ptr<tool_output_file> Out(nullptr); // If we will be printing diagnostic information, open the file if (!PointerEquivalenceAnalysis::PrintEquivalenceClassesTo.empty()) { std::error_code error; Out.reset(new tool_output_file(PointerEquivalenceAnalysis::PrintEquivalenceClassesTo, error, sys::fs::F_None)); if (error) { Out.release(); } } // Perform randomization DataRandomizer DR(M); RandomNumberGenerator *RNG = M.createRNG(this); FuncInfo empty; ContextSensitivePEA GGPEA(*RNG, M.getContext(), empty, *DSA->getGlobalsGraph()); DR.encryptGlobalVariables(M, GGPEA); // All original functions with DSGraphs will be in OldToNewFuncMap. If a clone // was not made, then the entry will map to nullptr. for (auto i : OldToNewFuncMap) { Function *Original = i.first; Function *Clone = i.second; FuncInfo &FI = FunctionInfo[Original]; DSGraph *Graph = DSA->getDSGraph(*Original); if (Clone) { // Perform randomization of the cloned function CloneFunctionPEA CP(*RNG, M.getContext(), FI, *Graph, &GGPEA); DR.instrumentMemoryOperations(*Clone, CP, NULL); DR.wrapLibraryFunctions(*Clone, CP, FW); replaceWithClones(Clone, FI, CP, Graph); if (Out.get()) { // Add all Instructions before dumping to make the dump more complete. addAllInstructions(Clone, CP); Out->os() << "*** Equivalence classes for: " << Clone->getName() << " ***\n"; CP.printEquivalenceClasses(Out->os()); Out->os() << "*** End of equivalence classes for: " << Clone->getName() << " ***\n"; } } // Perform randomization of the original function FunctionPEA FP(*RNG, M.getContext(), FI, *Graph, &GGPEA, !Clone); DR.instrumentMemoryOperations(*Original, FP, NULL); DR.wrapLibraryFunctions(*Original, FP, FW); replaceWithClones(Original, FI, FP, Graph); // Encrypt main args using the main function's PEA if (Main && Original == Main) { DR.encryptMainArgs(M, FP, FW); } if (Out.get()) { // Add all Instructions before dumping to make the dump more complete. addAllInstructions(Original, FP); Out->os() << "*** Equivalence classes for: " << Original->getName() << " ***\n"; FP.printEquivalenceClasses(Out->os()); Out->os() << "*** End of equivalence classes for: " << Original->getName() << " ***\n"; } } // Replace remaining uses of original functions with clones. replaceOriginalsWithClones(); if (Out.get()) { Out->os() << "*** Equivalence classes for global variables ***\n"; GGPEA.printEquivalenceClasses(Out->os()); Out->os() << "*** End of equivalence classes for global variables ***\n"; Out->keep(); } return true; }
/// InlineCallersIntoGraph - Inline all of the callers of the specified DS graph /// into it, then recompute completeness of nodes in the resultant graph. void TDDataStructures::InlineCallersIntoGraph(DSGraph* DSG) { // Inline caller graphs into this graph. First step, get the list of call // sites that call into this graph. std::vector<CallerCallEdge> EdgesFromCaller; std::map<DSGraph*, std::vector<CallerCallEdge> >::iterator CEI = CallerEdges.find(DSG); if (CEI != CallerEdges.end()) { std::swap(CEI->second, EdgesFromCaller); CallerEdges.erase(CEI); } // Sort the caller sites to provide a by-caller-graph ordering. std::sort(EdgesFromCaller.begin(), EdgesFromCaller.end()); // Merge information from the globals graph into this graph. FIXME: This is // stupid. Instead of us cloning information from the GG into this graph, // then having RemoveDeadNodes clone it back, we should do all of this as a // post-pass over all of the graphs. We need to take cloning out of // removeDeadNodes and gut removeDeadNodes at the same time first though. :( { DSGraph* GG = DSG->getGlobalsGraph(); ReachabilityCloner RC(DSG, GG, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); for (DSScalarMap::global_iterator GI = DSG->getScalarMap().global_begin(), E = DSG->getScalarMap().global_end(); GI != E; ++GI) RC.getClonedNH(GG->getNodeForValue(*GI)); } DEBUG(errs() << "[TD] Inlining callers into '" << DSG->getFunctionNames() << "'\n"); // Iteratively inline caller graphs into this graph. while (!EdgesFromCaller.empty()) { DSGraph* CallerGraph = EdgesFromCaller.back().CallerGraph; // Iterate through all of the call sites of this graph, cloning and merging // any nodes required by the call. ReachabilityCloner RC(DSG, CallerGraph, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); // Inline all call sites from this caller graph. do { const DSCallSite &CS = *EdgesFromCaller.back().CS; const Function &CF = *EdgesFromCaller.back().CalledFunction; DEBUG(errs() << " [TD] Inlining graph into Fn '" << CF.getNameStr() << "' from "); if (CallerGraph->getReturnNodes().empty()) { DEBUG(errs() << "SYNTHESIZED INDIRECT GRAPH"); } else { DEBUG(errs() << "Fn '" << CS.getCallSite().getInstruction()-> getParent()->getParent()->getNameStr() << "'"); } DEBUG(errs() << ": " << CF.getFunctionType()->getNumParams() << " args\n"); // Get the formal argument and return nodes for the called function and // merge them with the cloned subgraph. DSCallSite T1 = DSG->getCallSiteForArguments(CF); RC.mergeCallSite(T1, CS); ++NumTDInlines; EdgesFromCaller.pop_back(); } while (!EdgesFromCaller.empty() && EdgesFromCaller.back().CallerGraph == CallerGraph); } { DSGraph* GG = DSG->getGlobalsGraph(); ReachabilityCloner RC(GG, DSG, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); for (DSScalarMap::global_iterator GI = DSG->getScalarMap().global_begin(), E = DSG->getScalarMap().global_end(); GI != E; ++GI) RC.getClonedNH(DSG->getNodeForValue(*GI)); } // Next, now that this graph is finalized, we need to recompute the // incompleteness markers for this graph and remove unreachable nodes. DSG->maskIncompleteMarkers(); // If any of the functions has incomplete incoming arguments, don't mark any // of them as complete. bool HasIncompleteArgs = false; for (DSGraph::retnodes_iterator I = DSG->retnodes_begin(), E = DSG->retnodes_end(); I != E; ++I) if (ArgsRemainIncomplete.count(I->first)) { HasIncompleteArgs = true; break; } // Recompute the Incomplete markers. Depends on whether args are complete unsigned Flags = HasIncompleteArgs ? DSGraph::MarkFormalArgs : DSGraph::IgnoreFormalArgs; Flags |= DSGraph::IgnoreGlobals | DSGraph::MarkVAStart; DSG->markIncompleteNodes(Flags); // Delete dead nodes. Treat globals that are unreachable as dead also. DSG->removeDeadNodes(DSGraph::RemoveUnreachableGlobals); // We are done with computing the current TD Graph! Finally, before we can // finish processing this function, we figure out which functions it calls and // records these call graph edges, so that we have them when we process the // callee graphs. if (DSG->fc_begin() == DSG->fc_end()) return; // Loop over all the call sites and all the callees at each call site, and add // edges to the CallerEdges structure for each callee. for (DSGraph::fc_iterator CI = DSG->fc_begin(), E = DSG->fc_end(); CI != E; ++CI) { // Handle direct calls efficiently. if (CI->isDirectCall()) { if (!CI->getCalleeFunc()->isDeclaration() && !DSG->getReturnNodes().count(CI->getCalleeFunc())) CallerEdges[getOrFetchDSGraph(CI->getCalleeFunc())] .push_back(CallerCallEdge(DSG, &*CI, CI->getCalleeFunc())); continue; } Instruction *CallI = CI->getCallSite().getInstruction(); // For each function in the invoked function list at this call site... calleeTy::iterator IPI = callee.begin(CallI), IPE = callee.end(CallI); // Skip over all calls to this graph (SCC calls). while (IPI != IPE && getDSGraph(*IPI) == DSG) ++IPI; // All SCC calls? if (IPI == IPE) continue; const Function *FirstCallee = *IPI; ++IPI; // Skip over more SCC calls. while (IPI != IPE && getDSGraph(*IPI) == DSG) ++IPI; // If there is exactly one callee from this call site, remember the edge in // CallerEdges. if (IPI == IPE) { if (!FirstCallee->isDeclaration()) CallerEdges[getOrFetchDSGraph(FirstCallee)] .push_back(CallerCallEdge(DSG, &*CI, FirstCallee)); continue; } // Otherwise, there are multiple callees from this call site, so it must be // an indirect call. Chances are that there will be other call sites with // this set of targets. If so, we don't want to do M*N inlining operations, // so we build up a new, private, graph that represents the calls of all // calls to this set of functions. std::vector<const Function*> Callees; for (calleeTy::iterator I = callee.begin(CallI), E = callee.end(CallI); I != E; ++I) if (!(*I)->isDeclaration()) Callees.push_back(*I); std::sort(Callees.begin(), Callees.end()); std::map<std::vector<const Function*>, DSGraph*>::iterator IndCallRecI = IndCallMap.lower_bound(Callees); DSGraph *IndCallGraph; // If we already have this graph, recycle it. if (IndCallRecI != IndCallMap.end() && IndCallRecI->first == Callees) { DEBUG(errs() << " [TD] *** Reuse of indcall graph for " << Callees.size() << " callees!\n"); IndCallGraph = IndCallRecI->second; } else { // Otherwise, create a new DSGraph to represent this. IndCallGraph = new DSGraph(DSG->getGlobalECs(), DSG->getTargetData(), GlobalsGraph); // Make a nullary dummy call site, which will eventually get some content // merged into it. The actual callee function doesn't matter here, so we // just pass it something to keep the ctor happy. std::vector<DSNodeHandle> ArgDummyVec; DSCallSite DummyCS(CI->getCallSite(), DSNodeHandle(), Callees[0]/*dummy*/, ArgDummyVec); IndCallGraph->getFunctionCalls().push_back(DummyCS); IndCallRecI = IndCallMap.insert(IndCallRecI, std::make_pair(Callees, IndCallGraph)); // Additionally, make sure that each of the callees inlines this graph // exactly once. DSCallSite *NCS = &IndCallGraph->getFunctionCalls().front(); for (unsigned i = 0, e = Callees.size(); i != e; ++i) { DSGraph* CalleeGraph = getDSGraph(Callees[i]); if (CalleeGraph != DSG) CallerEdges[CalleeGraph].push_back(CallerCallEdge(IndCallGraph, NCS, Callees[i])); } } // Now that we know which graph to use for this, merge the caller // information into the graph, based on information from the call site. ReachabilityCloner RC(IndCallGraph, DSG, 0); RC.mergeCallSite(IndCallGraph->getFunctionCalls().front(), *CI); } }
void RTAssociate::replaceCall(CallSite CS, FuncInfo& FI, DataStructures* DS) { const Function *CF = CS.getCalledFunction(); Instruction *TheCall = CS.getInstruction(); // If the called function is casted from one function type to another, peer // into the cast instruction and pull out the actual function being called. if (ConstantExpr *CE = dyn_cast<ConstantExpr>(CS.getCalledValue())) if (CE->getOpcode() == Instruction::BitCast && isa<Function>(CE->getOperand(0))) CF = cast<Function>(CE->getOperand(0)); if (isa<InlineAsm>(TheCall->getOperand(0))) { errs() << "INLINE ASM: ignoring. Hoping that's safe.\n"; return; } // Ignore calls to NULL pointers. if (isa<ConstantPointerNull>(CS.getCalledValue())) { errs() << "WARNING: Ignoring call using NULL function pointer.\n"; return; } // We need to figure out which local pool descriptors correspond to the pool // descriptor arguments passed into the function call. Calculate a mapping // from callee DSNodes to caller DSNodes. We construct a partial isomophism // between the graphs to figure out which pool descriptors need to be passed // in. The roots of this mapping is found from arguments and return values. // DSGraph::NodeMapTy NodeMapping; Instruction *NewCall; Value *NewCallee; std::vector<const DSNode*> ArgNodes; DSGraph *CalleeGraph; // The callee graph // For indirect callees, find any callee since all DS graphs have been // merged. if (CF) { // Direct calls are nice and simple. DEBUG(errs() << " Handling direct call: " << *TheCall); FuncInfo *CFI = getFuncInfo(CF); if (CFI == 0 || CFI->Clone == 0) // Nothing to transform... return; NewCallee = CFI->Clone; ArgNodes = CFI->ArgNodes; assert ((DS->hasDSGraph (*CF)) && "Function has no ECGraph!\n"); CalleeGraph = DS->getDSGraph(*CF); } else { DEBUG(errs() << " Handling indirect call: " << *TheCall); // Here we fill in CF with one of the possible called functions. Because we // merged together all of the arguments to all of the functions in the // equivalence set, it doesn't really matter which one we pick. // (If the function was cloned, we have to map the cloned call instruction // in CS back to the original call instruction.) Instruction *OrigInst = cast<Instruction>(FI.getOldValueIfAvailable(CS.getInstruction())); DSCallGraph::callee_iterator I = DS->getCallGraph().callee_begin(CS); if (I != DS->getCallGraph().callee_end(CS)) CF = *I; // If we didn't find the callee in the constructed call graph, try // checking in the DSNode itself. // This isn't ideal as it means that this call site didn't have inlining // happen. if (!CF) { DSGraph* dg = DS->getDSGraph(*OrigInst->getParent()->getParent()); DSNode* d = dg->getNodeForValue(OrigInst->getOperand(0)).getNode(); assert (d && "No DSNode!\n"); std::vector<const Function*> g; d->addFullFunctionList(g); if (g.size()) { EquivalenceClasses< const GlobalValue *> & EC = dg->getGlobalECs(); for(std::vector<const Function*>::const_iterator ii = g.begin(), ee = g.end(); !CF && ii != ee; ++ii) { for (EquivalenceClasses<const GlobalValue *>::member_iterator MI = EC.findLeader(*ii); MI != EC.member_end(); ++MI) // Loop over members in this set. if ((CF = dyn_cast<Function>(*MI))) { break; } } } } // // Do an assert unless we're bugpointing something. // // if ((UsingBugpoint) && (!CF)) return; if (!CF) errs() << "No Graph for CallSite in " << TheCall->getParent()->getParent()->getName().str() << " originally " << OrigInst->getParent()->getParent()->getName().str() << "\n"; assert (CF && "No call graph info"); // Get the common graph for the set of functions this call may invoke. // if (UsingBugpoint && (!(Graphs.hasDSGraph(*CF)))) return; assert ((DS->hasDSGraph(*CF)) && "Function has no DSGraph!\n"); CalleeGraph = DS->getDSGraph(*CF); #ifndef NDEBUG // Verify that all potential callees at call site have the same DS graph. DSCallGraph::callee_iterator E = DS->getCallGraph().callee_end(CS); for (; I != E; ++I) if (!(*I)->isDeclaration()) assert(CalleeGraph == DS->getDSGraph(**I) && "Callees at call site do not have a common graph!"); #endif // Find the DS nodes for the arguments that need to be added, if any. FuncInfo *CFI = getFuncInfo(CF); assert(CFI && "No function info for callee at indirect call?"); ArgNodes = CFI->ArgNodes; if (ArgNodes.empty()) return; // No arguments to add? Transformation is a noop! // Cast the function pointer to an appropriate type! std::vector<Type*> ArgTys(ArgNodes.size(), PoolDescPtrTy); for (CallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end(); I != E; ++I) ArgTys.push_back((*I)->getType()); FunctionType *FTy = FunctionType::get(TheCall->getType(), ArgTys, false); PointerType *PFTy = PointerType::getUnqual(FTy); // If there are any pool arguments cast the func ptr to the right type. NewCallee = CastInst::CreatePointerCast(CS.getCalledValue(), PFTy, "tmp", TheCall); } Function::const_arg_iterator FAI = CF->arg_begin(), E = CF->arg_end(); CallSite::arg_iterator AI = CS.arg_begin(), AE = CS.arg_end(); for ( ; FAI != E && AI != AE; ++FAI, ++AI) if (!isa<Constant>(*AI)) DSGraph::computeNodeMapping(CalleeGraph->getNodeForValue(FAI), FI.getDSNodeHFor(*AI), NodeMapping, false); assert(AI == AE && "Varargs calls not handled yet!"); // Map the return value as well... if (isa<PointerType>(TheCall->getType())) DSGraph::computeNodeMapping(CalleeGraph->getReturnNodeFor(*CF), FI.getDSNodeHFor(TheCall), NodeMapping, false); // Okay, now that we have established our mapping, we can figure out which // pool descriptors to pass in... std::vector<Value*> Args; for (unsigned i = 0, e = ArgNodes.size(); i != e; ++i) { Value *ArgVal = Constant::getNullValue(PoolDescPtrTy); if (NodeMapping.count(ArgNodes[i])) if (DSNode *LocalNode = NodeMapping[ArgNodes[i]].getNode()) if (FI.PoolDescriptors.count(LocalNode)) ArgVal = FI.PoolDescriptors.find(LocalNode)->second; if (isa<Constant > (ArgVal) && cast<Constant > (ArgVal)->isNullValue()) errs() << "WARNING: NULL POOL ARGUMENTS ARE PASSED IN!\n"; Args.push_back(ArgVal); } // Add the rest of the arguments... Args.insert(Args.end(), CS.arg_begin(), CS.arg_end()); // // There are circumstances where a function is casted to another type and // then called (que horible). We need to perform a similar cast if the // type doesn't match the number of arguments. // if (Function * NewFunction = dyn_cast<Function>(NewCallee)) { FunctionType * NewCalleeType = NewFunction->getFunctionType(); if (NewCalleeType->getNumParams() != Args.size()) { std::vector<Type *> Types; Type * FuncTy = FunctionType::get (NewCalleeType->getReturnType(), Types, true); FuncTy = PointerType::getUnqual (FuncTy); NewCallee = new BitCastInst (NewCallee, FuncTy, "", TheCall); } } std::string Name = TheCall->getName(); TheCall->setName(""); if (InvokeInst *II = dyn_cast<InvokeInst>(TheCall)) { NewCall = InvokeInst::Create (NewCallee, II->getNormalDest(), II->getUnwindDest(), Args, Name, TheCall); } else { NewCall = CallInst::Create (NewCallee, Args, Name, TheCall); } TheCall->replaceAllUsesWith(NewCall); DEBUG(errs() << " Result Call: " << *NewCall); if (TheCall->getType()->getTypeID() != Type::VoidTyID) { // If we are modifying the original function, update the DSGraph... DSGraph::ScalarMapTy &SM = FI.G->getScalarMap(); DSGraph::ScalarMapTy::iterator CII = SM.find(TheCall); if (CII != SM.end()) { SM[NewCall] = CII->second; SM.erase(CII); // Destroy the CallInst } else if (!FI.NewToOldValueMap.empty()) { // Otherwise, if this is a clone, update the NewToOldValueMap with the new // CI return value. FI.UpdateNewToOldValueMap(TheCall, NewCall); } } else if (!FI.NewToOldValueMap.empty()) { FI.UpdateNewToOldValueMap(TheCall, NewCall); } //FIXME: attributes on call? CallSite(NewCall).setCallingConv(CallSite(TheCall).getCallingConv()); TheCall->eraseFromParent(); }
DSNode * ConvertUnsafeAllocas::getDSNode(const Value *V, Function *F) { DSGraph * TDG = budsPass->getDSGraph(*F); DSNode *DSN = TDG->getNodeForValue((Value *)V).getNode(); return DSN; }
// // Method: TransformCSSAllocasToMallocs() // // Description: // This method is given the set of DSNodes from the stack safety pass that // have been marked for promotion. It then finds all alloca instructions // that have not been marked type-unknown and promotes them to heap // allocations. // void ConvertUnsafeAllocas::TransformCSSAllocasToMallocs (Module & M, std::set<DSNode *> & cssAllocaNodes) { for (Module::iterator FI = M.begin(); FI != M.end(); ++FI) { // // Skip functions that have no DSGraph. These are probably functions with // no function body and are, hence, cannot be analyzed. // if (!(budsPass->hasDSGraph (*FI))) continue; // // Get the DSGraph for the current function. // DSGraph *DSG = budsPass->getDSGraph(*FI); // // Search for alloca instructions that need promotion and add them to the // worklist. // std::vector<AllocaInst *> Worklist; for (Function::iterator BB = FI->begin(); BB != FI->end(); ++BB) { for (BasicBlock::iterator ii = BB->begin(); ii != BB->end(); ++ii) { Instruction * I = ii; if (AllocaInst * AI = dyn_cast<AllocaInst>(I)) { // // Get the DSNode for the allocation. // DSNode *DSN = DSG->getNodeForValue(AI).getNode(); assert (DSN && "No DSNode for alloca!\n"); // // If the alloca is type-known, we do not need to promote it, so // don't bother with it. // if (DSN->isNodeCompletelyFolded()) continue; // // Determine if the DSNode for the alloca is one of those marked as // unsafe by the stack safety analysis pass. If not, then we do not // need to promote it. // if (cssAllocaNodes.find(DSN) == cssAllocaNodes.end()) continue; // // If the DSNode for this alloca is already listed in the // unsafeAllocaNode vector, remove it since we are processing it here // std::list<DSNode *>::iterator NodeI = find (unsafeAllocaNodes.begin(), unsafeAllocaNodes.end(), DSN); if (NodeI != unsafeAllocaNodes.end()) { unsafeAllocaNodes.erase(NodeI); } // // This alloca needs to be changed to a malloc. Add it to the // worklist. // Worklist.push_back (AI); } } } // // Update the statistics. // if (Worklist.size()) ConvAllocas += Worklist.size(); // // Convert everything in the worklist into a malloc instruction. // while (Worklist.size()) { // // Grab an alloca from the worklist. // AllocaInst * AI = Worklist.back(); Worklist.pop_back(); // // Get the DSNode for this alloca. // DSNode *DSN = DSG->getNodeForValue(AI).getNode(); assert (DSN && "No DSNode for alloca!\n"); // // Promote the alloca and remove it from the program. // promoteAlloca (AI, DSN); AI->getParent()->getInstList().erase(AI); } } }
void StdLibDataStructures::processFunction(int x, Function *F) { for (Value::use_iterator ii = F->use_begin(), ee = F->use_end(); ii != ee; ++ii) if (CallInst* CI = dyn_cast<CallInst>(*ii)){ if (CI->getCalledValue() == F) { DSGraph* Graph = getDSGraph(*CI->getParent()->getParent()); // // Set the read, write, and heap markers on the return value // as appropriate. // if(isa<PointerType>((CI)->getType())){ if(Graph->hasNodeForValue(CI)){ if (recFuncs[x].action.read[0]) Graph->getNodeForValue(CI).getNode()->setReadMarker(); if (recFuncs[x].action.write[0]) Graph->getNodeForValue(CI).getNode()->setModifiedMarker(); if (recFuncs[x].action.heap[0]) Graph->getNodeForValue(CI).getNode()->setHeapMarker(); } } // // Set the read, write, and heap markers on the actual arguments // as appropriate. // for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) if (isa<PointerType>(CI->getArgOperand(y)->getType())){ if (Graph->hasNodeForValue(CI->getArgOperand(y))){ if (recFuncs[x].action.read[y + 1]) Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setReadMarker(); if (recFuncs[x].action.write[y + 1]) Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setModifiedMarker(); if (recFuncs[x].action.heap[y + 1]) Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setHeapMarker(); } } // // Merge the DSNoes for return values and parameters as // appropriate. // std::vector<DSNodeHandle> toMerge; if (recFuncs[x].action.mergeNodes[0]) if (isa<PointerType>(CI->getType())) if (Graph->hasNodeForValue(CI)) toMerge.push_back(Graph->getNodeForValue(CI)); for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) if (recFuncs[x].action.mergeNodes[y + 1]) if (isa<PointerType>(CI->getArgOperand(y)->getType())) if (Graph->hasNodeForValue(CI->getArgOperand(y))) toMerge.push_back(Graph->getNodeForValue(CI->getArgOperand(y))); for (unsigned y = 1; y < toMerge.size(); ++y) toMerge[0].mergeWith(toMerge[y]); // // Collapse (fold) the DSNode of the return value and the actual // arguments if directed to do so. // if (!noStdLibFold && recFuncs[x].action.collapse) { if (isa<PointerType>(CI->getType())){ if (Graph->hasNodeForValue(CI)) Graph->getNodeForValue(CI).getNode()->foldNodeCompletely(); NumNodesFoldedInStdLib++; } for (unsigned y = 0; y < CI->getNumArgOperands(); ++y){ if (isa<PointerType>(CI->getArgOperand(y)->getType())){ if (Graph->hasNodeForValue(CI->getArgOperand(y))){ Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->foldNodeCompletely(); NumNodesFoldedInStdLib++; } } } } } } else if (InvokeInst* CI = dyn_cast<InvokeInst>(*ii)){ if (CI->getCalledValue() == F) { DSGraph* Graph = getDSGraph(*CI->getParent()->getParent()); // // Set the read, write, and heap markers on the return value // as appropriate. // if(isa<PointerType>((CI)->getType())){ if(Graph->hasNodeForValue(CI)){ if (recFuncs[x].action.read[0]) Graph->getNodeForValue(CI).getNode()->setReadMarker(); if (recFuncs[x].action.write[0]) Graph->getNodeForValue(CI).getNode()->setModifiedMarker(); if (recFuncs[x].action.heap[0]) Graph->getNodeForValue(CI).getNode()->setHeapMarker(); } } // // Set the read, write, and heap markers on the actual arguments // as appropriate. // for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) if (isa<PointerType>(CI->getArgOperand(y)->getType())){ if (Graph->hasNodeForValue(CI->getArgOperand(y))){ if (recFuncs[x].action.read[y + 1]) Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setReadMarker(); if (recFuncs[x].action.write[y + 1]) Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setModifiedMarker(); if (recFuncs[x].action.heap[y + 1]) Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setHeapMarker(); } } // // Merge the DSNoes for return values and parameters as // appropriate. // std::vector<DSNodeHandle> toMerge; if (recFuncs[x].action.mergeNodes[0]) if (isa<PointerType>(CI->getType())) if (Graph->hasNodeForValue(CI)) toMerge.push_back(Graph->getNodeForValue(CI)); for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) if (recFuncs[x].action.mergeNodes[y + 1]) if (isa<PointerType>(CI->getArgOperand(y)->getType())) if (Graph->hasNodeForValue(CI->getArgOperand(y))) toMerge.push_back(Graph->getNodeForValue(CI->getArgOperand(y))); for (unsigned y = 1; y < toMerge.size(); ++y) toMerge[0].mergeWith(toMerge[y]); // // Collapse (fold) the DSNode of the return value and the actual // arguments if directed to do so. // if (!noStdLibFold && recFuncs[x].action.collapse) { if (isa<PointerType>(CI->getType())){ if (Graph->hasNodeForValue(CI)) Graph->getNodeForValue(CI).getNode()->foldNodeCompletely(); NumNodesFoldedInStdLib++; } for (unsigned y = 0; y < CI->getNumArgOperands(); ++y){ if (isa<PointerType>(CI->getArgOperand(y)->getType())){ if (Graph->hasNodeForValue(CI->getArgOperand(y))){ Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->foldNodeCompletely(); NumNodesFoldedInStdLib++; } } } } } } else if(ConstantExpr *CE = dyn_cast<ConstantExpr>(*ii)) { if(CE->isCast()) for (Value::use_iterator ci = CE->use_begin(), ce = CE->use_end(); ci != ce; ++ci) { if (CallInst* CI = dyn_cast<CallInst>(*ci)){ if (CI->getCalledValue() == CE) { DSGraph* Graph = getDSGraph(*CI->getParent()->getParent()); // // Set the read, write, and heap markers on the return value // as appropriate. // if(isa<PointerType>((CI)->getType())){ if(Graph->hasNodeForValue(CI)){ if (recFuncs[x].action.read[0]) Graph->getNodeForValue(CI).getNode()->setReadMarker(); if (recFuncs[x].action.write[0]) Graph->getNodeForValue(CI).getNode()->setModifiedMarker(); if (recFuncs[x].action.heap[0]) Graph->getNodeForValue(CI).getNode()->setHeapMarker(); } } // // Set the read, write, and heap markers on the actual arguments // as appropriate. // for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) if (recFuncs[x].action.read[y + 1]){ if (isa<PointerType>(CI->getArgOperand(y)->getType())){ if (Graph->hasNodeForValue(CI->getArgOperand(y))) Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setReadMarker(); if (Graph->hasNodeForValue(CI->getArgOperand(y))) Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setModifiedMarker(); if (Graph->hasNodeForValue(CI->getArgOperand(y))) Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setHeapMarker(); } } // // Merge the DSNoes for return values and parameters as // appropriate. // std::vector<DSNodeHandle> toMerge; if (recFuncs[x].action.mergeNodes[0]) if (isa<PointerType>(CI->getType())) if (Graph->hasNodeForValue(CI)) toMerge.push_back(Graph->getNodeForValue(CI)); for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) if (recFuncs[x].action.mergeNodes[y + 1]) if (isa<PointerType>(CI->getArgOperand(y)->getType())) if (Graph->hasNodeForValue(CI->getArgOperand(y))) toMerge.push_back(Graph->getNodeForValue(CI->getArgOperand(y))); for (unsigned y = 1; y < toMerge.size(); ++y) toMerge[0].mergeWith(toMerge[y]); // // Collapse (fold) the DSNode of the return value and the actual // arguments if directed to do so. // if (!noStdLibFold && recFuncs[x].action.collapse) { if (isa<PointerType>(CI->getType())){ if (Graph->hasNodeForValue(CI)) Graph->getNodeForValue(CI).getNode()->foldNodeCompletely(); NumNodesFoldedInStdLib++; } for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) if (isa<PointerType>(CI->getArgOperand(y)->getType())){ if (Graph->hasNodeForValue(CI->getArgOperand(y))){ DSNode * Node=Graph->getNodeForValue(CI->getArgOperand(y)).getNode(); Node->foldNodeCompletely(); NumNodesFoldedInStdLib++; } } } } } } } // // Pretend that this call site does not call this function anymore. // eraseCallsTo(F); }
void PoolRegisterElimination::removeSingletonRegistrations (const char * name) { // // Scan through all uses of the registration function and see if it can be // safely removed. If so, schedule it for removal. // std::vector<CallInst*> toBeRemoved; Function * F = intrinsic->getIntrinsic(name).F; // // Look for and record all registrations that can be deleted. // for (Value::use_iterator UI=F->use_begin(), UE=F->use_end(); UI != UE; ++UI) { // // Get the pointer to the registered object. // CallInst * CI = cast<CallInst>(*UI); Value * Ptr = intrinsic->getValuePointer(CI); // // Lookup the DSNode for the value in the function's DSGraph. // DSGraph * TDG = dsaPass->getDSGraph(*(CI->getParent()->getParent())); DSNodeHandle DSH = TDG->getNodeForValue(Ptr); assert ((!(DSH.isNull())) && "No DSNode for Value!\n"); // // If the object being registered is the same size as that found in the // DSNode, then we know it's a singleton object. The run-time doesn't need // such objects registered in the splay trees, so we can remove the // registration function. // DSNode * N = DSH.getNode(); Value * Size = intrinsic->getObjectSize (Ptr->stripPointerCasts()); if (Size) { if (ConstantInt * C = dyn_cast<ConstantInt>(Size)) { unsigned long size = C->getZExtValue(); if (size == N->getSize()) { toBeRemoved.push_back(CI); continue; } } } } // // Update the statistics. // if (toBeRemoved.size()) { RemovedRegistration += toBeRemoved.size(); SingletonRegistrations += toBeRemoved.size(); } // // Remove the unnecesary registrations. // std::vector<CallInst*>::iterator it, end; for (it = toBeRemoved.begin(), end = toBeRemoved.end(); it != end; ++it) { (*it)->eraseFromParent(); } }
/// visitGraph - Visit the functions in the specified graph, updating the /// specified lattice values for all of their uses. /// void StructureFieldVisitorBase:: visitGraph(DSGraph &DSG, std::multimap<DSNode*, LatticeValue*> &NodeLVs) { assert(!NodeLVs.empty() && "No lattice values to compute!"); // To visit a graph, first step, we visit the instruction making up each // function in the graph, but ignore calls when processing them. We handle // call nodes explicitly by looking at call nodes in the graph if needed. We // handle instructions before calls to avoid interprocedural analysis if we // can drive lattice values to bottom early. // SFVInstVisitor IV(DSG, Callbacks, NodeLVs); for (DSGraph::retnodes_iterator FI = DSG.retnodes_begin(), E = DSG.retnodes_end(); FI != E; ++FI) for (Function::iterator BB = FI->first->begin(), E = FI->first->end(); BB != E; ++BB) for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I) if (IV.visit(*I) && NodeLVs.empty()) return; // Nothing left to analyze. // Keep track of which actual direct callees are handled. std::set<Function*> CalleesHandled; // Once we have visited all of the instructions in the function bodies, if // there are lattice values that have not been driven to bottom, see if any of // the nodes involved are passed into function calls. If so, we potentially // have to recursively traverse the call graph. for (DSGraph::fc_iterator CS = DSG.fc_begin(), E = DSG.fc_end(); CS != E; ++CS) { // Figure out the mapping from a node in the caller (potentially several) // nodes in the callee. DSGraph::NodeMapTy CallNodeMap; Instruction *TheCall = CS->getCallSite().getInstruction(); // If this is an indirect function call, assume nothing gets passed through // it. FIXME: THIS IS BROKEN! Just get the ECG for the fn ptr if it's not // direct. if (CS->isIndirectCall()) continue; // If this is an external function call, it cannot be involved with this // node, because otherwise the node would be marked incomplete! if (CS->getCalleeFunc()->isExternal()) continue; // If we can handle this function call, remove it from the set of direct // calls found by the visitor. CalleesHandled.insert(CS->getCalleeFunc()); std::vector<DSNodeHandle> Args; DSGraph *CG = &ECG.getDSGraph(*CS->getCalleeFunc()); CG->getFunctionArgumentsForCall(CS->getCalleeFunc(), Args); if (!CS->getRetVal().isNull()) DSGraph::computeNodeMapping(Args[0], CS->getRetVal(), CallNodeMap); for (unsigned i = 0, e = CS->getNumPtrArgs(); i != e; ++i) { if (i == Args.size()-1) break; DSGraph::computeNodeMapping(Args[i+1], CS->getPtrArg(i), CallNodeMap); } Args.clear(); // The mapping we just computed maps from nodes in the callee to nodes in // the caller, so we can't query it efficiently. Instead of going through // the trouble of inverting the map to do this (linear time with the size of // the mapping), we just do a linear search to see if any affected nodes are // passed into this call. bool CallCanModifyDataFlow = false; for (DSGraph::NodeMapTy::iterator MI = CallNodeMap.begin(), E = CallNodeMap.end(); MI != E; ++MI) if (NodeLVs.count(MI->second.getNode())) // Okay, the node is passed in, check to see if the call might do // something interesting to it (i.e. if analyzing the call can produce // anything other than "top"). if ((CallCanModifyDataFlow = NodeCanPossiblyBeInteresting(MI->first, Callbacks))) break; // If this function call cannot impact the analysis (either because the // nodes we are tracking are not passed into the call, or the DSGraph for // the callee tells us that analysis of the callee can't provide interesting // information), ignore it. if (!CallCanModifyDataFlow) continue; // Okay, either compute analysis results for the callee function, or reuse // results previously computed. std::multimap<DSNode*, LatticeValue*> &CalleeFacts = getCalleeFacts(*CG); // Merge all of the facts for the callee into the facts for the caller. If // this reduces anything in the caller to 'bottom', remove them. for (DSGraph::NodeMapTy::iterator MI = CallNodeMap.begin(), E = CallNodeMap.end(); MI != E; ++MI) { // If we have Lattice facts in the caller for this node in the callee, // merge any information from the callee into the caller. // If the node is not accessed in the callee at all, don't update. if (MI->first->getType() == Type::VoidTy) continue; // If there are no data-flow facts live in the caller for this node, don't // both processing it. std::multimap<DSNode*, LatticeValue*>::iterator NLVI = NodeLVs.find(MI->second.getNode()); if (NLVI == NodeLVs.end()) continue; // Iterate over all of the lattice values that have corresponding fields // in the callee, merging in information as we go. Be careful about the // fact that the callee may get passed the address of a substructure and // other funny games. //if (CalleeFacts.count(const_cast<DSNode*>(MI->first)) == 0) { DSNode *CalleeNode = const_cast<DSNode*>(MI->first); unsigned CalleeNodeOffset = MI->second.getOffset(); while (NLVI->first == MI->second.getNode()) { // Figure out what offset in the callee this field would land. unsigned FieldOff = NLVI->second->getFieldOffset()+CalleeNodeOffset; // If the field is not within the callee node, ignore it. if (FieldOff >= CalleeNode->getSize()) { ++NLVI; continue; } // Okay, check to see if we have a lattice value for the field at offset // FieldOff in the callee node. const LatticeValue *CalleeLV = 0; std::multimap<DSNode*, LatticeValue*>::iterator CFI = CalleeFacts.lower_bound(CalleeNode); for (; CFI != CalleeFacts.end() && CFI->first == CalleeNode; ++CFI) if (CFI->second->getFieldOffset() == FieldOff) { CalleeLV = CFI->second; // Found it! break; } // If we don't, the lattice value hit bottom and we should remove the // lattice value in the caller. if (!CalleeLV) { delete NLVI->second; // The lattice value hit bottom. NodeLVs.erase(NLVI++); continue; } // Finally, if we did find a corresponding entry, merge the information // into the caller's lattice value and keep going. if (NLVI->second->mergeInValue(CalleeLV)) { // Okay, merging these two caused the caller value to hit bottom. // Remove it. delete NLVI->second; // The lattice value hit bottom. NodeLVs.erase(NLVI++); } ++NLVI; // We successfully merged in some information! } // If we ran out of facts to prove, just exit. if (NodeLVs.empty()) return; } } // The local analysis pass inconveniently discards many local function calls // from the graph if they are to known functions. Loop over direct function // calls not handled above and visit them as appropriate. while (!IV.DirectCallSites.empty()) { Instruction *Call = *IV.DirectCallSites.begin(); IV.DirectCallSites.erase(IV.DirectCallSites.begin()); // Is this one actually handled by DSA? if (CalleesHandled.count(cast<Function>(Call->getOperand(0)))) continue; // Collect the pointers involved in this call. std::vector<Value*> Pointers; if (isa<PointerType>(Call->getType())) Pointers.push_back(Call); for (unsigned i = 1, e = Call->getNumOperands(); i != e; ++i) if (isa<PointerType>(Call->getOperand(i)->getType())) Pointers.push_back(Call->getOperand(i)); // If this is an intrinsic function call, figure out which one. unsigned IID = cast<Function>(Call->getOperand(0))->getIntrinsicID(); for (unsigned i = 0, e = Pointers.size(); i != e; ++i) { // If any of our lattice values are passed into this call, which is // specially handled by the local analyzer, inform the lattice function. DSNode *N = DSG.getNodeForValue(Pointers[i]).getNode(); for (std::multimap<DSNode*, LatticeValue*>::iterator LVI = NodeLVs.lower_bound(N); LVI != NodeLVs.end() && LVI->first == N;) { bool AtBottom = false; switch (IID) { default: AtBottom = LVI->second->visitRecognizedCall(*Call); break; case Intrinsic::memset: if (Callbacks & Visit::Stores) AtBottom = LVI->second->visitMemSet(*cast<CallInst>(Call)); break; } if (AtBottom) { delete LVI->second; NodeLVs.erase(LVI++); } else { ++LVI; } } } } }
// // Method: visitCallSite() // // Description: // This method transforms a call site. A call site may either be a call // instruction or an invoke instruction. // // Inputs: // CS - The call site representing the instruction that should be transformed. // void FuncTransform::visitCallSite(CallSite& CS) { const Function *CF = CS.getCalledFunction(); Instruction *TheCall = CS.getInstruction(); bool thread_creation_point = false; // // Get the value that is called at this call site. Strip away any pointer // casts that do not change the representation of the data (i.e., are // lossless casts). // Value * CalledValue = CS.getCalledValue()->stripPointerCasts(); // // The CallSite::getCalledFunction() method is not guaranteed to strip off // pointer casts. If no called function was found, manually strip pointer // casts off of the called value and see if we get a function. If so, this // is a direct call, and we want to update CF accordingly. // if (!CF) CF = dyn_cast<Function>(CalledValue); // // Do not change any inline assembly code. // if (isa<InlineAsm>(TheCall->getOperand(0))) { errs() << "INLINE ASM: ignoring. Hoping that's safe.\n"; return; } // // Ignore calls to NULL pointers or undefined values. // if ((isa<ConstantPointerNull>(CalledValue)) || (isa<UndefValue>(CalledValue))) { errs() << "WARNING: Ignoring call using NULL/Undef function pointer.\n"; return; } // If this function is one of the memory manipulating functions built into // libc, emulate it with pool calls as appropriate. if (CF && CF->isDeclaration()) { std::string Name = CF->getName(); if (Name == "free" || Name == "cfree") { visitFreeCall(CS); return; } else if (Name == "malloc") { visitMallocCall(CS); return; } else if (Name == "calloc") { visitCallocCall(CS); return; } else if (Name == "realloc") { visitReallocCall(CS); return; } else if (Name == "memalign" || Name == "posix_memalign") { visitMemAlignCall(CS); return; } else if (Name == "strdup") { visitStrdupCall(CS); return; } else if (Name == "valloc") { errs() << "VALLOC USED BUT NOT HANDLED!\n"; abort(); } else if (unsigned PoolArgc = PAInfo.getNumInitialPoolArguments(Name)) { visitRuntimeCheck(CS, PoolArgc); return; } else if (Name == "pthread_create") { thread_creation_point = true; // // Get DSNode representing the DSNode of the function pointer Value of // the pthread_create call // DSNode* thread_callee_node = G->getNodeForValue(CS.getArgument(2)).getNode(); if (!thread_callee_node) { assert(0 && "apparently you need this code"); FuncInfo *CFI = PAInfo.getFuncInfo(*CF); thread_callee_node = G->getNodeForValue(CFI->MapValueToOriginal(CS.getArgument(2))).getNode(); } // Fill in CF with the name of one of the functions in thread_callee_node CF = const_cast<Function*>(dyn_cast<Function>(*thread_callee_node->globals_begin())); } } // // We need to figure out which local pool descriptors correspond to the pool // descriptor arguments passed into the function call. Calculate a mapping // from callee DSNodes to caller DSNodes. We construct a partial isomophism // between the graphs to figure out which pool descriptors need to be passed // in. The roots of this mapping is found from arguments and return values. // DataStructures& Graphs = PAInfo.getGraphs(); DSGraph::NodeMapTy NodeMapping; Instruction *NewCall; Value *NewCallee; std::vector<const DSNode*> ArgNodes; DSGraph *CalleeGraph; // The callee graph // For indirect callees, find any callee since all DS graphs have been // merged. if (CF) { // Direct calls are nice and simple. DEBUG(errs() << " Handling direct call: " << *TheCall << "\n"); // // Do not try to add pool handles to the function if it: // a) Already calls a cloned function; or // b) Calls a function which was never cloned. // // For such a call, just replace any arguments that take original functions // with their cloned function poiner values. // FuncInfo *CFI = PAInfo.getFuncInfo(*CF); if (CFI == 0 || CFI->Clone == 0) { // Nothing to transform... visitInstruction(*TheCall); return; } // // Oh, dear. We must add pool descriptors to this direct call. // NewCallee = CFI->Clone; ArgNodes = CFI->ArgNodes; assert ((Graphs.hasDSGraph (*CF)) && "Function has no ECGraph!\n"); CalleeGraph = Graphs.getDSGraph(*CF); } else { DEBUG(errs() << " Handling indirect call: " << *TheCall << "\n"); DSGraph *G = Graphs.getGlobalsGraph(); DSGraph::ScalarMapTy& SM = G->getScalarMap(); // Here we fill in CF with one of the possible called functions. Because we // merged together all of the arguments to all of the functions in the // equivalence set, it doesn't really matter which one we pick. // (If the function was cloned, we have to map the cloned call instruction // in CS back to the original call instruction.) Instruction *OrigInst = cast<Instruction>(getOldValueIfAvailable(CS.getInstruction())); // // Attempt to get one of the function targets of this indirect call site by // looking at the call graph constructed by the points-to analysis. Be // sure to use the original call site from the original function; the // points-to analysis has no information on the clones we've created. // // Also, look for the target that has the greatest number of arguments that // have associated DSNodes. This ensures that we pass the maximum number // of pools possible and prevents us from eliding a pool because we're // examining a target that doesn't need it. // const DSCallGraph & callGraph = Graphs.getCallGraph(); DSCallGraph::callee_iterator I = callGraph.callee_begin(OrigInst); for (; I != callGraph.callee_end(OrigInst); ++I) { for(DSCallGraph::scc_iterator sccii = callGraph.scc_begin(*I), sccee = callGraph.scc_end(*I); sccii != sccee; ++sccii){ if(SM.find(SM.getLeaderForGlobal(*sccii)) == SM.end()) continue; // // Get the information for this function. Since this is coming from // DSA, it should be an original function. // // This call site calls a function, that is not defined in this module if (!(Graphs.hasDSGraph(**sccii))) return; // For all other cases Func Info must exist. PAInfo.getFuncInfo(**sccii); // // If this target takes more DSNodes than the last one we found, then // make *this* target our canonical target. // CF = *sccii; break; } } if(!CF){ const Function *F1 = OrigInst->getParent()->getParent(); F1 = callGraph.sccLeader(&*F1); for(DSCallGraph::scc_iterator sccii = callGraph.scc_begin(F1), sccee = callGraph.scc_end(F1); sccii != sccee; ++sccii){ if(SM.find(SM.getLeaderForGlobal(*sccii)) == SM.end()) continue; // // Get the information for this function. Since this is coming from DSA, // it should be an original function. // // This call site calls a function, that is not defined in this module if (!(Graphs.hasDSGraph(**sccii))) return; // For all other cases Func Info must exist. PAInfo.getFuncInfo(**sccii); // // If this target takes more DSNodes than the last one we found, then // make *this* target our canonical target. // CF = *sccii; } } // Assuming the call graph is always correct. And if the call graph reports, // no callees, we can assume that it is right. // // If we didn't find the callee in the constructed call graph, try // checking in the DSNode itself. // This isn't ideal as it means that this call site didn't have inlining // happen. // // // If we still haven't been able to find a target function of the call site // to transform, do nothing. // // One may be tempted to think that we should always have at least one // target, but this is not true. There are perfectly acceptable (but // strange) programs for which no function targets exist. Function // pointers loaded from undef values, for example, will have no targets. // if (!CF) return; // // It's possible that this program has indirect call targets that are // not defined in this module. Do not transformation for such functions. // if (!(Graphs.hasDSGraph(*CF))) return; // // Get the common graph for the set of functions this call may invoke. // assert ((Graphs.hasDSGraph(*CF)) && "Function has no DSGraph!\n"); CalleeGraph = Graphs.getDSGraph(*CF); #ifndef NDEBUG // Verify that all potential callees at call site have the same DS graph. DSCallGraph::callee_iterator E = Graphs.getCallGraph().callee_end(OrigInst); for (; I != E; ++I) { const Function * F = *I; assert (F); if (!(F)->isDeclaration()) assert(CalleeGraph == Graphs.getDSGraph(**I) && "Callees at call site do not have a common graph!"); } #endif // Find the DS nodes for the arguments that need to be added, if any. FuncInfo *CFI = PAInfo.getFuncInfo(*CF); assert(CFI && "No function info for callee at indirect call?"); ArgNodes = CFI->ArgNodes; if (ArgNodes.empty()) return; // No arguments to add? Transformation is a noop! // Cast the function pointer to an appropriate type! std::vector<Type*> ArgTys(ArgNodes.size(), PoolAllocate::PoolDescPtrTy); for (CallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end(); I != E; ++I) ArgTys.push_back((*I)->getType()); FunctionType *FTy = FunctionType::get(TheCall->getType(), ArgTys, false); PointerType *PFTy = PointerType::getUnqual(FTy); // If there are any pool arguments cast the func ptr to the right type. NewCallee = CastInst::CreatePointerCast(CS.getCalledValue(), PFTy, "tmp", TheCall); } // // FIXME: Why do we disable strict checking when calling the // DSGraph::computeNodeMapping() method? // Function::const_arg_iterator FAI = CF->arg_begin(), E = CF->arg_end(); CallSite::arg_iterator AI = CS.arg_begin() + (thread_creation_point ? 3 : 0); CallSite::arg_iterator AE = CS.arg_end(); for ( ; FAI != E && AI != AE; ++FAI, ++AI) if (!isa<Constant>(*AI)) { DSGraph::computeNodeMapping(CalleeGraph->getNodeForValue(FAI), getDSNodeHFor(*AI), NodeMapping, false); } //assert(AI == AE && "Varargs calls not handled yet!"); // Map the return value as well... if (isa<PointerType>(TheCall->getType())) DSGraph::computeNodeMapping(CalleeGraph->getReturnNodeFor(*CF), getDSNodeHFor(TheCall), NodeMapping, false); // This code seems redundant (and crashes occasionally) // There is no reason to map globals here, since they are not passed as // arguments // // Map the nodes that are pointed to by globals. // DSScalarMap &CalleeSM = CalleeGraph->getScalarMap(); // for (DSScalarMap::global_iterator GI = G.getScalarMap().global_begin(), // E = G.getScalarMap().global_end(); GI != E; ++GI) // if (CalleeSM.count(*GI)) // DSGraph::computeNodeMapping(CalleeGraph->getNodeForValue(*GI), // getDSNodeHFor(*GI), // NodeMapping, false); // // Okay, now that we have established our mapping, we can figure out which // pool descriptors to pass in... // // Note: // There used to be code here that would create a new pool before the // function call and destroy it after the function call. This could would // get triggered if bounds checking was disbled or the DSNode for the // argument was an array value. // // I believe that code was incorrect; an argument may have a NULL pool handle // (i.e., no pool handle) because the pool allocation heuristic used simply // decided not to assign that value a pool. The argument may alias data // that should not be freed after the function call is complete, so calling // pooldestroy() after the call would free data, causing dangling pointer // dereference errors. // std::vector<Value*> Args; for (unsigned i = 0, e = ArgNodes.size(); i != e; ++i) { Value *ArgVal = Constant::getNullValue(PoolAllocate::PoolDescPtrTy); if (NodeMapping.count(ArgNodes[i])) { if (DSNode *LocalNode = NodeMapping[ArgNodes[i]].getNode()) if (FI.PoolDescriptors.count(LocalNode)) ArgVal = FI.PoolDescriptors.find(LocalNode)->second; } Args.push_back(ArgVal); } // Add the rest of the arguments unless we're a thread creation point, in which case we only need the pools if(!thread_creation_point) Args.insert(Args.end(), CS.arg_begin(), CS.arg_end()); // // There are circumstances where a function is casted to another type and // then called (que horible). We need to perform a similar cast if the // type doesn't match the number of arguments. // if (Function * NewFunction = dyn_cast<Function>(NewCallee)) { FunctionType * NewCalleeType = NewFunction->getFunctionType(); if (NewCalleeType->getNumParams() != Args.size()) { std::vector<Type *> Types; Type * FuncTy = FunctionType::get (NewCalleeType->getReturnType(), Types, true); FuncTy = PointerType::getUnqual (FuncTy); NewCallee = new BitCastInst (NewCallee, FuncTy, "", TheCall); } } std::string Name = TheCall->getName(); TheCall->setName(""); if(thread_creation_point) { Module *M = CS.getInstruction()->getParent()->getParent()->getParent(); Value* pthread_replacement = M->getFunction("poolalloc_pthread_create"); std::vector<Value*> thread_args; //Push back original thread arguments through the callee thread_args.push_back(CS.getArgument(0)); thread_args.push_back(CS.getArgument(1)); thread_args.push_back(CS.getArgument(2)); //Push back the integer argument saying how many uses there are thread_args.push_back(Constant::getIntegerValue(llvm::Type::getInt32Ty(M->getContext()),APInt(32,Args.size()))); thread_args.insert(thread_args.end(),Args.begin(),Args.end()); thread_args.push_back(CS.getArgument(3)); //Make the thread creation call NewCall = CallInst::Create(pthread_replacement, thread_args, Name,TheCall); } else if (InvokeInst *II = dyn_cast<InvokeInst>(TheCall)) { NewCall = InvokeInst::Create (NewCallee, II->getNormalDest(), II->getUnwindDest(), Args, Name, TheCall); } else { NewCall = CallInst::Create (NewCallee, Args, Name, TheCall); } // Add all of the uses of the pool descriptor for (unsigned i = 0, e = ArgNodes.size(); i != e; ++i) AddPoolUse(*NewCall, Args[i], PoolUses); TheCall->replaceAllUsesWith(NewCall); DEBUG(errs() << " Result Call: " << *NewCall << "\n"); if (!TheCall->getType()->isVoidTy()) { // If we are modifying the original function, update the DSGraph... DSGraph::ScalarMapTy &SM = G->getScalarMap(); DSGraph::ScalarMapTy::iterator CII = SM.find(TheCall); if (CII != SM.end()) { SM[NewCall] = CII->second; SM.erase(CII); // Destroy the CallInst } else if (!FI.NewToOldValueMap.empty()) { // Otherwise, if this is a clone, update the NewToOldValueMap with the new // CI return value. UpdateNewToOldValueMap(TheCall, NewCall); } } else if (!FI.NewToOldValueMap.empty()) { UpdateNewToOldValueMap(TheCall, NewCall); } // // Copy over the calling convention and attributes of the original call // instruction to the new call instruction. // CallSite(NewCall).setCallingConv(CallSite(TheCall).getCallingConv()); TheCall->eraseFromParent(); visitInstruction(*NewCall); }
// // Method: insertEasyDanglingPointers() // // Description: // Insert dangling pointer dereferences into the code. This is done by // finding load/store instructions and inserting a free on the pointer to // ensure the dereference (and all future dereferences) are illegal. // // Return value: // true - The module was modified. // false - The module was left unmodified. // // Notes: // This code utilizes DSA to ensure that the pointer can pointer to heap // memory (although the pointer is allowed to alias global and stack memory). // bool FaultInjector::insertEasyDanglingPointers (Function & F) { // // Ensure that we can get analysis information for this function. // if (!(TDPass->hasDSGraph(F))) return false; // // Scan through each instruction of the function looking for load and store // instructions. Free the pointer right before. // DSGraph * DSG = TDPass->getDSGraph(F); for (Function::iterator fI = F.begin(), fE = F.end(); fI != fE; ++fI) { BasicBlock & BB = *fI; for (BasicBlock::iterator bI = BB.begin(), bE = BB.end(); bI != bE; ++bI) { Instruction * I = bI; // // Look to see if there is an instruction that uses a pointer. If so, // then free the pointer before the use. // Value * Pointer = 0; if (LoadInst * LI = dyn_cast<LoadInst>(I)) Pointer = LI->getPointerOperand(); else if (StoreInst * SI = dyn_cast<StoreInst>(I)) Pointer = SI->getPointerOperand(); else continue; // // Check to ensure that this pointer aliases with the heap. If so, go // ahead and add the free. Note that we may introduce an invalid free, // but we're injecting errors, so I think that's okay. // DSNode * Node = DSG->getNodeForValue(Pointer).getNode(); if (Node && (Node->isHeapNode())) { // // Avoid free'ing pointers that are trivially stack objects or global // variables. // if (isa<GlobalValue>(Pointer->stripPointerCasts()) || isa<AllocaInst>(Pointer->stripPointerCasts())) { continue; } // Skip if we should not insert a fault. if (!doFault()) continue; // // Print information about where the fault is being inserted. // printSourceInfo ("Easy dangling pointer", I); CallInst::Create (Free, Pointer, "", I); ++DPFaults; } } } return (DPFaults > 0); }