diff options
-rw-r--r-- | include/clang/Analysis/ProgramPoint.h | 74 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h | 4 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/CheckerManager.h | 3 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h | 2 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 22 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h | 6 | ||||
-rw-r--r-- | lib/Analysis/ProgramPoint.cpp | 6 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/BugReporter.cpp | 10 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CheckerManager.cpp | 17 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CoreEngine.cpp | 13 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExplodedGraph.cpp | 5 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngine.cpp | 125 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 147 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/PathDiagnostic.cpp | 11 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/SymbolManager.cpp | 7 | ||||
-rw-r--r-- | test/Analysis/malloc-plist.c | 7213 |
16 files changed, 4817 insertions, 2848 deletions
diff --git a/include/clang/Analysis/ProgramPoint.h b/include/clang/Analysis/ProgramPoint.h index aa7a33c956..377f1bc256 100644 --- a/include/clang/Analysis/ProgramPoint.h +++ b/include/clang/Analysis/ProgramPoint.h @@ -45,14 +45,16 @@ public: PostLoadKind, PreStoreKind, PostStoreKind, - PostPurgeDeadSymbolsKind, + PostStmtPurgeDeadSymbolsKind, + PreStmtPurgeDeadSymbolsKind, PostConditionKind, PostLValueKind, PostInitializerKind, CallEnterKind, - CallExitKind, + CallExitBeginKind, + CallExitEndKind, MinPostStmtKind = PostStmtKind, - MaxPostStmtKind = CallExitKind, + MaxPostStmtKind = CallExitEndKind, EpsilonKind}; private: @@ -113,6 +115,14 @@ public: return (Kind) x; } + /// \brief Is this a program point corresponding to purge/removal of dead + /// symbols and bindings. + bool isPurgeKind() { + Kind K = getKind(); + return (K == PostStmtPurgeDeadSymbolsKind || + K == PreStmtPurgeDeadSymbolsKind); + } + const ProgramPointTag *getTag() const { return Tag; } const LocationContext *getLocationContext() const { @@ -340,14 +350,29 @@ public: } }; -class PostPurgeDeadSymbols : public PostStmt { +/// \class Represents a point after we ran remove dead bindings BEFORE +/// processing the given statement. +class PreStmtPurgeDeadSymbols : public PostStmt { public: - PostPurgeDeadSymbols(const Stmt *S, const LocationContext *L, + PreStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L, const ProgramPointTag *tag = 0) - : PostStmt(S, PostPurgeDeadSymbolsKind, L, tag) {} + : PostStmt(S, PreStmtPurgeDeadSymbolsKind, L, tag) { } static bool classof(const ProgramPoint* Location) { - return Location->getKind() == PostPurgeDeadSymbolsKind; + return Location->getKind() == PreStmtPurgeDeadSymbolsKind; + } +}; + +/// \class Represents a point after we ran remove dead bindings AFTER +/// processing the given statement. +class PostStmtPurgeDeadSymbols : public PostStmt { +public: + PostStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L, + const ProgramPointTag *tag = 0) + : PostStmt(S, PostStmtPurgeDeadSymbolsKind, L, tag) { } + + static bool classof(const ProgramPoint* Location) { + return Location->getKind() == PostStmtPurgeDeadSymbolsKind; } }; @@ -383,6 +408,7 @@ public: } }; +/// \class Represents a point when we begin processing an inlined call. class CallEnter : public StmtPoint { public: CallEnter(const Stmt *stmt, const StackFrameContext *calleeCtx, @@ -402,14 +428,38 @@ public: } }; -class CallExit : public StmtPoint { +/// \class Represents a point when we start the call exit sequence (for +/// inlined call). +/// +/// 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: +/// - CallExitBegin +/// - Bind the return value +/// - Run Remove dead bindings (to clean up the dead symbols from the callee). +/// - CallExitEnd +class CallExitBegin : public StmtPoint { +public: + // CallExitBegin uses the callee's location context. + CallExitBegin(const Stmt *S, const LocationContext *L) + : StmtPoint(S, 0, CallExitBeginKind, L, 0) {} + + static bool classof(const ProgramPoint *Location) { + return Location->getKind() == CallExitBeginKind; + } +}; + +/// \class Represents a point when we finish the call exit sequence (for +/// inlined call). +/// \sa CallExitBegin +class CallExitEnd : public StmtPoint { public: - // CallExit uses the callee's location context. - CallExit(const Stmt *S, const LocationContext *L) - : StmtPoint(S, 0, CallExitKind, L, 0) {} + // CallExitEnd uses the caller's location context. + CallExitEnd(const Stmt *S, const LocationContext *L) + : StmtPoint(S, 0, CallExitEndKind, L, 0) {} static bool classof(const ProgramPoint *Location) { - return Location->getKind() == CallExitKind; + return Location->getKind() == CallExitEndKind; } }; diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h index 5a8a1c78ca..730a558b74 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h @@ -362,7 +362,7 @@ public: /// \brief Interface for classes constructing Stack hints. /// /// If a PathDiagnosticEvent occurs in a different frame than the final -/// diagnostic the hints can be used to summarise the effect of the call. +/// diagnostic the hints can be used to summarize the effect of the call. class StackHintGenerator { public: virtual ~StackHintGenerator() = 0; @@ -510,7 +510,7 @@ public: } static PathDiagnosticCallPiece *construct(const ExplodedNode *N, - const CallExit &CE, + const CallExitEnd &CE, const SourceManager &SM); static PathDiagnosticCallPiece *construct(PathPieces &pieces, diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index d215f992e6..252d36d69b 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -272,7 +272,8 @@ public: void runCheckersForDeadSymbols(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SymbolReaper &SymReaper, const Stmt *S, - ExprEngine &Eng); + ExprEngine &Eng, + ProgramPoint::Kind K); /// \brief True if at least one checker wants to check region changes. bool wantsRegionChangeUpdate(ProgramStateRef state); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index f5984021d8..5bd566ff63 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -104,7 +104,7 @@ private: CoreEngine(const CoreEngine&); // Do not implement. CoreEngine& operator=(const CoreEngine&); - ExplodedNode *generateCallExitNode(ExplodedNode *N); + ExplodedNode *generateCallExitBeginNode(ExplodedNode *N); public: /// Construct a CoreEngine object to analyze the provided CFG using diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 2a21a03e9a..73f39b1bce 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -151,6 +151,25 @@ public: ExplodedGraph& getGraph() { return G; } const ExplodedGraph& getGraph() const { return G; } + /// \brief Run the analyzer's garbage collection - remove dead symbols and + /// bindings. + /// + /// \param Node - The predecessor node, from which the processing should + /// start. + /// \param Out - The returned set of output nodes. + /// \param ReferenceStmt - Run garbage collection using the symbols, + /// which are live before the given statement. + /// \param LC - The location context of the ReferenceStmt. + /// \param DiagnosticStmt - the statement used to associate the diagnostic + /// message, if any warnings should occur while removing the dead (leaks + /// are usually reported here). + /// \param K - In some cases it is possible to use PreStmt kind. (Do + /// not use it unless you know what you are doing.) + void removeDead(ExplodedNode *Node, ExplodedNodeSet &Out, + const Stmt *ReferenceStmt, const LocationContext *LC, + const Stmt *DiagnosticStmt, + ProgramPoint::Kind K = ProgramPoint::PreStmtPurgeDeadSymbolsKind); + /// processCFGElement - Called by CoreEngine. Used to generate new successor /// nodes by processing the 'effects' of a CFG element. void processCFGElement(const CFGElement E, ExplodedNode *Pred, @@ -199,7 +218,8 @@ public: /// Generate the entry node of the callee. void processCallEnter(CallEnter CE, ExplodedNode *Pred); - /// Generate the first post callsite node. + /// Generate the sequence of nodes that simulate the call exit and the post + /// visit for CallExpr. void processCallExit(ExplodedNode *Pred); /// Called by CoreEngine when the analysis worklist has terminated. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h index 69f5e7ca6b..4d329d2f10 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -553,6 +553,7 @@ public: BasicValueFactory &getBasicVals() { return BV; } }; +/// \class A class responsible for cleaning up unused symbols. class SymbolReaper { enum SymbolStatus { NotProcessed, @@ -576,6 +577,11 @@ class SymbolReaper { llvm::DenseMap<const MemRegion *, unsigned> includedRegionCache; public: + /// \brief Construct a reaper object, which removes everything which is not + /// live before we execute statement s in the given location context. + /// + /// If the statement is NULL, everything is this and parent contexts is + /// considered live. SymbolReaper(const LocationContext *ctx, const Stmt *s, SymbolManager& symmgr, StoreManager &storeMgr) : LCtx(ctx), Loc(s), SymMgr(symmgr), reapedStore(0, storeMgr) {} diff --git a/lib/Analysis/ProgramPoint.cpp b/lib/Analysis/ProgramPoint.cpp index 3f711b447a..7d67e8a91c 100644 --- a/lib/Analysis/ProgramPoint.cpp +++ b/lib/Analysis/ProgramPoint.cpp @@ -36,8 +36,10 @@ ProgramPoint ProgramPoint::getProgramPoint(const Stmt *S, ProgramPoint::Kind K, return PreStore(S, LC, tag); case ProgramPoint::PostLValueKind: return PostLValue(S, LC, tag); - case ProgramPoint::PostPurgeDeadSymbolsKind: - return PostPurgeDeadSymbols(S, LC, tag); + case ProgramPoint::PostStmtPurgeDeadSymbolsKind: + return PostStmtPurgeDeadSymbols(S, LC, tag); + case ProgramPoint::PreStmtPurgeDeadSymbolsKind: + return PreStmtPurgeDeadSymbols(S, LC, tag); } } diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index a2642120b7..4330d2ed47 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -427,7 +427,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); - if (const CallExit *CE = dyn_cast<CallExit>(&P)) { + if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) { PathDiagnosticCallPiece *C = PathDiagnosticCallPiece::construct(N, *CE, SMgr); PD.getActivePath().push_front(C); @@ -440,7 +440,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, PD.popActivePath(); // The current active path should never be empty. Either we // just added a bunch of stuff to the top-level path, or - // we have a previous CallExit. If the front of the active + // we have a previous CallExitEnd. If the front of the active // path is not a PathDiagnosticCallPiece, it means that the // path terminated within a function call. We must then take the // current contents of the active path and place it within @@ -1066,10 +1066,10 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); do { - if (const CallExit *CE = dyn_cast<CallExit>(&P)) { + if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) { const StackFrameContext *LCtx = CE->getLocationContext()->getCurrentStackFrame(); - PathDiagnosticLocation Loc(LCtx->getCallSite(), + PathDiagnosticLocation Loc(CE->getStmt(), PDB.getSourceManager(), LCtx); EB.addEdge(Loc, true); @@ -1099,7 +1099,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, // The current active path should never be empty. Either we // just added a bunch of stuff to the top-level path, or - // we have a previous CallExit. If the front of the active + // we have a previous CallExitEnd. If the front of the active // path is not a PathDiagnosticCallPiece, it means that the // path terminated within a function call. We must then take the // current contents of the active path and place it within diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 0bcc343fba..8a6ea1d0f8 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -381,21 +381,25 @@ namespace { SymbolReaper &SR; const Stmt *S; ExprEngine &Eng; + ProgramPoint::Kind ProgarmPointKind; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr, - const Stmt *s, ExprEngine &eng) - : Checkers(checkers), SR(sr), S(s), Eng(eng) { } + const Stmt *s, ExprEngine &eng, + ProgramPoint::Kind K) + : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) { } void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { - ProgramPoint::Kind K = ProgramPoint::PostPurgeDeadSymbolsKind; - const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, ProgarmPointKind, Pred->getLocationContext(), checkFn.Checker); CheckerContext C(Bldr, Eng, Pred, L); + // Note, do not pass the statement to the checkers without letting them + // differentiate if we ran remove dead bindings before or after the + // statement. checkFn(SR, C); } }; @@ -406,8 +410,9 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SymbolReaper &SymReaper, const Stmt *S, - ExprEngine &Eng) { - CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng); + ExprEngine &Eng, + ProgramPoint::Kind K) { + CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng, K); expandGraphWithCheckers(C, Dst, Src); } diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index ca662c7992..c9de20e500 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -248,7 +248,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, break; } - case ProgramPoint::CallExitKind: + case ProgramPoint::CallExitBeginKind: SubEng.processCallExit(Pred); break; @@ -531,14 +531,14 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N, WList->enqueue(Succ, Block, Idx+1); } -ExplodedNode *CoreEngine::generateCallExitNode(ExplodedNode *N) { - // Create a CallExit node and enqueue it. +ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N) { + // Create a CallExitBegin node and enqueue it. const StackFrameContext *LocCtx = cast<StackFrameContext>(N->getLocationContext()); const Stmt *CE = LocCtx->getCallSite(); // Use the the callee location context. - CallExit Loc(CE, LocCtx); + CallExitBegin Loc(CE, LocCtx); bool isNew; ExplodedNode *Node = G->getNode(Loc, N->getState(), false, &isNew); @@ -565,12 +565,13 @@ void CoreEngine::enqueue(ExplodedNodeSet &Set, void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set) { for (ExplodedNodeSet::iterator I = Set.begin(), E = Set.end(); I != E; ++I) { ExplodedNode *N = *I; - // If we are in an inlined call, generate CallExit node. + // If we are in an inlined call, generate CallExitBegin node. if (N->getLocationContext()->getParent()) { - N = generateCallExitNode(N); + N = generateCallExitBeginNode(N); if (N) WList->enqueue(N); } else { + // TODO: We should run remove dead bindings here. G->addEndOfPath(N); NumPathsExplored++; } diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index 0dcbe1ff4a..e433c353c1 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -57,7 +57,7 @@ ExplodedGraph::~ExplodedGraph() {} //===----------------------------------------------------------------------===// bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { - // Reclaimn all nodes that match *all* the following criteria: + // Reclaim all nodes that match *all* the following criteria: // // (1) 1 predecessor (that has one successor) // (2) 1 successor (that has one predecessor) @@ -83,7 +83,8 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // Condition 3. ProgramPoint progPoint = node->getLocation(); if (!isa<PostStmt>(progPoint) || - (isa<CallEnter>(progPoint) || isa<CallExit>(progPoint))) + (isa<CallEnter>(progPoint) || + isa<CallExitBegin>(progPoint) || isa<CallExitEnd>(progPoint))) return false; // Condition 4. diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index abc6316538..1fd4dcf712 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -42,8 +42,6 @@ using llvm::APSInt; STATISTIC(NumRemoveDeadBindings, "The # of times RemoveDeadBindings is called"); -STATISTIC(NumRemoveDeadBindingsSkipped, - "The # of times RemoveDeadBindings is skipped"); STATISTIC(NumMaxBlockCountReached, "The # of aborted paths due to reaching the maximum block count in " "a top level function"); @@ -231,6 +229,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Pred); return; } + currentBuilderContext = 0; } static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, @@ -260,62 +259,47 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, return !PM.isConsumedExpr(cast<Expr>(S.getStmt())); } -void ExprEngine::ProcessStmt(const CFGStmt S, - ExplodedNode *Pred) { - // Reclaim any unnecessary nodes in the ExplodedGraph. - G.reclaimRecentlyAllocatedNodes(); - - currentStmt = S.getStmt(); - PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), - currentStmt->getLocStart(), - "Error evaluating statement"); - - EntryNode = Pred; - - ProgramStateRef EntryState = EntryNode->getState(); - CleanedState = EntryState; - - // Create the cleaned state. - const LocationContext *LC = EntryNode->getLocationContext(); - SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager()); - - if (shouldRemoveDeadBindings(AMgr, S, Pred, LC)) { - NumRemoveDeadBindings++; - getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); - - const StackFrameContext *SFC = LC->getCurrentStackFrame(); - - // Create a state in which dead bindings are removed from the environment - // and the store. TODO: The function should just return new env and store, - // not a new state. - CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper); - } else { - NumRemoveDeadBindingsSkipped++; - } +void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, + const Stmt *ReferenceStmt, + const LocationContext *LC, + const Stmt *DiagnosticStmt, + ProgramPoint::Kind K) { + assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind || + ReferenceStmt == 0) && "PreStmt is not generally supported by " + "the SymbolReaper yet"); + NumRemoveDeadBindings++; + CleanedState = Pred->getState(); + SymbolReaper SymReaper(LC, ReferenceStmt, SymMgr, getStoreManager()); + + getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); + + // Create a state in which dead bindings are removed from the environment + // and the store. TODO: The function should just return new env and store, + // not a new state. + const StackFrameContext *SFC = LC->getCurrentStackFrame(); + CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper); // Process any special transfer function for dead symbols. - ExplodedNodeSet Tmp; // A tag to track convenience transitions, which can be removed at cleanup. static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node"); - if (!SymReaper.hasDeadSymbols()) { // Generate a CleanedNode that has the environment and store cleaned // up. Since no symbols are dead, we can optimize and not clean out // the constraint manager. - StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext); - Bldr.generateNode(currentStmt, EntryNode, CleanedState, false, &cleanupTag); + StmtNodeBuilder Bldr(Pred, Out, *currentBuilderContext); + Bldr.generateNode(DiagnosticStmt, Pred, CleanedState, false, &cleanupTag,K); } else { // Call checkers with the non-cleaned state so that they could query the // values of the soon to be dead symbols. ExplodedNodeSet CheckedSet; - getCheckerManager().runCheckersForDeadSymbols(CheckedSet, EntryNode, - SymReaper, currentStmt, *this); + getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper, + DiagnosticStmt, *this, K); // For each node in CheckedSet, generate CleanedNodes that have the // environment, the store, and the constraints cleaned up but have the // user-supplied states as the predecessors. - StmtNodeBuilder Bldr(CheckedSet, Tmp, *currentBuilderContext); + StmtNodeBuilder Bldr(CheckedSet, Out, *currentBuilderContext); for (ExplodedNodeSet::const_iterator I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) { ProgramStateRef CheckerState = (*I)->getState(); @@ -324,10 +308,10 @@ void ExprEngine::ProcessStmt(const CFGStmt S, CheckerState = getConstraintManager().removeDeadBindings(CheckerState, SymReaper); - assert(StateMgr.haveEqualEnvironments(CheckerState, EntryState) && + assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) && "Checkers are not allowed to modify the Environment as a part of " "checkDeadSymbols processing."); - assert(StateMgr.haveEqualStores(CheckerState, EntryState) && + assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) && "Checkers are not allowed to modify the Store as a part of " "checkDeadSymbols processing."); @@ -335,13 +319,35 @@ void ExprEngine::ProcessStmt(const CFGStmt S, // generate a transition to that state. ProgramStateRef CleanedCheckerSt = StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); - Bldr.generateNode(currentStmt, *I, CleanedCheckerSt, false, &cleanupTag, - ProgramPoint::PostPurgeDeadSymbolsKind); + Bldr.generateNode(DiagnosticStmt, *I, CleanedCheckerSt, false, + &cleanupTag, K); } } +} + +void ExprEngine::ProcessStmt(const CFGStmt S, + ExplodedNode *Pred) { + // Reclaim any unnecessary nodes in the ExplodedGraph. + G.reclaimRecentlyAllocatedNodes(); + + currentStmt = S.getStmt(); + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + currentStmt->getLocStart(), + "Error evaluating statement"); + // Remove dead bindings and symbols. + EntryNode = Pred; + ExplodedNodeSet CleanedStates; + if (shouldRemoveDeadBindings(AMgr, S, Pred, EntryNode->getLocationContext())){ + removeDead(EntryNode, CleanedStates, currentStmt, + Pred->getLocationContext(), currentStmt); + } else + CleanedStates.Add(EntryNode); + + // Visit the statement. ExplodedNodeSet Dst; - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + for (ExplodedNodeSet::iterator I = CleanedStates.begin(), + E = CleanedStates.end(); I != E; ++I) { ExplodedNodeSet DstI; // Visit the statement. Visit(currentStmt, *I, DstI); @@ -994,7 +1000,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, continue; // We reached the caller. Find the node right before we started // processing the CallExpr. - if (isa<PostPurgeDeadSymbols>(L)) + if (L.isPurgeKind()) continue; if (const StmtPoint *SP = dyn_cast<StmtPoint>(&L)) if (SP->getStmt() == CalleeSF->getCallSite()) @@ -1861,10 +1867,17 @@ struct DOTGraphTraits<ExplodedNode*> : ProgramPoint Loc = N->getLocation(); switch (Loc.getKind()) { - case ProgramPoint::BlockEntranceKind: + case ProgramPoint::BlockEntranceKind: { Out << "Block Entrance: B" << cast<BlockEntrance>(Loc).getBlock()->getBlockID(); + if (const NamedDecl *ND = + dyn_cast<NamedDecl>(Loc.getLocationContext()->getDecl())) { + Out << " ("; + ND->printName(Out); + Out << ")"; + } break; + } case ProgramPoint::BlockExitKind: assert (false); @@ -1874,8 +1887,20 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "CallEnter"; break; - case ProgramPoint::CallExitKind: - Out << "CallExit"; + case ProgramPoint::CallExitBeginKind: + Out << "CallExitBegin"; + break; + + case ProgramPoint::CallExitEndKind: + Out << "CallExitEnd"; + break; + + case ProgramPoint::PostStmtPurgeDeadSymbolsKind: + Out << "PostStmtPurgeDeadSymbols"; + break; + + case ProgramPoint::PreStmtPurgeDeadSymbolsKind: + Out << "PreStmtPurgeDeadSymbols"; break; case ProgramPoint::EpsilonKind: diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index b9f4e153d7..4d0da06427 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -51,71 +51,140 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { Engine.getWorkList()->enqueue(Node); } -static const ReturnStmt *getReturnStmt(const ExplodedNode *Node) { +// Find the last statement on the path to the exploded node and the +// corresponding Block. +static std::pair<const Stmt*, + const CFGBlock*> getLastStmt(const ExplodedNode *Node) { + const Stmt *S = 0; + const CFGBlock *Blk = 0; + const StackFrameContext *SF = + Node->getLocation().getLocationContext()->getCurrentStackFrame(); while (Node) { const ProgramPoint &PP = Node->getLocation(); - // Skip any BlockEdges. - if (isa<BlockEdge>(PP) || isa<CallExit>(PP)) { + // Skip any BlockEdges, empty blocks, and the CallExitBegin node. + if (isa<BlockEdge>(PP) || isa<CallExitBegin>(PP) || isa<BlockEntrance>(PP)){ assert(Node->pred_size() == 1); Node = *Node->pred_begin(); continue; - } + } + // If we reached the CallEnter, the function has no statements. + if (isa<CallEnter>(PP)) + break; if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) { - const Stmt *S = SP->getStmt(); - return dyn_cast<ReturnStmt>(S); + S = SP->getStmt(); + // Now, get the enclosing basic block. + while (Node && Node->pred_size() >=1 ) { + const ProgramPoint &PP = Node->getLocation(); + if (isa<BlockEdge>(PP) && + (PP.getLocationContext()->getCurrentStackFrame() == SF)) { + BlockEdge &EPP = cast<BlockEdge>(PP); + Blk = EPP.getDst(); + break; + } + Node = *Node->pred_begin(); + } + break; } break; } - return 0; + return std::pair<const Stmt*, const CFGBlock*>(S, Blk); } -void ExprEngine::processCallExit(ExplodedNode *Pred) { - ProgramStateRef state = Pred->getState(); - const StackFrameContext *calleeCtx = - Pred->getLocationContext()->getCurrentStackFrame(); +/// 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 = getReturnStmt(Pred)) { - const LocationContext *LCtx = Pred->getLocationContext(); + 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); - + getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx); + SVal ThisV = state->getSVal(ThisR); // Always bind the region to the CXXConstructExpr. - state = state->BindExpr(CCE, Pred->getLocationContext(), ThisV); + state = state->BindExpr(CCE, CEBNode->getLocationContext(), ThisV); } - - static SimpleProgramPointTag returnTag("ExprEngine : Call Return"); - PostStmt Loc(CE, callerCtx, &returnTag); + + static SimpleProgramPointTag retValBindTag("ExprEngine : Bind Return Value"); + PostStmt Loc(LastSt, calleeCtx, &retValBindTag); bool isNew; - ExplodedNode *N = G.getNode(Loc, state, false, &isNew); - N->addPredecessor(Pred, G); + ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew); + BindedRetNode->addPredecessor(CEBNode, 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); + + // 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(getCoreEngi |