diff options
author | Anna Zaks <ganna@apple.com> | 2012-11-06 04:20:57 +0000 |
---|---|---|
committer | Anna Zaks <ganna@apple.com> | 2012-11-06 04:20:57 +0000 |
commit | 35d4a09efbdc313b02f05612e6501a7ec7d3a37d (patch) | |
tree | 0fc37888b1d5e3f4db5316d15bf03284ce4eb156 /lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp | |
parent | d1ad5e5d6c895f809ada5b420060b2ec0b48567b (diff) |
[analyzer] Add symbol escapes logic to the SimpleStreamChecker.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@167439 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp | 126 |
1 files changed, 125 insertions, 1 deletions
diff --git a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 641f2ed0b9..ee055adf6e 100644 --- a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -49,7 +49,9 @@ public: class SimpleStreamChecker : public Checker<check::PostCall, check::PreCall, - check::DeadSymbols > { + check::DeadSymbols, + check::Bind, + check::RegionChanges> { mutable IdentifierInfo *IIfopen, *IIfclose; @@ -66,6 +68,8 @@ class SimpleStreamChecker : public Checker<check::PostCall, CheckerContext &C, ExplodedNode *ErrNode) const; + bool guaranteedNotToCloseFile(const CallEvent &Call) const; + public: SimpleStreamChecker(); @@ -75,6 +79,21 @@ public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + + /// Deal with symbol escape as a byproduct of a bind. + void checkBind(SVal location, SVal val, const Stmt*S, + CheckerContext &C) const; + + /// Deal with symbol escape as a byproduct of a region change. + 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; + } }; } // end anonymous namespace @@ -83,6 +102,21 @@ public: /// state. Let's store it in the ProgramState. REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) +namespace { +class StopTrackingCallback : public SymbolVisitor { + ProgramStateRef state; +public: + StopTrackingCallback(ProgramStateRef st) : state(st) {} + ProgramStateRef getState() const { return state; } + + bool VisitSymbol(SymbolRef sym) { + state = state->remove<StreamMap>(sym); + return true; + } +}; +} // end anonymous namespace + + SimpleStreamChecker::SimpleStreamChecker() : IIfopen(0), IIfclose(0) { // Initialize the bug types. DoubleCloseBugType.reset(new BugType("Double fclose", @@ -212,6 +246,96 @@ void SimpleStreamChecker::reportLeaks(SymbolVector LeakedStreams, } } +// Check various ways a symbol can be invalidated. +// 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 SimpleStreamChecker::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 the state and + // 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); +} + +bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{ + // If it's not in a system header, assume it might close a file. + if (!Call.isInSystemHeader()) + return false; + + // Handle cases where we know a buffer's /address/ can escape. + if (Call.argumentsMayEscape()) + return false; + + // Note, even though fclose closes the file, we do not list it here + // since the checker is modeling the call. + + return true; +} + +// If the symbol we are tracking is invalidated, do not track the symbol as +// we cannot reason about it anymore. +ProgramStateRef +SimpleStreamChecker::checkRegionChanges(ProgramStateRef State, + const StoreManager::InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const CallEvent *Call) const { + + if (!invalidated || invalidated->empty()) + return State; + + // If it's a call which might close the file, we assume that all regions + // (explicit and implicit) escaped. Otherwise, whitelist explicit pointers + // (the parameters to the call); we still can track them. + llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols; + if (!Call || guaranteedNotToCloseFile(*Call)) { + 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) { + SymbolRef sym = *I; + if (WhitelistedSymbols.count(sym)) + continue; + // The symbol escaped. Optimistically, assume that the corresponding file + // handle will be closed somewhere else. + State = State->remove<StreamMap>(sym); + } + return State; +} + void SimpleStreamChecker::initIdentifierInfo(ASTContext &Ctx) const { if (IIfopen) return; |