ExplodedNode* SwitchNodeBuilder::generateDefaultCaseNode(ProgramStateRef St, bool IsSink) { // Get the block for the default case. assert(Src->succ_rbegin() != Src->succ_rend()); CFGBlock *DefaultBlock = *Src->succ_rbegin(); // Sanity check for default blocks that are unreachable and not caught // by earlier stages. if (!DefaultBlock) return NULL; bool IsNew; ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, Pred->getLocationContext()), St, IsSink, &IsNew); Succ->addPredecessor(Pred, *Eng.G); if (!IsNew) return 0; if (!IsSink) Eng.WList->enqueue(Succ); return Succ; }
void ExprEngine::processCallEnter(NodeBuilderContext& BC, CallEnter CE, ExplodedNode *Pred) { // Get the entry block in the CFG of the callee. const StackFrameContext *calleeCtx = CE.getCalleeContext(); PrettyStackTraceLocationContext CrashInfo(calleeCtx); const CFGBlock *Entry = CE.getEntry(); // Validate the CFG. assert(Entry->empty()); assert(Entry->succ_size() == 1); // Get the solitary successor. const CFGBlock *Succ = *(Entry->succ_begin()); // Construct an edge representing the starting location in the callee. BlockEdge Loc(Entry, Succ, calleeCtx); ProgramStateRef state = Pred->getState(); // Construct a new node, notify checkers that analysis of the function has // begun, and add the resultant nodes to the worklist. bool isNew; ExplodedNode *Node = G.getNode(Loc, state, false, &isNew); Node->addPredecessor(Pred, G); if (isNew) { ExplodedNodeSet DstBegin; processBeginOfFunction(BC, Node, DstBegin, Loc); Engine.enqueue(DstBegin); } }
void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode *N) { assert (!N->isSink()); // Check if this node entered a callee. if (isa<CallEnter>(N->getLocation())) { // Still use the index of the CallExpr. It's needed to create the callee // StackFrameContext. Eng.WList->enqueue(N, &B, Idx); return; } // Do not create extra nodes. Move to the next CFG element. if (isa<PostInitializer>(N->getLocation())) { Eng.WList->enqueue(N, &B, Idx+1); return; } PostStmt Loc(getStmt(), N->getLocationContext()); if (Loc == N->getLocation()) { // Note: 'N' should be a fresh node because otherwise it shouldn't be // a member of Deferred. Eng.WList->enqueue(N, &B, Idx+1); return; } bool IsNew; ExplodedNode *Succ = Eng.G->getNode(Loc, N->State, &IsNew); Succ->addPredecessor(N, *Eng.G); if (IsNew) Eng.WList->enqueue(Succ, &B, Idx+1); }
ExplodedNode* GRBranchNodeBuilder::generateNode(const GRState* State, bool branch) { // If the branch has been marked infeasible we should not generate a node. if (!isFeasible(branch)) return NULL; bool IsNew; ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src,branch ? DstT:DstF,Pred->getLocationContext()), State, &IsNew); Succ->addPredecessor(Pred, *Eng.G); if (branch) GeneratedTrue = true; else GeneratedFalse = true; if (IsNew) { Deferred.push_back(Succ); return Succ; } return NULL; }
void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { // Get the entry block in the CFG of the callee. const StackFrameContext *calleeCtx = CE.getCalleeContext(); PrettyStackTraceLocationContext CrashInfo(calleeCtx); const CFG *CalleeCFG = calleeCtx->getCFG(); const CFGBlock *Entry = &(CalleeCFG->getEntry()); // Validate the CFG. assert(Entry->empty()); assert(Entry->succ_size() == 1); // Get the solitary successor. const CFGBlock *Succ = *(Entry->succ_begin()); // Construct an edge representing the starting location in the callee. BlockEdge Loc(Entry, Succ, calleeCtx); ProgramStateRef state = Pred->getState(); // Construct a new node and add it to the worklist. bool isNew; ExplodedNode *Node = G.getNode(Loc, state, false, &isNew); Node->addPredecessor(Pred, G); if (isNew) Engine.getWorkList()->enqueue(Node); }
ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N) { // Create a CallExitBegin node and enqueue it. const StackFrameContext *LocCtx = cast<StackFrameContext>(N->getLocationContext()); // Use the the callee location context. CallExitBegin Loc(LocCtx); bool isNew; ExplodedNode *Node = G->getNode(Loc, N->getState(), false, &isNew); Node->addPredecessor(N, *G); return isNew ? Node : 0; }
ExplodedNode* SwitchNodeBuilder::generateCaseStmtNode(const iterator &I, ProgramStateRef St) { bool IsNew; ExplodedNode *Succ = Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), St, false, &IsNew); Succ->addPredecessor(Pred, Eng.G); if (!IsNew) return nullptr; Eng.WList->enqueue(Succ); return Succ; }
void CallExitNodeBuilder::generateNode(const ProgramState *state) { // Get the callee's location context. const StackFrameContext *LocCtx = cast<StackFrameContext>(Pred->getLocationContext()); // When exiting an implicit automatic obj dtor call, the callsite is the Stmt // that triggers the dtor. PostStmt Loc(LocCtx->getCallSite(), LocCtx->getParent()); bool isNew; ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); Node->addPredecessor(const_cast<ExplodedNode*>(Pred), *Eng.G); if (isNew) Eng.WList->enqueue(Node, LocCtx->getCallSiteBlock(), LocCtx->getIndex() + 1); }
void ExprEngine::processCallExit(ExplodedNode *Pred) { ProgramStateRef state = Pred->getState(); const StackFrameContext *calleeCtx = Pred->getLocationContext()->getCurrentStackFrame(); const LocationContext *callerCtx = calleeCtx->getParent(); const Stmt *CE = calleeCtx->getCallSite(); // If the callee returns an expression, bind its value to CallExpr. if (const ReturnStmt *RS = getReturnStmt(Pred)) { const LocationContext *LCtx = Pred->getLocationContext(); SVal V = state->getSVal(RS, LCtx); state = state->BindExpr(CE, callerCtx, V); } // Bind the constructed object value to CXXConstructExpr. if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) { const CXXThisRegion *ThisR = getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx); SVal ThisV = state->getSVal(ThisR); // Always bind the region to the CXXConstructExpr. state = state->BindExpr(CCE, Pred->getLocationContext(), ThisV); } static SimpleProgramPointTag returnTag("ExprEngine : Call Return"); PostStmt Loc(CE, callerCtx, &returnTag); bool isNew; ExplodedNode *N = G.getNode(Loc, state, false, &isNew); N->addPredecessor(Pred, G); if (!isNew) return; // Perform the post-condition check of the CallExpr. ExplodedNodeSet Dst; NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), N); SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext, &Ctx); SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex()); getCheckerManager().runCheckersForPostStmt(Dst, N, CE, *this, /* wasInlined */ true); // Enqueue the next element in the block. for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I) { Engine.getWorkList()->enqueue(*I, calleeCtx->getCallSiteBlock(), calleeCtx->getIndex()+1); } }
void CoreEngine::enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx) { assert(Block); assert(!N->isSink()); // Check if this node entered a callee. if (N->getLocation().getAs<CallEnter>()) { // Still use the index of the CallExpr. It's needed to create the callee // StackFrameContext. WList->enqueue(N, Block, Idx); return; } // Do not create extra nodes. Move to the next CFG element. if (N->getLocation().getAs<PostInitializer>() || N->getLocation().getAs<PostImplicitCall>()|| N->getLocation().getAs<LoopExit>()) { WList->enqueue(N, Block, Idx+1); return; } if (N->getLocation().getAs<EpsilonPoint>()) { WList->enqueue(N, Block, Idx); return; } if ((*Block)[Idx].getKind() == CFGElement::NewAllocator) { WList->enqueue(N, Block, Idx+1); return; } // At this point, we know we're processing a normal statement. CFGStmt CS = (*Block)[Idx].castAs<CFGStmt>(); PostStmt Loc(CS.getStmt(), N->getLocationContext()); if (Loc == N->getLocation().withTag(nullptr)) { // Note: 'N' should be a fresh node because otherwise it shouldn't be // a member of Deferred. WList->enqueue(N, Block, Idx+1); return; } bool IsNew; ExplodedNode *Succ = G.getNode(Loc, N->getState(), false, &IsNew); Succ->addPredecessor(N, G); if (IsNew) WList->enqueue(Succ, Block, Idx+1); }
ExplodedNode* SwitchNodeBuilder::generateCaseStmtNode(const iterator &I, const ProgramState *St) { bool IsNew; ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), St, &IsNew); Succ->addPredecessor(Pred, *Eng.G); if (IsNew) { Eng.WList->enqueue(Succ); return Succ; } return NULL; }
ExplodedNode* StmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc, const ProgramState *State, ExplodedNode *Pred) { bool IsNew; ExplodedNode *N = Eng.G->getNode(Loc, State, &IsNew); N->addPredecessor(Pred, *Eng.G); Deferred.erase(Pred); if (IsNew) { Deferred.insert(N); return N; } return NULL; }
/// GenerateNode - Utility method to generate nodes, hook up successors, /// and add nodes to the worklist. void GRCoreEngine::GenerateNode(const ProgramPoint& Loc, const GRState* State, ExplodedNode* Pred) { bool IsNew; ExplodedNode* Node = G->getNode(Loc, State, &IsNew); if (Pred) Node->addPredecessor(Pred, *G); // Link 'Node' with its predecessor. else { assert (IsNew); G->addRoot(Node); // 'Node' has no predecessor. Make it a root. } // Only add 'Node' to the worklist if it was freshly generated. if (IsNew) WList->Enqueue(Node); }
void EndOfFunctionNodeBuilder::GenerateCallExitNode(const ProgramState *state) { hasGeneratedNode = true; // Create a CallExit node and enqueue it. const StackFrameContext *LocCtx = cast<StackFrameContext>(Pred->getLocationContext()); const Stmt *CE = LocCtx->getCallSite(); // Use the the callee location context. CallExit Loc(CE, LocCtx); bool isNew; ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); Node->addPredecessor(Pred, *Eng.G); if (isNew) Eng.WList->enqueue(Node); }
// This function generate a new ExplodedNode but not a new branch(block edge). ExplodedNode *BranchNodeBuilder::generateNode(const Stmt *Condition, const ProgramState *State) { bool IsNew; ExplodedNode *Succ = Eng.G->getNode(PostCondition(Condition, Pred->getLocationContext()), State, &IsNew); Succ->addPredecessor(Pred, *Eng.G); Pred = Succ; if (IsNew) return Succ; return NULL; }
ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc, ProgramStateRef State, ExplodedNode *FromN, bool MarkAsSink) { HasGeneratedNodes = true; bool IsNew; ExplodedNode *N = C.Eng.G->getNode(Loc, State, MarkAsSink, &IsNew); N->addPredecessor(FromN, *C.Eng.G); Frontier.erase(FromN); if (!IsNew) return 0; if (!MarkAsSink) Frontier.Add(N); return N; }
ExplodedNode* GREndPathNodeBuilder::generateNode(const GRState* State, const void *tag, ExplodedNode* P) { HasGeneratedNode = true; bool IsNew; ExplodedNode* Node = Eng.G->getNode(BlockEntrance(&B, Pred->getLocationContext(), tag), State, &IsNew); Node->addPredecessor(P ? P : Pred, *Eng.G); if (IsNew) { Eng.G->addEndOfPath(Node); return Node; } return NULL; }
ExplodedNode* IndirectGotoNodeBuilder::generateNode(const iterator &I, ProgramStateRef St, bool IsSink) { bool IsNew; ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), St, IsSink, &IsNew); Succ->addPredecessor(Pred, *Eng.G); if (!IsNew) return 0; if (!IsSink) Eng.WList->enqueue(Succ); return Succ; }
void GRStmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { assert (!N->isSink()); PostStmt Loc(getStmt(), N->getLocationContext()); if (Loc == N->getLocation()) { // Note: 'N' should be a fresh node because otherwise it shouldn't be // a member of Deferred. Eng.WList->Enqueue(N, B, Idx+1); return; } bool IsNew; ExplodedNode* Succ = Eng.G->getNode(Loc, N->State, &IsNew); Succ->addPredecessor(N, *Eng.G); if (IsNew) Eng.WList->Enqueue(Succ, B, Idx+1); }
void CoreEngine::enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx) { assert(Block); assert (!N->isSink()); // Check if this node entered a callee. if (isa<CallEnter>(N->getLocation())) { // Still use the index of the CallExpr. It's needed to create the callee // StackFrameContext. WList->enqueue(N, Block, Idx); return; } // Do not create extra nodes. Move to the next CFG element. if (isa<PostInitializer>(N->getLocation())) { WList->enqueue(N, Block, Idx+1); return; } if (isa<EpsilonPoint>(N->getLocation())) { WList->enqueue(N, Block, Idx); return; } const CFGStmt *CS = (*Block)[Idx].getAs<CFGStmt>(); const Stmt *St = CS ? CS->getStmt() : 0; PostStmt Loc(St, N->getLocationContext()); if (Loc == N->getLocation()) { // Note: 'N' should be a fresh node because otherwise it shouldn't be // a member of Deferred. WList->enqueue(N, Block, Idx+1); return; } bool IsNew; ExplodedNode *Succ = G->getNode(Loc, N->getState(), false, &IsNew); Succ->addPredecessor(N, *G); if (IsNew) WList->enqueue(Succ, Block, Idx+1); }
ExplodedNode * GenericNodeBuilderImpl::generateNodeImpl(const ProgramState *state, ExplodedNode *pred, ProgramPoint programPoint, bool asSink) { hasGeneratedNode = true; bool isNew; ExplodedNode *node = engine.getGraph().getNode(programPoint, state, &isNew); if (pred) node->addPredecessor(pred, engine.getGraph()); if (isNew) { if (asSink) { node->markAsSink(); sinksGenerated.push_back(node); } return node; } return 0; }
ExplodedNode* EndOfFunctionNodeBuilder::generateNode(const ProgramState *State, ExplodedNode *P, const ProgramPointTag *tag) { hasGeneratedNode = true; bool IsNew; ExplodedNode *Node = Eng.G->getNode(BlockEntrance(&B, Pred->getLocationContext(), tag ? tag : Tag), State, &IsNew); Node->addPredecessor(P ? P : Pred, *Eng.G); if (IsNew) { Eng.G->addEndOfPath(Node); return Node; } return NULL; }
ExplodedNode* GRIndirectGotoNodeBuilder::generateNode(const iterator& I, const GRState* St, bool isSink) { bool IsNew; ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), St, &IsNew); Succ->addPredecessor(Pred, *Eng.G); if (IsNew) { if (isSink) Succ->markAsSink(); else Eng.WList->Enqueue(Succ); return Succ; } return NULL; }
void CallInliner::EvalCall(ExplodedNodeSet& Dst, GRExprEngine& Engine, GRStmtNodeBuilder& Builder, CallExpr* CE, SVal L, ExplodedNode* Pred) { FunctionDecl const *FD = L.getAsFunctionDecl(); if (!FD) return; // GRExprEngine is responsible for the autotransition. // Make a new LocationContext. StackFrameContext const *LocCtx = Engine.getAnalysisManager().getStackFrame(FD, Pred->getLocationContext(), CE); CFGBlock const *Entry = &(LocCtx->getCFG()->getEntry()); assert (Entry->empty() && "Entry block must be empty."); assert (Entry->succ_size() == 1 && "Entry block must have 1 successor."); // Get the solitary successor. CFGBlock const *SuccB = *(Entry->succ_begin()); // Construct an edge representing the starting location in the function. BlockEdge Loc(Entry, SuccB, LocCtx); GRState const *state = Builder.GetState(Pred); state = Engine.getStoreManager().EnterStackFrame(state, LocCtx); bool isNew; ExplodedNode *SuccN = Engine.getGraph().getNode(Loc, state, &isNew); SuccN->addPredecessor(Pred, Engine.getGraph()); Builder.Deferred.erase(Pred); // This is a hack. We really should not use the GRStmtNodeBuilder. if (isNew) Builder.getWorkList()->Enqueue(SuccN); Builder.HasGeneratedNode = true; }
ExplodedNode* GRSwitchNodeBuilder::generateDefaultCaseNode(const GRState* St, bool isSink) { // Get the block for the default case. assert (Src->succ_rbegin() != Src->succ_rend()); CFGBlock* DefaultBlock = *Src->succ_rbegin(); bool IsNew; ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, Pred->getLocationContext()), St, &IsNew); Succ->addPredecessor(Pred, *Eng.G); if (IsNew) { if (isSink) Succ->markAsSink(); else Eng.WList->Enqueue(Succ); return Succ; } return NULL; }
ExplodedGraph* ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, const ExplodedNode* const* EndSources, InterExplodedGraphMap* M, llvm::DenseMap<const void*, const void*> *InverseMap) const { typedef llvm::DenseSet<const ExplodedNode*> Pass1Ty; Pass1Ty Pass1; typedef llvm::DenseMap<const ExplodedNode*, ExplodedNode*> Pass2Ty; Pass2Ty& Pass2 = M->M; SmallVector<const ExplodedNode*, 10> WL1, WL2; // ===- Pass 1 (reverse DFS) -=== for (const ExplodedNode* const* I = BeginSources; I != EndSources; ++I) { assert(*I); WL1.push_back(*I); } // Process the first worklist until it is empty. Because it is a std::list // it acts like a FIFO queue. while (!WL1.empty()) { const ExplodedNode *N = WL1.back(); WL1.pop_back(); // Have we already visited this node? If so, continue to the next one. if (Pass1.count(N)) continue; // Otherwise, mark this node as visited. Pass1.insert(N); // If this is a root enqueue it to the second worklist. if (N->Preds.empty()) { WL2.push_back(N); continue; } // Visit our predecessors and enqueue them. for (ExplodedNode::pred_iterator I = N->Preds.begin(), E = N->Preds.end(); I != E; ++I) WL1.push_back(*I); } // We didn't hit a root? Return with a null pointer for the new graph. if (WL2.empty()) return 0; // Create an empty graph. ExplodedGraph* G = MakeEmptyGraph(); // ===- Pass 2 (forward DFS to construct the new graph) -=== while (!WL2.empty()) { const ExplodedNode *N = WL2.back(); WL2.pop_back(); // Skip this node if we have already processed it. if (Pass2.find(N) != Pass2.end()) continue; // Create the corresponding node in the new graph and record the mapping // from the old node to the new node. ExplodedNode *NewN = G->getNode(N->getLocation(), N->State, N->isSink(), 0); Pass2[N] = NewN; // Also record the reverse mapping from the new node to the old node. if (InverseMap) (*InverseMap)[NewN] = N; // If this node is a root, designate it as such in the graph. if (N->Preds.empty()) G->addRoot(NewN); // In the case that some of the intended predecessors of NewN have already // been created, we should hook them up as predecessors. // Walk through the predecessors of 'N' and hook up their corresponding // nodes in the new graph (if any) to the freshly created node. for (ExplodedNode::pred_iterator I = N->Preds.begin(), E = N->Preds.end(); I != E; ++I) { Pass2Ty::iterator PI = Pass2.find(*I); if (PI == Pass2.end()) continue; NewN->addPredecessor(PI->second, *G); } // In the case that some of the intended successors of NewN have already // been created, we should hook them up as successors. Otherwise, enqueue // the new nodes from the original graph that should have nodes created // in the new graph. for (ExplodedNode::succ_iterator I = N->Succs.begin(), E = N->Succs.end(); I != E; ++I) { Pass2Ty::iterator PI = Pass2.find(*I); if (PI != Pass2.end()) { PI->second->addPredecessor(NewN, *G); continue; } // Enqueue nodes to the worklist that were marked during pass 1. if (Pass1.count(*I)) WL2.push_back(*I); } } return G; }
/// The call exit is simulated with a sequence of nodes, which occur between /// CallExitBegin and CallExitEnd. The following operations occur between the /// two program points: /// 1. CallExitBegin (triggers the start of call exit sequence) /// 2. Bind the return value /// 3. Run Remove dead bindings to clean up the dead symbols from the callee. /// 4. CallExitEnd (switch to the caller context) /// 5. PostStmt<CallExpr> void ExprEngine::processCallExit(ExplodedNode *CEBNode) { // Step 1 CEBNode was generated before the call. PrettyStackTraceLocationContext CrashInfo(CEBNode->getLocationContext()); const StackFrameContext *calleeCtx = CEBNode->getLocationContext()->getCurrentStackFrame(); // The parent context might not be a stack frame, so make sure we // look up the first enclosing stack frame. const StackFrameContext *callerCtx = calleeCtx->getParent()->getCurrentStackFrame(); const Stmt *CE = calleeCtx->getCallSite(); ProgramStateRef state = CEBNode->getState(); // Find the last statement in the function and the corresponding basic block. const Stmt *LastSt = nullptr; const CFGBlock *Blk = nullptr; std::tie(LastSt, Blk) = getLastStmt(CEBNode); // Generate a CallEvent /before/ cleaning the state, so that we can get the // correct value for 'this' (if necessary). CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<> Call = CEMgr.getCaller(calleeCtx, state); // Step 2: generate node with bound return value: CEBNode -> BindedRetNode. // If the callee returns an expression, bind its value to CallExpr. if (CE) { if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) { const LocationContext *LCtx = CEBNode->getLocationContext(); SVal V = state->getSVal(RS, LCtx); // Ensure that the return type matches the type of the returned Expr. if (wasDifferentDeclUsedForInlining(Call, calleeCtx)) { QualType ReturnedTy = CallEvent::getDeclaredResultType(calleeCtx->getDecl()); if (!ReturnedTy.isNull()) { if (const Expr *Ex = dyn_cast<Expr>(CE)) { V = adjustReturnValue(V, Ex->getType(), ReturnedTy, getStoreManager()); } } } state = state->BindExpr(CE, callerCtx, V); } // Bind the constructed object value to CXXConstructExpr. if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) { loc::MemRegionVal This = svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx); SVal ThisV = state->getSVal(This); // If the constructed object is a temporary prvalue, get its bindings. if (isTemporaryPRValue(CCE, ThisV)) ThisV = state->getSVal(ThisV.castAs<Loc>()); state = state->BindExpr(CCE, callerCtx, ThisV); } } // Step 3: BindedRetNode -> CleanedNodes // If we can find a statement and a block in the inlined function, run remove // dead bindings before returning from the call. This is important to ensure // that we report the issues such as leaks in the stack contexts in which // they occurred. ExplodedNodeSet CleanedNodes; if (LastSt && Blk && AMgr.options.AnalysisPurgeOpt != PurgeNone) { static SimpleProgramPointTag retValBind("ExprEngine", "Bind Return Value"); PostStmt Loc(LastSt, calleeCtx, &retValBind); bool isNew; ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew); BindedRetNode->addPredecessor(CEBNode, G); if (!isNew) return; NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode); currBldrCtx = &Ctx; // Here, we call the Symbol Reaper with 0 statement and callee location // context, telling it to clean up everything in the callee's context // (and its children). We use the callee's function body as a diagnostic // statement, with which the program point will be associated. removeDead(BindedRetNode, CleanedNodes, nullptr, calleeCtx, calleeCtx->getAnalysisDeclContext()->getBody(), ProgramPoint::PostStmtPurgeDeadSymbolsKind); currBldrCtx = nullptr; } else { CleanedNodes.Add(CEBNode); } for (ExplodedNodeSet::iterator I = CleanedNodes.begin(), E = CleanedNodes.end(); I != E; ++I) { // Step 4: Generate the CallExit and leave the callee's context. // CleanedNodes -> CEENode CallExitEnd Loc(calleeCtx, callerCtx); bool isNew; ProgramStateRef CEEState = (*I == CEBNode) ? state : (*I)->getState(); ExplodedNode *CEENode = G.getNode(Loc, CEEState, false, &isNew); CEENode->addPredecessor(*I, G); if (!isNew) return; // Step 5: Perform the post-condition check of the CallExpr and enqueue the // result onto the work list. // CEENode -> Dst -> WorkList NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode); SaveAndRestore<const NodeBuilderContext*> NBCSave(currBldrCtx, &Ctx); SaveAndRestore<unsigned> CBISave(currStmtIdx, calleeCtx->getIndex()); CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState); ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, *UpdatedCall, *this, /*WasInlined=*/true); ExplodedNodeSet Dst; if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) { getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg, *this, /*WasInlined=*/true); } else if (CE) { getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE, *this, /*WasInlined=*/true); } else { Dst.insert(DstPostCall); } // Enqueue the next element in the block. for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end(); PSI != PSE; ++PSI) { Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(), calleeCtx->getIndex()+1); } } }
void CallEnterNodeBuilder::generateNode(const ProgramState *state) { // Check if the callee is in the same translation unit. if (CalleeCtx->getTranslationUnit() != Pred->getLocationContext()->getTranslationUnit()) { // Create a new engine. We must be careful that the new engine should not // reference data structures owned by the old engine. AnalysisManager &OldMgr = Eng.SubEng.getAnalysisManager(); // Get the callee's translation unit. idx::TranslationUnit *TU = CalleeCtx->getTranslationUnit(); // Create a new AnalysisManager with components of the callee's // TranslationUnit. // The Diagnostic is actually shared when we create ASTUnits from AST files. AnalysisManager AMgr(TU->getASTContext(), TU->getDiagnostic(), OldMgr.getLangOptions(), OldMgr.getPathDiagnosticClient(), OldMgr.getStoreManagerCreator(), OldMgr.getConstraintManagerCreator(), OldMgr.getCheckerManager(), OldMgr.getIndexer(), OldMgr.getMaxNodes(), OldMgr.getMaxVisit(), OldMgr.shouldVisualizeGraphviz(), OldMgr.shouldVisualizeUbigraph(), OldMgr.shouldPurgeDead(), OldMgr.shouldEagerlyAssume(), OldMgr.shouldTrimGraph(), OldMgr.shouldInlineCall(), OldMgr.getAnalysisContextManager().getUseUnoptimizedCFG(), OldMgr.getAnalysisContextManager(). getCFGBuildOptions().AddImplicitDtors, OldMgr.getAnalysisContextManager(). getCFGBuildOptions().AddInitializers, OldMgr.shouldEagerlyTrimExplodedGraph()); // Create the new engine. // FIXME: This cast isn't really safe. bool GCEnabled = static_cast<ExprEngine&>(Eng.SubEng).isObjCGCEnabled(); ExprEngine NewEng(AMgr, GCEnabled); // Create the new LocationContext. AnalysisContext *NewAnaCtx = AMgr.getAnalysisContext(CalleeCtx->getDecl(), CalleeCtx->getTranslationUnit()); const StackFrameContext *OldLocCtx = CalleeCtx; const StackFrameContext *NewLocCtx = AMgr.getStackFrame(NewAnaCtx, OldLocCtx->getParent(), OldLocCtx->getCallSite(), OldLocCtx->getCallSiteBlock(), OldLocCtx->getIndex()); // Now create an initial state for the new engine. const ProgramState *NewState = NewEng.getStateManager().MarshalState(state, NewLocCtx); ExplodedNodeSet ReturnNodes; NewEng.ExecuteWorkListWithInitialState(NewLocCtx, AMgr.getMaxNodes(), NewState, ReturnNodes); return; } // Get the callee entry block. const CFGBlock *Entry = &(CalleeCtx->getCFG()->getEntry()); assert(Entry->empty()); assert(Entry->succ_size() == 1); // Get the solitary successor. const CFGBlock *SuccB = *(Entry->succ_begin()); // Construct an edge representing the starting location in the callee. BlockEdge Loc(Entry, SuccB, CalleeCtx); bool isNew; ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); Node->addPredecessor(const_cast<ExplodedNode*>(Pred), *Eng.G); if (isNew) Eng.WList->enqueue(Node); }
std::unique_ptr<ExplodedGraph> ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks, InterExplodedGraphMap *ForwardMap, InterExplodedGraphMap *InverseMap) const { if (Nodes.empty()) return nullptr; typedef llvm::DenseSet<const ExplodedNode*> Pass1Ty; Pass1Ty Pass1; typedef InterExplodedGraphMap Pass2Ty; InterExplodedGraphMap Pass2Scratch; Pass2Ty &Pass2 = ForwardMap ? *ForwardMap : Pass2Scratch; SmallVector<const ExplodedNode*, 10> WL1, WL2; // ===- Pass 1 (reverse DFS) -=== for (ArrayRef<const NodeTy *>::iterator I = Sinks.begin(), E = Sinks.end(); I != E; ++I) { if (*I) WL1.push_back(*I); } // Process the first worklist until it is empty. while (!WL1.empty()) { const ExplodedNode *N = WL1.pop_back_val(); // Have we already visited this node? If so, continue to the next one. if (!Pass1.insert(N).second) continue; // If this is a root enqueue it to the second worklist. if (N->Preds.empty()) { WL2.push_back(N); continue; } // Visit our predecessors and enqueue them. WL1.append(N->Preds.begin(), N->Preds.end()); } // We didn't hit a root? Return with a null pointer for the new graph. if (WL2.empty()) return nullptr; // Create an empty graph. std::unique_ptr<ExplodedGraph> G = MakeEmptyGraph(); // ===- Pass 2 (forward DFS to construct the new graph) -=== while (!WL2.empty()) { const ExplodedNode *N = WL2.pop_back_val(); // Skip this node if we have already processed it. if (Pass2.find(N) != Pass2.end()) continue; // Create the corresponding node in the new graph and record the mapping // from the old node to the new node. ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State, N->isSink()); Pass2[N] = NewN; // Also record the reverse mapping from the new node to the old node. if (InverseMap) (*InverseMap)[NewN] = N; // If this node is a root, designate it as such in the graph. if (N->Preds.empty()) G->addRoot(NewN); // In the case that some of the intended predecessors of NewN have already // been created, we should hook them up as predecessors. // Walk through the predecessors of 'N' and hook up their corresponding // nodes in the new graph (if any) to the freshly created node. for (ExplodedNode::pred_iterator I = N->Preds.begin(), E = N->Preds.end(); I != E; ++I) { Pass2Ty::iterator PI = Pass2.find(*I); if (PI == Pass2.end()) continue; NewN->addPredecessor(const_cast<ExplodedNode *>(PI->second), *G); } // In the case that some of the intended successors of NewN have already // been created, we should hook them up as successors. Otherwise, enqueue // the new nodes from the original graph that should have nodes created // in the new graph. for (ExplodedNode::succ_iterator I = N->Succs.begin(), E = N->Succs.end(); I != E; ++I) { Pass2Ty::iterator PI = Pass2.find(*I); if (PI != Pass2.end()) { const_cast<ExplodedNode *>(PI->second)->addPredecessor(NewN, *G); continue; } // Enqueue nodes to the worklist that were marked during pass 1. if (Pass1.count(*I)) WL2.push_back(*I); } } return G; }
/// The call exit is simulated with a sequence of nodes, which occur between /// CallExitBegin and CallExitEnd. The following operations occur between the /// two program points: /// 1. CallExitBegin (triggers the start of call exit sequence) /// 2. Bind the return value /// 3. Run Remove dead bindings to clean up the dead symbols from the callee. /// 4. CallExitEnd (switch to the caller context) /// 5. PostStmt<CallExpr> void ExprEngine::processCallExit(ExplodedNode *CEBNode) { // Step 1 CEBNode was generated before the call. const StackFrameContext *calleeCtx = CEBNode->getLocationContext()->getCurrentStackFrame(); const LocationContext *callerCtx = calleeCtx->getParent(); const Stmt *CE = calleeCtx->getCallSite(); ProgramStateRef state = CEBNode->getState(); // Find the last statement in the function and the corresponding basic block. const Stmt *LastSt = 0; const CFGBlock *Blk = 0; llvm::tie(LastSt, Blk) = getLastStmt(CEBNode); // Step 2: generate node with binded return value: CEBNode -> BindedRetNode. // If the callee returns an expression, bind its value to CallExpr. if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) { const LocationContext *LCtx = CEBNode->getLocationContext(); SVal V = state->getSVal(RS, LCtx); state = state->BindExpr(CE, callerCtx, V); } // Bind the constructed object value to CXXConstructExpr. if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) { const CXXThisRegion *ThisR = getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx); SVal ThisV = state->getSVal(ThisR); // Always bind the region to the CXXConstructExpr. state = state->BindExpr(CCE, CEBNode->getLocationContext(), ThisV); } static SimpleProgramPointTag retValBindTag("ExprEngine : Bind Return Value"); PostStmt Loc(LastSt, calleeCtx, &retValBindTag); bool isNew; ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew); BindedRetNode->addPredecessor(CEBNode, G); if (!isNew) return; // Step 3: BindedRetNode -> CleanedNodes // If we can find a statement and a block in the inlined function, run remove // dead bindings before returning from the call. This is important to ensure // that we report the issues such as leaks in the stack contexts in which // they occurred. ExplodedNodeSet CleanedNodes; if (LastSt && Blk) { NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode); currentBuilderContext = &Ctx; // Here, we call the Symbol Reaper with 0 statement and caller location // context, telling it to clean up everything in the callee's context // (and it's children). We use LastStmt as a diagnostic statement, which // which the PreStmtPurge Dead point will be associated. removeDead(BindedRetNode, CleanedNodes, 0, callerCtx, LastSt, ProgramPoint::PostStmtPurgeDeadSymbolsKind); currentBuilderContext = 0; } for (ExplodedNodeSet::iterator I = CleanedNodes.begin(), E = CleanedNodes.end(); I != E; ++I) { // Step 4: Generate the CallExit and leave the callee's context. // CleanedNodes -> CEENode CallExitEnd Loc(CE, callerCtx); bool isNew; ExplodedNode *CEENode = G.getNode(Loc, (*I)->getState(), false, &isNew); CEENode->addPredecessor(*I, G); if (!isNew) return; // Step 5: Perform the post-condition check of the CallExpr and enqueue the // result onto the work list. // CEENode -> Dst -> WorkList ExplodedNodeSet Dst; NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode); SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext, &Ctx); SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex()); getCheckerManager().runCheckersForPostStmt(Dst, CEENode, CE, *this, true); // Enqueue the next element in the block. for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end(); PSI != PSE; ++PSI) { Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(), calleeCtx->getIndex()+1); } } }