From: Anna Zaks Date: Thu, 20 Dec 2012 00:38:25 +0000 (+0000) Subject: [analyzer] Add the pointer escaped callback. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bf53dfac8195835028bd6347433f7dbebcc29fc1;p=clang [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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@170625 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h index 9eb1248f6a..61c1884544 100644 --- a/include/clang/StaticAnalyzer/Core/Checker.h +++ b/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 Explicits, ArrayRef Regions, const CallEvent *Call) { @@ -317,6 +317,27 @@ public: } }; +class PointerEscape { + template + static ProgramStateRef + _checkPointerEscape(void *checker, + ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call) { + return ((const CHECKER *)checker)->checkPointerEscape(State, + Escaped, + Call); + } + +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPointerEscape( + CheckerManager::CheckPointerEscapeFunc(checker, + _checkPointerEscape)); + } +}; + template class Event { template diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index 710e9c1bca..80e06c1e84 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/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 ExplicitRegions, ArrayRef 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 CheckLiveSymbolsFunc; typedef CheckerFn ExplicitRegions, ArrayRef Regions, const CallEvent *Call)> CheckRegionChangesFunc; typedef CheckerFn WantsRegionChangeUpdateFunc; + + typedef CheckerFn + CheckPointerEscapeFunc; typedef CheckerFn @@ -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 RegionChangesCheckers; + std::vector PointerEscapeCheckers; + std::vector EvalAssumeCheckers; std::vector EvalCallCheckers; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 151b0d6420..7c56062152 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/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 ExplicitRegions, ArrayRef 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 ExplicitRegions, + ArrayRef Regions, + const CallEvent *Call); + public: // FIXME: 'tag' should be removed, and a LocationContext should be used // instead. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index cb8dd08a24..0857d127b7 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/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 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 Regions, const Expr *E, unsigned BlockCount, const LocationContext *LCtx, - StoreManager::InvalidatedSymbols &IS, + bool ResultsInSymbolEscape, + InvalidatedSymbols &IS, const CallEvent *Call) const; }; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h index e7f29f4f7c..c515105b00 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h @@ -35,6 +35,8 @@ class ProgramState; class ProgramStateManager; class ScanReachableSymbols; +typedef llvm::DenseSet 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 InvalidatedSymbols; typedef SmallVector InvalidatedRegions; /// invalidateRegions - Clears out the specified regions from the store, diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h index 1e710778d9..0e9f25375d 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h +++ b/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 ExplicitRegions, ArrayRef 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 ExplicitRegions, + ArrayRef 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/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index c958bfd50f..9fbf97641f 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -64,7 +64,7 @@ public: ProgramStateRef checkRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *, + const InvalidatedSymbols *, ArrayRef ExplicitRegions, ArrayRef 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 ExplicitRegions, ArrayRef Regions, const CallEvent *Call) const { diff --git a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp index aec551fdb6..fa2c4ffb11 100644 --- a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -51,6 +51,7 @@ class CheckerDocumentation : public Checker< check::PreStmt, eval::Assume, check::LiveSymbols, check::RegionChanges, + check::PointerEscape, check::Event, check::ASTDecl > { public: @@ -246,13 +247,31 @@ public: /// check::RegionChanges ProgramStateRef checkRegionChanges(ProgramStateRef State, - const StoreManager::InvalidatedSymbols *Invalidated, + const InvalidatedSymbols *Invalidated, ArrayRef ExplicitRegions, ArrayRef 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 void checkEvent(ImplicitNullDerefEvent Event) const {} diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 26fd1c26ea..af902a0096 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -103,15 +103,14 @@ struct ReallocPair { typedef std::pair LeakInfo; class MallocChecker : public Checker, check::PreStmt, check::PostStmt, check::PostStmt, check::PostObjCMessage, check::Location, - check::Bind, - eval::Assume, - check::RegionChanges> + eval::Assume> { mutable OwningPtr BT_DoubleFree; mutable OwningPtr 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 ExplicitRegions, - ArrayRef 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)) { - 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(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 ExplicitRegions, - ArrayRef 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 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::iterator I = ExplicitRegions.begin(), - E = ExplicitRegions.end(); I != E; ++I) { - if (const SymbolicRegion *R = (*I)->StripCasts()->getAs()) - 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(sym)) { if (RS->isAllocated()) State = State->remove(sym); diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 5ab9499a61..b38894ccfa 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -2512,7 +2512,7 @@ public: ProgramStateRef checkRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef ExplicitRegions, ArrayRef 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 ExplicitRegions, ArrayRef 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/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 7485fbb0b4..5f7a4a2da2 100644 --- a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/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 ExplicitRegions, ArrayRef 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 ExplicitRegions, ArrayRef 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/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 60b4d988d6..3709fd94ed 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/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/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index a4915c0dc0..9591f0cd2e 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/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 ExplicitRegions, - ArrayRef Regions, - const CallEvent *Call) { + ArrayRef 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/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index efb4f7229f..6b33940463 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/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 Explicits, ArrayRef 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)) { + 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(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 ExplicitRegions, + ArrayRef 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::iterator I = ExplicitRegions.begin(), + E = ExplicitRegions.end(); I != E; ++I) { + if (const SymbolicRegion *R = (*I)->StripCasts()->getAs()) + 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(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(location), Val, /* notifyChanges = */ !atDeclInit); - + const MemRegion *LocReg = 0; if (loc::MemRegionVal *LocRegVal = dyn_cast(&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/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 3b96cc899f..15662776d9 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -144,31 +144,41 @@ ProgramStateRef ProgramState::invalidateRegions(ArrayRef 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 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/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index 32eb56ca73..9d66c16e73 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -811,7 +811,7 @@ class invalidateRegionsWorker : public ClusterAnalysis 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(rm, stateMgr, b, includeGlobals),