From 35d4a09efbdc313b02f05612e6501a7ec7d3a37d Mon Sep 17 00:00:00 2001 From: Anna Zaks Date: Tue, 6 Nov 2012 04:20:57 +0000 Subject: [PATCH] [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 --- .../Checkers/SimpleStreamChecker.cpp | 126 +++++++++++++++++- ...ystem-header-simulator-for-simple-stream.h | 11 ++ test/Analysis/simple-stream-checks.c | 29 ++-- 3 files changed, 157 insertions(+), 9 deletions(-) create mode 100644 test/Analysis/Inputs/system-header-simulator-for-simple-stream.h 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::DeadSymbols, + check::Bind, + check::RegionChanges> { mutable IdentifierInfo *IIfopen, *IIfclose; @@ -66,6 +68,8 @@ class SimpleStreamChecker : public Checker ExplicitRegions, + ArrayRef 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(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)) { + 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(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 ExplicitRegions, + ArrayRef 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 WhitelistedSymbols; + if (!Call || guaranteedNotToCloseFile(*Call)) { + 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) { + 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(sym); + } + return State; +} + void SimpleStreamChecker::initIdentifierInfo(ASTContext &Ctx) const { if (IIfopen) return; diff --git a/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h b/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h new file mode 100644 index 0000000000..99986f4549 --- /dev/null +++ b/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h @@ -0,0 +1,11 @@ + +#pragma clang system_header + +typedef struct __sFILE { + unsigned char *_p; +} FILE; +FILE *fopen(const char * restrict, const char * restrict) __asm("_" "fopen" ); +int fputc(int, FILE *); +int fputs(const char * restrict, FILE * restrict) __asm("_" "fputs" ); +int fclose(FILE *); +void exit(int); diff --git a/test/Analysis/simple-stream-checks.c b/test/Analysis/simple-stream-checks.c index 7a2718ebc5..2f09e5dd20 100644 --- a/test/Analysis/simple-stream-checks.c +++ b/test/Analysis/simple-stream-checks.c @@ -1,13 +1,6 @@ // RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.unix.SimpleStream -verify %s -typedef struct __sFILE { - unsigned char *_p; -} FILE; -FILE *fopen(const char * restrict, const char * restrict) __asm("_" "fopen" ); -int fputc(int, FILE *); -int fputs(const char * restrict, FILE * restrict) __asm("_" "fputs" ); -int fclose(FILE *); -void exit(int); +#include "Inputs/system-header-simulator-for-simple-stream.h" void checkDoubleFClose(int *Data) { FILE *F = fopen("myfile.txt", "w"); @@ -63,3 +56,23 @@ FILE *leakOnEnfOfPath3(int *Data) { FILE *F = fopen("myfile.txt", "w"); return F; } + +void myfclose(FILE *F); +void SymbolEscapedThroughFunctionCall() { + FILE *F = fopen("myfile.txt", "w"); + myfclose(F); + return; // no warning +} + +FILE *GlobalF; +void SymbolEscapedThroughAssignmentToGloabl() { + FILE *F = fopen("myfile.txt", "w"); + GlobalF = F; + return; // no warning +} + +void SymbolDoesNotEscapeThoughStringAPIs(char *Data) { + FILE *F = fopen("myfile.txt", "w"); + fputc(*Data, F); + return; // expected-warning {{Opened file is never closed; potential resource leak}} +} -- 2.50.1