1d65e55d691655462880ffd51c10784955ab6a362Anna Zaks//===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--// 2d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// 3d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// The LLVM Compiler Infrastructure 4d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// 5d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// This file is distributed under the University of Illinois Open Source 6d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// License. See LICENSE.TXT for details. 7d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// 8d65e55d691655462880ffd51c10784955ab6a362Anna Zaks//===----------------------------------------------------------------------===// 9d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// 10d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// Defines a checker for proper use of fopen/fclose APIs. 11d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// - If a file has been closed with fclose, it should not be accessed again. 12d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// Accessing a closed file results in undefined behavior. 13d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// - If a file was opened with fopen, it must be closed with fclose before 14d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// the execution ends. Failing to do so results in a resource leak. 15d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// 16d65e55d691655462880ffd51c10784955ab6a362Anna Zaks//===----------------------------------------------------------------------===// 17d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 18d65e55d691655462880ffd51c10784955ab6a362Anna Zaks#include "ClangSACheckers.h" 19d65e55d691655462880ffd51c10784955ab6a362Anna Zaks#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 2055fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "clang/StaticAnalyzer/Core/Checker.h" 210c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 22d65e55d691655462880ffd51c10784955ab6a362Anna Zaks#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 23d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 24d65e55d691655462880ffd51c10784955ab6a362Anna Zaksusing namespace clang; 25d65e55d691655462880ffd51c10784955ab6a362Anna Zaksusing namespace ento; 26d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 27d65e55d691655462880ffd51c10784955ab6a362Anna Zaksnamespace { 28cfa88f893915ceb8ae4ce2f17c46c24a4d67502fDmitri Gribenkotypedef SmallVector<SymbolRef, 2> SymbolVector; 29d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 30d65e55d691655462880ffd51c10784955ab6a362Anna Zaksstruct StreamState { 3132133cfb333510ba94aff040067713c0b32d58c5Anna Zaksprivate: 32d65e55d691655462880ffd51c10784955ab6a362Anna Zaks enum Kind { Opened, Closed } K; 33d65e55d691655462880ffd51c10784955ab6a362Anna Zaks StreamState(Kind InK) : K(InK) { } 34d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 3532133cfb333510ba94aff040067713c0b32d58c5Anna Zakspublic: 36d65e55d691655462880ffd51c10784955ab6a362Anna Zaks bool isOpened() const { return K == Opened; } 37d65e55d691655462880ffd51c10784955ab6a362Anna Zaks bool isClosed() const { return K == Closed; } 38d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 39d65e55d691655462880ffd51c10784955ab6a362Anna Zaks static StreamState getOpened() { return StreamState(Opened); } 40d65e55d691655462880ffd51c10784955ab6a362Anna Zaks static StreamState getClosed() { return StreamState(Closed); } 41d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 42d65e55d691655462880ffd51c10784955ab6a362Anna Zaks bool operator==(const StreamState &X) const { 43d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return K == X.K; 44d65e55d691655462880ffd51c10784955ab6a362Anna Zaks } 45d65e55d691655462880ffd51c10784955ab6a362Anna Zaks void Profile(llvm::FoldingSetNodeID &ID) const { 46d65e55d691655462880ffd51c10784955ab6a362Anna Zaks ID.AddInteger(K); 47d65e55d691655462880ffd51c10784955ab6a362Anna Zaks } 48d65e55d691655462880ffd51c10784955ab6a362Anna Zaks}; 49d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 500c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Roseclass SimpleStreamChecker : public Checker<check::PostCall, 510c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose check::PreCall, 5235d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks check::DeadSymbols, 534b6bb40b22877472d0b3d2961689f1f0ac23cc71Anna Zaks check::PointerEscape> { 54d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 55d65e55d691655462880ffd51c10784955ab6a362Anna Zaks mutable IdentifierInfo *IIfopen, *IIfclose; 56d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 57651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines std::unique_ptr<BugType> DoubleCloseBugType; 58651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines std::unique_ptr<BugType> LeakBugType; 59d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 60d65e55d691655462880ffd51c10784955ab6a362Anna Zaks void initIdentifierInfo(ASTContext &Ctx) const; 61d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 62d65e55d691655462880ffd51c10784955ab6a362Anna Zaks void reportDoubleClose(SymbolRef FileDescSym, 630c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose const CallEvent &Call, 64d65e55d691655462880ffd51c10784955ab6a362Anna Zaks CheckerContext &C) const; 65d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 6632f38c11642ddeaeb6c4ffb1e589ab444c825f6eJordan Rose void reportLeaks(SymbolVector LeakedStreams, 6732f38c11642ddeaeb6c4ffb1e589ab444c825f6eJordan Rose CheckerContext &C, 6832f38c11642ddeaeb6c4ffb1e589ab444c825f6eJordan Rose ExplodedNode *ErrNode) const; 69d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 7035d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks bool guaranteedNotToCloseFile(const CallEvent &Call) const; 7135d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks 72d65e55d691655462880ffd51c10784955ab6a362Anna Zakspublic: 7332133cfb333510ba94aff040067713c0b32d58c5Anna Zaks SimpleStreamChecker(); 74d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 75d65e55d691655462880ffd51c10784955ab6a362Anna Zaks /// Process fopen. 760c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 77d65e55d691655462880ffd51c10784955ab6a362Anna Zaks /// Process fclose. 780c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 79d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 80d65e55d691655462880ffd51c10784955ab6a362Anna Zaks void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 8135d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks 824b6bb40b22877472d0b3d2961689f1f0ac23cc71Anna Zaks /// Stop tracking addresses which escape. 834b6bb40b22877472d0b3d2961689f1f0ac23cc71Anna Zaks ProgramStateRef checkPointerEscape(ProgramStateRef State, 844b6bb40b22877472d0b3d2961689f1f0ac23cc71Anna Zaks const InvalidatedSymbols &Escaped, 85233e26acc0ff2a1098f4c813f69286fce840a422Anna Zaks const CallEvent *Call, 86233e26acc0ff2a1098f4c813f69286fce840a422Anna Zaks PointerEscapeKind Kind) const; 87d65e55d691655462880ffd51c10784955ab6a362Anna Zaks}; 88d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 89d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} // end anonymous namespace 90d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 91d65e55d691655462880ffd51c10784955ab6a362Anna Zaks/// The state of the checker is a map from tracked stream symbols to their 92ac150f2619efcadbf23acd6e86695b5412723eb1Anna Zaks/// state. Let's store it in the ProgramState. 93ac150f2619efcadbf23acd6e86695b5412723eb1Anna ZaksREGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 94d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 9535d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaksnamespace { 9635d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaksclass StopTrackingCallback : public SymbolVisitor { 9735d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks ProgramStateRef state; 9835d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zakspublic: 9935d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks StopTrackingCallback(ProgramStateRef st) : state(st) {} 10035d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks ProgramStateRef getState() const { return state; } 10135d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks 102651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines bool VisitSymbol(SymbolRef sym) override { 10335d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks state = state->remove<StreamMap>(sym); 10435d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks return true; 10535d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks } 10635d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks}; 10735d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks} // end anonymous namespace 10835d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks 1096bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen HinesSimpleStreamChecker::SimpleStreamChecker() 1106bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines : IIfopen(nullptr), IIfclose(nullptr) { 11132133cfb333510ba94aff040067713c0b32d58c5Anna Zaks // Initialize the bug types. 112651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines DoubleCloseBugType.reset( 113651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines new BugType(this, "Double fclose", "Unix Stream API Error")); 11432133cfb333510ba94aff040067713c0b32d58c5Anna Zaks 115651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines LeakBugType.reset( 116651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines new BugType(this, "Resource Leak", "Unix Stream API Error")); 11732133cfb333510ba94aff040067713c0b32d58c5Anna Zaks // Sinks are higher importance bugs as well as calls to assert() or exit(0). 11832133cfb333510ba94aff040067713c0b32d58c5Anna Zaks LeakBugType->setSuppressOnSink(true); 11932133cfb333510ba94aff040067713c0b32d58c5Anna Zaks} 12032133cfb333510ba94aff040067713c0b32d58c5Anna Zaks 1210c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rosevoid SimpleStreamChecker::checkPostCall(const CallEvent &Call, 122d65e55d691655462880ffd51c10784955ab6a362Anna Zaks CheckerContext &C) const { 123d65e55d691655462880ffd51c10784955ab6a362Anna Zaks initIdentifierInfo(C.getASTContext()); 124d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 1250c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose if (!Call.isGlobalCFunction()) 1260c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose return; 1270c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose 1280c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose if (Call.getCalleeIdentifier() != IIfopen) 129d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 130d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 131d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Get the symbolic value corresponding to the file handle. 1320c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose SymbolRef FileDesc = Call.getReturnValue().getAsSymbol(); 133d65e55d691655462880ffd51c10784955ab6a362Anna Zaks if (!FileDesc) 134d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 135d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 136d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Generate the next transition (an edge in the exploded graph). 137d65e55d691655462880ffd51c10784955ab6a362Anna Zaks ProgramStateRef State = C.getState(); 138d65e55d691655462880ffd51c10784955ab6a362Anna Zaks State = State->set<StreamMap>(FileDesc, StreamState::getOpened()); 139d65e55d691655462880ffd51c10784955ab6a362Anna Zaks C.addTransition(State); 140d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 141d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 1420c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rosevoid SimpleStreamChecker::checkPreCall(const CallEvent &Call, 143d65e55d691655462880ffd51c10784955ab6a362Anna Zaks CheckerContext &C) const { 144d65e55d691655462880ffd51c10784955ab6a362Anna Zaks initIdentifierInfo(C.getASTContext()); 145d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 1460c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose if (!Call.isGlobalCFunction()) 1470c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose return; 1480c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose 1490c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose if (Call.getCalleeIdentifier() != IIfclose) 1500c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose return; 1510c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose 1520c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose if (Call.getNumArgs() != 1) 153d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 154d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 155d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Get the symbolic value corresponding to the file handle. 1560c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose SymbolRef FileDesc = Call.getArgSVal(0).getAsSymbol(); 157d65e55d691655462880ffd51c10784955ab6a362Anna Zaks if (!FileDesc) 158d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 159d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 160d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Check if the stream has already been closed. 161d65e55d691655462880ffd51c10784955ab6a362Anna Zaks ProgramStateRef State = C.getState(); 162d65e55d691655462880ffd51c10784955ab6a362Anna Zaks const StreamState *SS = State->get<StreamMap>(FileDesc); 163bbb751a1788c461bc9765ec3387536cad6b52619Anna Zaks if (SS && SS->isClosed()) { 164d65e55d691655462880ffd51c10784955ab6a362Anna Zaks reportDoubleClose(FileDesc, Call, C); 165bbb751a1788c461bc9765ec3387536cad6b52619Anna Zaks return; 166bbb751a1788c461bc9765ec3387536cad6b52619Anna Zaks } 167d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 168d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Generate the next transition, in which the stream is closed. 169d65e55d691655462880ffd51c10784955ab6a362Anna Zaks State = State->set<StreamMap>(FileDesc, StreamState::getClosed()); 170d65e55d691655462880ffd51c10784955ab6a362Anna Zaks C.addTransition(State); 171d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 172d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 173edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaksstatic bool isLeaked(SymbolRef Sym, const StreamState &SS, 174edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks bool IsSymDead, ProgramStateRef State) { 175edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks if (IsSymDead && SS.isOpened()) { 176edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks // If a symbol is NULL, assume that fopen failed on this path. 177edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks // A symbol should only be considered leaked if it is non-null. 178edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks ConstraintManager &CMgr = State->getConstraintManager(); 179edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym); 180edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks return !OpenFailed.isConstrainedTrue(); 181edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks } 182edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks return false; 183edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks} 184edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks 185d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 186d65e55d691655462880ffd51c10784955ab6a362Anna Zaks CheckerContext &C) const { 187d65e55d691655462880ffd51c10784955ab6a362Anna Zaks ProgramStateRef State = C.getState(); 188d65e55d691655462880ffd51c10784955ab6a362Anna Zaks SymbolVector LeakedStreams; 189edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks StreamMapTy TrackedStreams = State->get<StreamMap>(); 190360b29c52a4c10f9d4c031d84d962ed2a4d58263Anna Zaks for (StreamMapTy::iterator I = TrackedStreams.begin(), 191ec8d420d4fa57fc6b5a5a2b1446742e976a7ba00Jordan Rose E = TrackedStreams.end(); I != E; ++I) { 192d65e55d691655462880ffd51c10784955ab6a362Anna Zaks SymbolRef Sym = I->first; 193edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks bool IsSymDead = SymReaper.isDead(Sym); 194edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks 195edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks // Collect leaked symbols. 196edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks if (isLeaked(Sym, I->second, IsSymDead, State)) 197edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks LeakedStreams.push_back(Sym); 198edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks 199edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks // Remove the dead symbol from the streams map. 200edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks if (IsSymDead) 201d65e55d691655462880ffd51c10784955ab6a362Anna Zaks State = State->remove<StreamMap>(Sym); 202d65e55d691655462880ffd51c10784955ab6a362Anna Zaks } 203d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 20432133cfb333510ba94aff040067713c0b32d58c5Anna Zaks ExplodedNode *N = C.addTransition(State); 20532133cfb333510ba94aff040067713c0b32d58c5Anna Zaks reportLeaks(LeakedStreams, C, N); 206d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 207d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 208d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym, 2090c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose const CallEvent &Call, 210d65e55d691655462880ffd51c10784955ab6a362Anna Zaks CheckerContext &C) const { 211d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // We reached a bug, stop exploring the path here by generating a sink. 212d65e55d691655462880ffd51c10784955ab6a362Anna Zaks ExplodedNode *ErrNode = C.generateSink(); 21332133cfb333510ba94aff040067713c0b32d58c5Anna Zaks // If we've already reached this node on another path, return. 214d65e55d691655462880ffd51c10784955ab6a362Anna Zaks if (!ErrNode) 215d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 216d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 217d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Generate the report. 218d65e55d691655462880ffd51c10784955ab6a362Anna Zaks BugReport *R = new BugReport(*DoubleCloseBugType, 219d65e55d691655462880ffd51c10784955ab6a362Anna Zaks "Closing a previously closed file stream", ErrNode); 2200c396d618dfa7cdd6ddafea24df7f74789d1f829Jordan Rose R->addRange(Call.getSourceRange()); 221d65e55d691655462880ffd51c10784955ab6a362Anna Zaks R->markInteresting(FileDescSym); 222785950e59424dca7ce0081bebf13c0acd2c4fff6Jordan Rose C.emitReport(R); 223d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 224d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 22532133cfb333510ba94aff040067713c0b32d58c5Anna Zaksvoid SimpleStreamChecker::reportLeaks(SymbolVector LeakedStreams, 22632133cfb333510ba94aff040067713c0b32d58c5Anna Zaks CheckerContext &C, 22732133cfb333510ba94aff040067713c0b32d58c5Anna Zaks ExplodedNode *ErrNode) const { 228d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Attach bug reports to the leak node. 229bdbb17b81ca02f0279909836668420351b7f24c1Anna Zaks // TODO: Identify the leaked file descriptor. 23009d19efaa147762f84aed55efa7930bb3616a4e5Craig Topper for (SmallVectorImpl<SymbolRef>::iterator 23109d19efaa147762f84aed55efa7930bb3616a4e5Craig Topper I = LeakedStreams.begin(), E = LeakedStreams.end(); I != E; ++I) { 232d65e55d691655462880ffd51c10784955ab6a362Anna Zaks BugReport *R = new BugReport(*LeakBugType, 233d65e55d691655462880ffd51c10784955ab6a362Anna Zaks "Opened file is never closed; potential resource leak", ErrNode); 234bdbb17b81ca02f0279909836668420351b7f24c1Anna Zaks R->markInteresting(*I); 235785950e59424dca7ce0081bebf13c0acd2c4fff6Jordan Rose C.emitReport(R); 236d65e55d691655462880ffd51c10784955ab6a362Anna Zaks } 237d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 238d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 23935d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaksbool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{ 24035d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks // If it's not in a system header, assume it might close a file. 24135d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks if (!Call.isInSystemHeader()) 24235d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks return false; 24335d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks 24435d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks // Handle cases where we know a buffer's /address/ can escape. 24535d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks if (Call.argumentsMayEscape()) 24635d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks return false; 24735d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks 24835d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks // Note, even though fclose closes the file, we do not list it here 24935d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks // since the checker is modeling the call. 25035d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks 25135d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks return true; 25235d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks} 25335d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks 2544b6bb40b22877472d0b3d2961689f1f0ac23cc71Anna Zaks// If the pointer we are tracking escaped, do not track the symbol as 25535d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks// we cannot reason about it anymore. 25635d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna ZaksProgramStateRef 2574b6bb40b22877472d0b3d2961689f1f0ac23cc71Anna ZaksSimpleStreamChecker::checkPointerEscape(ProgramStateRef State, 2584b6bb40b22877472d0b3d2961689f1f0ac23cc71Anna Zaks const InvalidatedSymbols &Escaped, 259233e26acc0ff2a1098f4c813f69286fce840a422Anna Zaks const CallEvent *Call, 260233e26acc0ff2a1098f4c813f69286fce840a422Anna Zaks PointerEscapeKind Kind) const { 2614b6bb40b22877472d0b3d2961689f1f0ac23cc71Anna Zaks // If we know that the call cannot close a file, there is nothing to do. 262374ae320b87c15b0262c40e5c46e8990111df5caJordan Rose if (Kind == PSK_DirectEscapeOnCall && guaranteedNotToCloseFile(*Call)) { 26335d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks return State; 264233e26acc0ff2a1098f4c813f69286fce840a422Anna Zaks } 26535d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks 2664b6bb40b22877472d0b3d2961689f1f0ac23cc71Anna Zaks for (InvalidatedSymbols::const_iterator I = Escaped.begin(), 2674b6bb40b22877472d0b3d2961689f1f0ac23cc71Anna Zaks E = Escaped.end(); 2684b6bb40b22877472d0b3d2961689f1f0ac23cc71Anna Zaks I != E; ++I) { 2694b6bb40b22877472d0b3d2961689f1f0ac23cc71Anna Zaks SymbolRef Sym = *I; 27035d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks 27135d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks // The symbol escaped. Optimistically, assume that the corresponding file 27235d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks // handle will be closed somewhere else. 2734b6bb40b22877472d0b3d2961689f1f0ac23cc71Anna Zaks State = State->remove<StreamMap>(Sym); 27435d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks } 27535d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks return State; 27635d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks} 27735d4a09efbdc313b02f05612e6501a7ec7d3a37dAnna Zaks 278d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid SimpleStreamChecker::initIdentifierInfo(ASTContext &Ctx) const { 279d65e55d691655462880ffd51c10784955ab6a362Anna Zaks if (IIfopen) 280d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 281d65e55d691655462880ffd51c10784955ab6a362Anna Zaks IIfopen = &Ctx.Idents.get("fopen"); 282d65e55d691655462880ffd51c10784955ab6a362Anna Zaks IIfclose = &Ctx.Idents.get("fclose"); 283d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 284d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 285d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid ento::registerSimpleStreamChecker(CheckerManager &mgr) { 286d65e55d691655462880ffd51c10784955ab6a362Anna Zaks mgr.registerChecker<SimpleStreamChecker>(); 287d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 288