diff options
author | Anna Zaks <ganna@apple.com> | 2012-12-20 00:38:25 +0000 |
---|---|---|
committer | Anna Zaks <ganna@apple.com> | 2012-12-20 00:38:25 +0000 |
commit | dc15415da4cefd8494b8805906d0004abc66ae0a (patch) | |
tree | 2997984bdeab61595c06c62b904a1ec2fff889c7 /clang | |
parent | 8ac915055651e72b21f974760e4f2b55887591c8 (diff) | |
download | bcm5719-llvm-dc15415da4cefd8494b8805906d0004abc66ae0a.tar.gz bcm5719-llvm-dc15415da4cefd8494b8805906d0004abc66ae0a.zip |
[analyzer] Add the pointer escaped callback.
Instead of using several callbacks to identify the pointer escape event,
checkers now can register for the checkPointerEscape.
Converted the Malloc checker to use the new callback.
SimpleStreamChecker will be converted next.
llvm-svn: 170625
Diffstat (limited to 'clang')
16 files changed, 295 insertions, 127 deletions
diff --git a/clang/include/clang/StaticAnalyzer/Core/Checker.h b/clang/include/clang/StaticAnalyzer/Core/Checker.h index 9eb1248f6a7..61c1884544f 100644 --- a/clang/include/clang/StaticAnalyzer/Core/Checker.h +++ b/clang/include/clang/StaticAnalyzer/Core/Checker.h @@ -293,7 +293,7 @@ class RegionChanges { static ProgramStateRef _checkRegionChanges(void *checker, ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> Explicits, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) { @@ -317,6 +317,27 @@ public: } }; +class PointerEscape { + template <typename CHECKER> + static ProgramStateRef + _checkPointerEscape(void *checker, + ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call) { + return ((const CHECKER *)checker)->checkPointerEscape(State, + Escaped, + Call); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPointerEscape( + CheckerManager::CheckPointerEscapeFunc(checker, + _checkPointerEscape<CHECKER>)); + } +}; + template <typename EVENT> class Event { template <typename CHECKER> diff --git a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h index 710e9c1bca6..80e06c1e842 100644 --- a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -310,11 +310,27 @@ public: /// by a call. ProgramStateRef runCheckersForRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call); + /// \brief Run checkers when pointers escape. + /// + /// This notifies the checkers about pointer escape, which occurs whenever + /// the analzyer cannot track the symbol any more. For example, as a + /// result of assigning a pointer into a global or when it's passed to a + /// function call the analyzer cannot model. + /// + /// \param State The state at the point of escape. + /// \param Escaped The list of escaped symbols. + /// \param Call The corresponding CallEvent, if the symbols escape as + /// parameters to the given call. + /// \returns Checkers can modify the state by returning a new one. + ProgramStateRef runCheckersForPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call); + /// \brief Run checkers for handling assumptions on symbolic values. ProgramStateRef runCheckersForEvalAssume(ProgramStateRef state, SVal Cond, bool Assumption); @@ -393,13 +409,18 @@ public: typedef CheckerFn<void (ProgramStateRef,SymbolReaper &)> CheckLiveSymbolsFunc; typedef CheckerFn<ProgramStateRef (ProgramStateRef, - const StoreManager::InvalidatedSymbols *symbols, + const InvalidatedSymbols *symbols, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call)> CheckRegionChangesFunc; typedef CheckerFn<bool (ProgramStateRef)> WantsRegionChangeUpdateFunc; + + typedef CheckerFn<ProgramStateRef (ProgramStateRef, + const InvalidatedSymbols &Escaped, + const CallEvent *Call)> + CheckPointerEscapeFunc; typedef CheckerFn<ProgramStateRef (ProgramStateRef, const SVal &cond, bool assumption)> @@ -441,6 +462,8 @@ public: void _registerForRegionChanges(CheckRegionChangesFunc checkfn, WantsRegionChangeUpdateFunc wantUpdateFn); + void _registerForPointerEscape(CheckPointerEscapeFunc checkfn); + void _registerForEvalAssume(EvalAssumeFunc checkfn); void _registerForEvalCall(EvalCallFunc checkfn); @@ -566,6 +589,8 @@ private: }; std::vector<RegionChangesCheckerInfo> RegionChangesCheckers; + std::vector<CheckPointerEscapeFunc> PointerEscapeCheckers; + std::vector<EvalAssumeFunc> EvalAssumeCheckers; std::vector<EvalCallFunc> EvalCallCheckers; diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 151b0d6420a..7c56062152b 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -262,7 +262,7 @@ public: /// to the store. Used to update checkers that track region values. ProgramStateRef processRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call); @@ -459,6 +459,16 @@ protected: SVal location, SVal Val, bool atDeclInit = false, const ProgramPoint *PP = 0); + ProgramStateRef processPointerEscapedOnBind(ProgramStateRef State, + SVal Loc, SVal Val); + + ProgramStateRef processPointerEscapedOnInvalidateRegions( + ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const CallEvent *Call); + public: // FIXME: 'tag' should be removed, and a LocationContext should be used // instead. diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index cb8dd08a24c..0857d127b74 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -213,13 +213,25 @@ public: ProgramStateRef killBinding(Loc LV) const; - /// invalidateRegions - Returns the state with bindings for the given regions - /// cleared from the store. The regions are provided as a continuous array - /// from Begin to End. Optionally invalidates global regions as well. + /// \brief Returns the state with bindings for the given regions + /// cleared from the store. + /// + /// Optionally invalidates global regions as well. + /// + /// \param Regions the set of regions to be invalidated. + /// \param E the expression that caused the invalidation. + /// \param BlockCount the current basic block count. + /// \param ResultsInPointerEscape the flag is set to true when + /// the invalidation is due to escape of a symbol (representing a pointer). + /// For example, due to it being passed as an argument in a call. + /// \param IS the set of invalidated symbols. + /// \param If Call is non-null, the invalidated regions were directly + /// invalidated by the call - as parameters. ProgramStateRef invalidateRegions(ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned BlockCount, const LocationContext *LCtx, - StoreManager::InvalidatedSymbols *IS = 0, + bool ResultsInPointerEscape, + InvalidatedSymbols *IS = 0, const CallEvent *Call = 0) const; /// enterStackFrame - Returns the state for entry to the given stack frame, @@ -395,7 +407,8 @@ private: invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned BlockCount, const LocationContext *LCtx, - StoreManager::InvalidatedSymbols &IS, + bool ResultsInSymbolEscape, + InvalidatedSymbols &IS, const CallEvent *Call) const; }; diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h index e7f29f4f7c0..c515105b00d 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h @@ -35,6 +35,8 @@ class ProgramState; class ProgramStateManager; class ScanReachableSymbols; +typedef llvm::DenseSet<SymbolRef> InvalidatedSymbols; + class StoreManager { protected: SValBuilder &svalBuilder; @@ -168,7 +170,6 @@ public: /// associated with the object is recycled. virtual void decrementReferenceCount(Store store) {} - typedef llvm::DenseSet<SymbolRef> InvalidatedSymbols; typedef SmallVector<const MemRegion *, 8> InvalidatedRegions; /// invalidateRegions - Clears out the specified regions from the store, diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h index 1e710778d9b..0e9f25375d3 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h @@ -104,7 +104,7 @@ public: /// made to the store. Used to update checkers that track region values. virtual ProgramStateRef processRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) = 0; @@ -116,6 +116,16 @@ public: return processRegionChanges(state, 0, MR, MR, 0); } + virtual ProgramStateRef + processPointerEscapedOnBind(ProgramStateRef State, SVal Loc, SVal Val) = 0; + + virtual ProgramStateRef + processPointerEscapedOnInvalidateRegions(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const CallEvent *Call) = 0; + /// printState - Called by ProgramStateManager to print checker-specific data. virtual void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) = 0; diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index c958bfd50f2..9fbf97641f3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -64,7 +64,7 @@ public: ProgramStateRef checkRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *, + const InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) const; @@ -816,7 +816,8 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, // Invalidate this region. const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); - return state->invalidateRegions(R, E, C.blockCount(), LCtx); + return state->invalidateRegions(R, E, C.blockCount(), LCtx, + /*ResultsInPointerEscape*/ false); } // If we have a non-region value by chance, just remove the binding. @@ -1873,7 +1874,7 @@ bool CStringChecker::wantsRegionChangeUpdate(ProgramStateRef state) const { ProgramStateRef CStringChecker::checkRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *, + const InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) const { diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp index aec551fdb60..fa2c4ffb115 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -51,6 +51,7 @@ class CheckerDocumentation : public Checker< check::PreStmt<ReturnStmt>, eval::Assume, check::LiveSymbols, check::RegionChanges, + check::PointerEscape, check::Event<ImplicitNullDerefEvent>, check::ASTDecl<FunctionDecl> > { public: @@ -246,13 +247,31 @@ public: /// check::RegionChanges ProgramStateRef checkRegionChanges(ProgramStateRef State, - const StoreManager::InvalidatedSymbols *Invalidated, + const InvalidatedSymbols *Invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) const { return State; } + /// \brief Called when pointers escape. + /// + /// This notifies the checkers about pointer escape, which occurs whenever + /// the analzyer cannot track the symbol any more. For example, as a + /// result of assigning a pointer into a global or when it's passed to a + /// function call the analyzer cannot model. + /// + /// \param State The state at the point of escape. + /// \param Escaped The list of escaped symbols. + /// \param Call The corresponding CallEvent, if the symbols escape as + /// parameters to the given call. + /// \returns Checkers can modify the state by returning a new state. + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call) const { + return State; + } + /// check::Event<ImplicitNullDerefEvent> void checkEvent(ImplicitNullDerefEvent Event) const {} diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 26fd1c26ea7..af902a00963 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -103,15 +103,14 @@ struct ReallocPair { typedef std::pair<const Stmt*, const MemRegion*> LeakInfo; class MallocChecker : public Checker<check::DeadSymbols, + check::PointerEscape, check::PreStmt<ReturnStmt>, check::PreStmt<CallExpr>, check::PostStmt<CallExpr>, check::PostStmt<BlockExpr>, check::PostObjCMessage, check::Location, - check::Bind, - eval::Assume, - check::RegionChanges> + eval::Assume> { mutable OwningPtr<BugType> BT_DoubleFree; mutable OwningPtr<BugType> BT_Leak; @@ -143,17 +142,10 @@ public: bool Assumption) const; void checkLocation(SVal l, bool isLoad, const Stmt *S, CheckerContext &C) const; - void checkBind(SVal location, SVal val, const Stmt*S, - CheckerContext &C) const; - ProgramStateRef - checkRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, - ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions, - const CallEvent *Call) const; - bool wantsRegionChangeUpdate(ProgramStateRef state) const { - return true; - } + + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call) const; void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const; @@ -1254,51 +1246,6 @@ void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S, checkUseAfterFree(Sym, C, S); } -//===----------------------------------------------------------------------===// -// Check various ways a symbol can be invalidated. -// TODO: This logic (the next 3 functions) is copied/similar to the -// RetainRelease checker. We might want to factor this out. -//===----------------------------------------------------------------------===// - -// Stop tracking symbols when a value escapes as a result of checkBind. -// A value escapes in three possible cases: -// (1) we are binding to something that is not a memory region. -// (2) we are binding to a memregion that does not have stack storage -// (3) we are binding to a memregion with stack storage that the store -// does not understand. -void MallocChecker::checkBind(SVal loc, SVal val, const Stmt *S, - CheckerContext &C) const { - // Are we storing to something that causes the value to "escape"? - bool escapes = true; - ProgramStateRef state = C.getState(); - - if (loc::MemRegionVal *regionLoc = dyn_cast<loc::MemRegionVal>(&loc)) { - escapes = !regionLoc->getRegion()->hasStackStorage(); - - if (!escapes) { - // To test (3), generate a new state with the binding added. If it is - // the same state, then it escapes (since the store cannot represent - // the binding). - // Do this only if we know that the store is not supposed to generate the - // same state. - SVal StoredVal = state->getSVal(regionLoc->getRegion()); - if (StoredVal != val) - escapes = (state == (state->bindLoc(*regionLoc, val))); - } - } - - // If our store can represent the binding and we aren't storing to something - // that doesn't have local storage then just return and have the simulation - // state continue as is. - if (!escapes) - return; - - // Otherwise, find all symbols referenced by 'val' that we are tracking - // and stop tracking them. - state = state->scanReachableSymbols<StopTrackingCallback>(val).getState(); - C.addTransition(state); -} - // If a symbolic region is assumed to NULL (or another constant), stop tracking // it - assuming that allocation failed on this path. ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, @@ -1485,39 +1432,19 @@ bool MallocChecker::doesNotFreeMemory(const CallEvent *Call, return true; } -// If the symbol we are tracking is invalidated, but not explicitly (ex: the &p -// escapes, when we are tracking p), do not track the symbol as we cannot reason -// about it anymore. -ProgramStateRef -MallocChecker::checkRegionChanges(ProgramStateRef State, - const StoreManager::InvalidatedSymbols *invalidated, - ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions, - const CallEvent *Call) const { - if (!invalidated || invalidated->empty()) +ProgramStateRef MallocChecker::checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call) const { + // If we know that the call does not free memory, keep tracking the top + // level arguments. + if (Call && doesNotFreeMemory(Call, State)) return State; - llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols; - - // If it's a call which might free or reallocate memory, we assume that all - // regions (explicit and implicit) escaped. - // Otherwise, whitelist explicit pointers; we still can track them. - if (!Call || doesNotFreeMemory(Call, State)) { - for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(), - E = ExplicitRegions.end(); I != E; ++I) { - if (const SymbolicRegion *R = (*I)->StripCasts()->getAs<SymbolicRegion>()) - WhitelistedSymbols.insert(R->getSymbol()); - } - } - - for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(), - E = invalidated->end(); I!=E; ++I) { + for (InvalidatedSymbols::const_iterator I = Escaped.begin(), + E = Escaped.end(); + I != E; ++I) { SymbolRef sym = *I; - if (WhitelistedSymbols.count(sym)) - continue; - // The symbol escaped. Note, we assume that if the symbol is released, - // passing it out will result in a use after free. We also keep tracking - // relinquished symbols. + if (const RefState *RS = State->get<RegionState>(sym)) { if (RS->isAllocated()) State = State->remove<RegionState>(sym); diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 5ab9499a614..b38894ccfac 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -2512,7 +2512,7 @@ public: ProgramStateRef checkRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) const; @@ -3176,7 +3176,8 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { Binding = getRefBinding(state, Sym); // Invalidate the argument region. - state = state->invalidateRegions(ArgRegion, CE, C.blockCount(), LCtx); + state = state->invalidateRegions(ArgRegion, CE, C.blockCount(), LCtx, + /*ResultsInPointerEscape*/ false); // Restore the refcount status of the argument. if (Binding) @@ -3443,7 +3444,7 @@ ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state, ProgramStateRef RetainCountChecker::checkRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) const { @@ -3457,7 +3458,7 @@ RetainCountChecker::checkRegionChanges(ProgramStateRef state, WhitelistedSymbols.insert(SR->getSymbol()); } - for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(), + for (InvalidatedSymbols::const_iterator I=invalidated->begin(), E = invalidated->end(); I!=E; ++I) { SymbolRef sym = *I; if (WhitelistedSymbols.count(sym)) diff --git a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 7485fbb0b49..5f7a4a2da29 100644 --- a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -87,7 +87,7 @@ public: /// Deal with symbol escape as a byproduct of a region change. ProgramStateRef checkRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) const; @@ -304,7 +304,7 @@ bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{ // we cannot reason about it anymore. ProgramStateRef SimpleStreamChecker::checkRegionChanges(ProgramStateRef State, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) const { @@ -324,7 +324,7 @@ SimpleStreamChecker::checkRegionChanges(ProgramStateRef State, } } - for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(), + for (InvalidatedSymbols::const_iterator I=invalidated->begin(), E = invalidated->end(); I!=E; ++I) { SymbolRef sym = *I; if (WhitelistedSymbols.count(sym)) diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index 60b4d988d68..3709fd94eda 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -199,6 +199,7 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, // global variables. return Result->invalidateRegions(RegionsToInvalidate, getOriginExpr(), BlockCount, getLocationContext(), + /*ResultsInPointerEscape*/ true, /*Symbols=*/0, this); } diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index a4915c0dc0b..9591f0cd2ec 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -469,10 +469,10 @@ bool CheckerManager::wantsRegionChangeUpdate(ProgramStateRef state) { /// \brief Run checkers for region changes. ProgramStateRef CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions, - const CallEvent *Call) { + ArrayRef<const MemRegion *> Regions, + const CallEvent *Call) { for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) { // If any checker declares the state infeasible (or if it starts that way), // bail out. @@ -484,6 +484,21 @@ CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, return state; } +/// \brief Run checkers to process symbol escape event. +ProgramStateRef +CheckerManager::runCheckersForPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call) { + for (unsigned i = 0, e = PointerEscapeCheckers.size(); i != e; ++i) { + // If any checker declares the state infeasible (or if it starts that way), + // bail out. + if (!State) + return NULL; + State = PointerEscapeCheckers[i](State, Escaped, Call); + } + return State; +} + /// \brief Run checkers for handling assumptions on symbolic values. ProgramStateRef CheckerManager::runCheckersForEvalAssume(ProgramStateRef state, @@ -641,6 +656,10 @@ void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn, RegionChangesCheckers.push_back(info); } +void CheckerManager::_registerForPointerEscape(CheckPointerEscapeFunc checkfn){ + PointerEscapeCheckers.push_back(checkfn); +} + void CheckerManager::_registerForEvalAssume(EvalAssumeFunc checkfn) { EvalAssumeCheckers.push_back(checkfn); } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index efb4f7229f8..6b339404638 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -199,7 +199,7 @@ bool ExprEngine::wantsRegionChangeUpdate(ProgramStateRef state) { ProgramStateRef ExprEngine::processRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> Explicits, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) { @@ -1588,6 +1588,111 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, } } +namespace { +class CollectReachableSymbolsCallback : public SymbolVisitor { + InvalidatedSymbols Symbols; +public: + CollectReachableSymbolsCallback(ProgramStateRef State) {} + const InvalidatedSymbols &getSymbols() const { return Symbols; } + + bool VisitSymbol(SymbolRef Sym) { + Symbols.insert(Sym); + return true; + } +}; +} // end anonymous namespace + +/// Call PointerEscape callback when a value escapes as a result of bind. +/// A value escapes in three possible cases: +/// (1) we are binding to something that is not a memory region. +/// (2) we are binding to a memregion that does not have stack storage +/// (3) we are binding to a memregion with stack storage that the store +/// does not understand. +ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, + SVal Loc, SVal Val) { + // Are we storing to something that causes the value to "escape"? + bool escapes = true; + + // TODO: Move to StoreManager. + if (loc::MemRegionVal *regionLoc = dyn_cast<loc::MemRegionVal>(&Loc)) { + escapes = !regionLoc->getRegion()->hasStackStorage(); + + if (!escapes) { + // To test (3), generate a new state with the binding added. If it is + // the same state, then it escapes (since the store cannot represent + // the binding). + // Do this only if we know that the store is not supposed to generate the + // same state. + SVal StoredVal = State->getSVal(regionLoc->getRegion()); + if (StoredVal != Val) + escapes = (State == (State->bindLoc(*regionLoc, Val))); + } + } + + // If our store can represent the binding and we aren't storing to something + // that doesn't have local storage then just return and have the simulation + // state continue as is. + if (!escapes) + return State; + + // Otherwise, find all symbols referenced by 'val' that we are tracking + // and stop tracking them. + CollectReachableSymbolsCallback Scanner = + State->scanReachableSymbols<CollectReachableSymbolsCallback>(Val); + const InvalidatedSymbols &EscapedSymbols = Scanner.getSymbols(); + State = getCheckerManager().runCheckersForPointerEscape(State, + EscapedSymbols, + /*CallEvent*/ 0); + + return State; +} + +/// Call PointerEscape callback when a value escapes as a result of +/// region invalidation. +ProgramStateRef +ExprEngine::processPointerEscapedOnInvalidateRegions(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const CallEvent *Call) { + + if (!Invalidated || Invalidated->empty()) + return State; + + if (!Call) + return getCheckerManager().runCheckersForPointerEscape(State, + *Invalidated, 0); + + // If the symbols were invalidated by a call, we want to find out which ones + // were invalidated directly due to being arguments to the call. + InvalidatedSymbols SymbolsDirectlyInvalidated; + for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(), + E = ExplicitRegions.end(); I != E; ++I) { + if (const SymbolicRegion *R = (*I)->StripCasts()->getAs<SymbolicRegion>()) + SymbolsDirectlyInvalidated.insert(R->getSymbol()); + } + + InvalidatedSymbols SymbolsIndirectlyInvalidated; + for (InvalidatedSymbols::const_iterator I=Invalidated->begin(), + E = Invalidated->end(); I!=E; ++I) { + SymbolRef sym = *I; + if (SymbolsDirectlyInvalidated.count(sym)) + continue; + SymbolsIndirectlyInvalidated.insert(sym); + } + + if (!SymbolsDirectlyInvalidated.empty()) + State = getCheckerManager().runCheckersForPointerEscape(State, + SymbolsDirectlyInvalidated, Call); + + // Notify about the symbols that get indirectly invalidated by the call. + if (!SymbolsIndirectlyInvalidated.empty()) + State = getCheckerManager().runCheckersForPointerEscape(State, + SymbolsIndirectlyInvalidated, /*CallEvent*/ 0); + + return State; +} + /// evalBind - Handle the semantics of binding a value to a specific location. /// This method is used by evalStore and (soon) VisitDeclStmt, and others. void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, @@ -1605,27 +1710,33 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val, StoreE, *this, *PP); + + StmtNodeBuilder Bldr(CheckedSet, Dst, *currBldrCtx); + // If the location is not a 'Loc', it will already be handled by // the checkers. There is nothing left to do. if (!isa<Loc>(location)) { - Dst = CheckedSet; + const ProgramPoint L = PostStore(StoreE, LC, /*Loc*/0, /*tag*/0); + ProgramStateRef state = Pred->getState(); + state = processPointerEscapedOnBind(state, location, Val); + Bldr.generateNode(L, state, Pred); return; } - ExplodedNodeSet TmpDst; - StmtNodeBuilder Bldr(CheckedSet, TmpDst, *currBldrCtx); for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); I!=E; ++I) { ExplodedNode *PredI = *I; ProgramStateRef state = PredI->getState(); + state = processPointerEscapedOnBind(state, location, Val); + // When binding the value, pass on the hint that this is a initialization. // For initializations, we do not need to inform clients of region // changes. state = state->bindLoc(cast<Loc>(location), Val, /* notifyChanges = */ !atDeclInit); - + const MemRegion *LocReg = 0; if (loc::MemRegionVal *LocRegVal = dyn_cast<loc::MemRegionVal>(&location)) { LocReg = LocRegVal->getRegion(); @@ -1634,7 +1745,6 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, const ProgramPoint L = PostStore(StoreE, LC, LocReg, 0); Bldr.generateNode(L, state, PredI); } - Dst.insert(TmpDst); } /// evalStore - Handle the semantics of a store via an assignment. diff --git a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp index 3b96cc899f5..15662776d9e 100644 --- a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -144,31 +144,41 @@ ProgramStateRef ProgramState::invalidateRegions(ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned Count, const LocationContext *LCtx, - StoreManager::InvalidatedSymbols *IS, + bool ResultsInPointerEscape, + InvalidatedSymbols *IS, const CallEvent *Call) const { if (!IS) { - StoreManager::InvalidatedSymbols invalidated; + InvalidatedSymbols invalidated; return invalidateRegionsImpl(Regions, E, Count, LCtx, + ResultsInPointerEscape, invalidated, Call); } - return invalidateRegionsImpl(Regions, E, Count, LCtx, *IS, Call); + return invalidateRegionsImpl(Regions, E, Count, LCtx, ResultsInPointerEscape, + *IS, Call); } ProgramStateRef ProgramState::invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned Count, const LocationContext *LCtx, - StoreManager::InvalidatedSymbols &IS, + bool ResultsInPointerEscape, + InvalidatedSymbols &IS, const CallEvent *Call) const { ProgramStateManager &Mgr = getStateManager(); SubEngine* Eng = Mgr.getOwningEngine(); - if (Eng && Eng->wantsRegionChangeUpdate(this)) { + if (Eng) { StoreManager::InvalidatedRegions Invalidated; const StoreRef &newStore = Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, LCtx, IS, Call, &Invalidated); + ProgramStateRef newState = makeWithStore(newStore); + + if (ResultsInPointerEscape) + newState = Eng->processPointerEscapedOnInvalidateRegions(newState, + &IS, Regions, Invalidated, Call); + return Eng->processRegionChanges(newState, &IS, Regions, Invalidated, Call); } diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index 32eb56ca735..9d66c16e73e 100644 --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -811,7 +811,7 @@ class invalidateRegionsWorker : public ClusterAnalysis<invalidateRegionsWorker> const Expr *Ex; unsigned Count; const LocationContext *LCtx; - StoreManager::InvalidatedSymbols &IS; + InvalidatedSymbols &IS; StoreManager::InvalidatedRegions *Regions; public: invalidateRegionsWorker(RegionStoreManager &rm, @@ -819,7 +819,7 @@ public: RegionBindingsRef b, const Expr *ex, unsigned count, const LocationContext *lctx, - StoreManager::InvalidatedSymbols &is, + InvalidatedSymbols &is, StoreManager::InvalidatedRegions *r, bool includeGlobals) : ClusterAnalysis<invalidateRegionsWorker>(rm, stateMgr, b, includeGlobals), |