// Find the number of arguments we need to add to the functions. void CSDataRando::findFunctionArgNodes(const std::vector<const Function *> &Functions) { std::vector<DSNodeHandle> RootNodes; for (const Function *F : Functions) { DSGraph *G = DSA->getDSGraph(*F); G->getFunctionArgumentsForCall(F, RootNodes); } // No additional args to pass. if (RootNodes.size() == 0) { return; } DenseSet<const DSNode*> MarkedNodes; for (DSNodeHandle &NH : RootNodes) { if (DSNode *N = NH.getNode()) { N->markReachableNodes(MarkedNodes); } } // Remove global nodes from the arg nodes. If we are using the bottom-up // analysis then if a node is a global node all contexts will use the global map. for (auto i : GlobalNodes) { MarkedNodes.erase(i); } // Remove any nodes that are marked do not encrypt. SmallVector<const DSNode*, 8> MarkedNodeWorkList; for (auto i : MarkedNodes) { if (i->isDoNotEncryptNode()) { MarkedNodeWorkList.push_back(i); } } for (auto i : MarkedNodeWorkList) { MarkedNodes.erase(i); } if (MarkedNodes.empty()) { return; } // Create a FuncInfo entry for each of the functions with the arg nodes that // need to be passed for (const Function *F : Functions) { FuncInfo &FI = FunctionInfo[F]; FI.ArgNodes.insert(FI.ArgNodes.end(), MarkedNodes.begin(), MarkedNodes.end()); } }
/// 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; } } } } }