// // Method: getLocalPoolNodes() // // Description: // For a given function, determine which DSNodes for that function should have // local pools created for them. // void Heuristic::getLocalPoolNodes (const Function & F, DSNodeList_t & Nodes) { // // Get the DSGraph of the specified function. If the DSGraph has no nodes, // then there is nothing we need to do. // DSGraph* G = Graphs->getDSGraph(F); if (G->node_begin() == G->node_end()) return; // // Calculate which DSNodes are reachable from globals. If a node is reachable // from a global, we will create a global pool for it, so no argument passage // is required. Graphs->getGlobalsGraph(); // Map all node reachable from this global to the corresponding nodes in // the globals graph. DSGraph::NodeMapTy GlobalsGraphNodeMapping; G->computeGToGGMapping(GlobalsGraphNodeMapping); // // Loop over all of the nodes which are non-escaping, adding pool-allocatable // ones to the NodesToPA vector. In other words, scan over the DSGraph and // find nodes for which a new pool must be created within this function. // for (DSGraph::node_iterator I = G->node_begin(), E = G->node_end(); I != E; ++I){ // Get the DSNode and, if applicable, its mirror in the globals graph DSNode * N = I; DSNode * GGN = GlobalsGraphNodeMapping[N].getNode(); // // Only the following nodes are pool allocated: // 1) Local Heap nodes // 2) Nodes which are mirrored in the globals graph and, in the globals // graph, are heap nodes. // if ((N->isHeapNode()) || (GGN && GGN->isHeapNode())) { if (!(GlobalPoolNodes.count (N) || GlobalPoolNodes.count (GGN))) { // Otherwise, if it was not passed in from outside the function, it must // be a local pool! assert((!N->isGlobalNode() || N->isPtrToIntNode()) && "Should be in global mapping!"); if(!N->isPtrToIntNode()) { Nodes.push_back (N); } } } } return; }
// // 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); }
void AllButUnreachableFromMemoryHeuristic::AssignToPools ( const DSNodeList_t &NodesToPA, Function *F, DSGraph* G, std::vector<OnePool> &ResultPools) { // Build a set of all nodes that are reachable from another node in the // graph. Here we ignore scalar nodes that are only globals as they are // often global pointers to big arrays. std::set<const DSNode*> ReachableFromMemory; for (DSGraph::node_iterator I = G->node_begin(), E = G->node_end(); I != E; ++I) { DSNode *N = I; #if 0 // // Ignore nodes that are just globals and not arrays. // if (N->isArray() || N->isHeapNode() || N->isAllocaNode() || N->isUnknownNode()) #endif // If a node is marked, all children are too. if (!ReachableFromMemory.count(N)) { for (DSNode::iterator NI = N->begin(), E = N->end(); NI != E; ++NI) { // // Sometimes this results in a NULL DSNode. Skip it if that is the // case. // if (!(*NI)) continue; // // Do a depth-first iteration over the DSGraph starting with this // child node. // for (df_ext_iterator<const DSNode*> DI = df_ext_begin(*NI, ReachableFromMemory), E = df_ext_end(*NI, ReachableFromMemory); DI != E; ++DI) /*empty*/; } } } // Only pool allocate a node if it is reachable from a memory object (itself // included). for (unsigned i = 0, e = NodesToPA.size(); i != e; ++i) if (ReachableFromMemory.count(NodesToPA[i])) ResultPools.push_back(OnePool(NodesToPA[i])); }
// // Function: FoldNodesInDSGraph() // // Description: // This function will take the specified DSGraph and fold all DSNodes within // it that are marked with the heap flag. // static void FoldNodesInDSGraph (DSGraph & Graph) { // Worklist of heap nodes to process std::vector<DSNodeHandle> HeapNodes; // // Go find all of the heap nodes. // DSGraph::node_iterator i; DSGraph::node_iterator e = Graph.node_end(); for (i = Graph.node_begin(); i != e; ++i) { DSNode * Node = i; if (Node->isHeapNode()) HeapNodes.push_back (DSNodeHandle(Node)); } // // Fold all of the heap nodes; this makes them type-unknown. // for (unsigned i = 0; i < HeapNodes.size(); ++i) HeapNodes[i].getNode()->foldNodeCompletely(); return; }
// Precondition: Enforce that the alloca nodes haven't been already converted void ConvertUnsafeAllocas::TransformAllocasToMallocs(std::list<DSNode *> & unsafeAllocaNodes) { std::list<DSNode *>::const_iterator iCurrent = unsafeAllocaNodes.begin(), iEnd = unsafeAllocaNodes.end(); for (; iCurrent != iEnd; ++iCurrent) { DSNode *DSN = *iCurrent; // Now change the alloca instruction corresponding to the node // to malloc DSGraph *DSG = DSN->getParentGraph(); DSGraph::ScalarMapTy &SM = DSG->getScalarMap(); #ifndef LLVA_KERNEL Instruction *MI = 0; #else Value *MI = 0; #endif for (DSGraph::ScalarMapTy::iterator SMI = SM.begin(), SME = SM.end(); SMI != SME; ) { bool stackAllocate = true; // If this is already a heap node, then you cannot allocate this on the // stack if (DSN->isHeapNode()) { stackAllocate = false; } if (SMI->second.getNode() == DSN) { if (AllocaInst *AI = dyn_cast<AllocaInst>((Value *)(SMI->first))) { // // Create a new heap allocation instruction. // if (AI->getParent() != 0) { // // Create an LLVM value representing the size of the allocation. // If it's an array allocation, we'll need to insert a // multiplication instruction to get the size times the number of // elements. // unsigned long size = TD->getTypeAllocSize(AI->getAllocatedType()); Value *AllocSize = ConstantInt::get(Int32Type, size); if (AI->isArrayAllocation()) AllocSize = BinaryOperator::Create(Instruction::Mul, AllocSize, AI->getOperand(0), "sizetmp", AI); std::vector<Value *> args(1, AllocSize); CallInst *CI = CallInst::Create (kmalloc, args.begin(), args.end(), "", AI); MI = castTo (CI, AI->getType(), "", AI); DSN->setHeapMarker(); AI->replaceAllUsesWith(MI); SM.erase(SMI++); AI->getParent()->getInstList().erase(AI); ++ConvAllocas; InsertFreesAtEnd(MI); #ifndef LLVA_KERNEL if (stackAllocate) { ArrayMallocs.insert(MI); } #endif } else { ++SMI; } } else { ++SMI; } } else { ++SMI; } } } }
// // 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); }