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