clang  3.7.0
StreamChecker.cpp
Go to the documentation of this file.
1 //===-- StreamChecker.cpp -----------------------------------------*- C++ -*--//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file defines checkers that model and check stream handling functions.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "ClangSACheckers.h"
22 #include "llvm/ADT/ImmutableMap.h"
23 
24 using namespace clang;
25 using namespace ento;
26 
27 namespace {
28 
29 struct StreamState {
30  enum Kind { Opened, Closed, OpenFailed, Escaped } K;
31  const Stmt *S;
32 
33  StreamState(Kind k, const Stmt *s) : K(k), S(s) {}
34 
35  bool isOpened() const { return K == Opened; }
36  bool isClosed() const { return K == Closed; }
37  //bool isOpenFailed() const { return K == OpenFailed; }
38  //bool isEscaped() const { return K == Escaped; }
39 
40  bool operator==(const StreamState &X) const {
41  return K == X.K && S == X.S;
42  }
43 
44  static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); }
45  static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); }
46  static StreamState getOpenFailed(const Stmt *s) {
47  return StreamState(OpenFailed, s);
48  }
49  static StreamState getEscaped(const Stmt *s) {
50  return StreamState(Escaped, s);
51  }
52 
53  void Profile(llvm::FoldingSetNodeID &ID) const {
54  ID.AddInteger(K);
55  ID.AddPointer(S);
56  }
57 };
58 
59 class StreamChecker : public Checker<eval::Call,
60  check::DeadSymbols > {
61  mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread,
62  *II_fwrite,
63  *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
64  *II_clearerr, *II_feof, *II_ferror, *II_fileno;
65  mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
66  BT_doubleclose, BT_ResourceLeak;
67 
68 public:
69  StreamChecker()
70  : II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr),
71  II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr),
72  II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr),
73  II_fsetpos(nullptr), II_clearerr(nullptr), II_feof(nullptr),
74  II_ferror(nullptr), II_fileno(nullptr) {}
75 
76  bool evalCall(const CallExpr *CE, CheckerContext &C) const;
77  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
78 
79 private:
80  void Fopen(CheckerContext &C, const CallExpr *CE) const;
81  void Tmpfile(CheckerContext &C, const CallExpr *CE) const;
82  void Fclose(CheckerContext &C, const CallExpr *CE) const;
83  void Fread(CheckerContext &C, const CallExpr *CE) const;
84  void Fwrite(CheckerContext &C, const CallExpr *CE) const;
85  void Fseek(CheckerContext &C, const CallExpr *CE) const;
86  void Ftell(CheckerContext &C, const CallExpr *CE) const;
87  void Rewind(CheckerContext &C, const CallExpr *CE) const;
88  void Fgetpos(CheckerContext &C, const CallExpr *CE) const;
89  void Fsetpos(CheckerContext &C, const CallExpr *CE) const;
90  void Clearerr(CheckerContext &C, const CallExpr *CE) const;
91  void Feof(CheckerContext &C, const CallExpr *CE) const;
92  void Ferror(CheckerContext &C, const CallExpr *CE) const;
93  void Fileno(CheckerContext &C, const CallExpr *CE) const;
94 
95  void OpenFileAux(CheckerContext &C, const CallExpr *CE) const;
96 
97  ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state,
98  CheckerContext &C) const;
99  ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state,
100  CheckerContext &C) const;
101 };
102 
103 } // end anonymous namespace
104 
105 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
106 
107 
108 bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
109  const FunctionDecl *FD = C.getCalleeDecl(CE);
110  if (!FD || FD->getKind() != Decl::Function)
111  return false;
112 
113  ASTContext &Ctx = C.getASTContext();
114  if (!II_fopen)
115  II_fopen = &Ctx.Idents.get("fopen");
116  if (!II_tmpfile)
117  II_tmpfile = &Ctx.Idents.get("tmpfile");
118  if (!II_fclose)
119  II_fclose = &Ctx.Idents.get("fclose");
120  if (!II_fread)
121  II_fread = &Ctx.Idents.get("fread");
122  if (!II_fwrite)
123  II_fwrite = &Ctx.Idents.get("fwrite");
124  if (!II_fseek)
125  II_fseek = &Ctx.Idents.get("fseek");
126  if (!II_ftell)
127  II_ftell = &Ctx.Idents.get("ftell");
128  if (!II_rewind)
129  II_rewind = &Ctx.Idents.get("rewind");
130  if (!II_fgetpos)
131  II_fgetpos = &Ctx.Idents.get("fgetpos");
132  if (!II_fsetpos)
133  II_fsetpos = &Ctx.Idents.get("fsetpos");
134  if (!II_clearerr)
135  II_clearerr = &Ctx.Idents.get("clearerr");
136  if (!II_feof)
137  II_feof = &Ctx.Idents.get("feof");
138  if (!II_ferror)
139  II_ferror = &Ctx.Idents.get("ferror");
140  if (!II_fileno)
141  II_fileno = &Ctx.Idents.get("fileno");
142 
143  if (FD->getIdentifier() == II_fopen) {
144  Fopen(C, CE);
145  return true;
146  }
147  if (FD->getIdentifier() == II_tmpfile) {
148  Tmpfile(C, CE);
149  return true;
150  }
151  if (FD->getIdentifier() == II_fclose) {
152  Fclose(C, CE);
153  return true;
154  }
155  if (FD->getIdentifier() == II_fread) {
156  Fread(C, CE);
157  return true;
158  }
159  if (FD->getIdentifier() == II_fwrite) {
160  Fwrite(C, CE);
161  return true;
162  }
163  if (FD->getIdentifier() == II_fseek) {
164  Fseek(C, CE);
165  return true;
166  }
167  if (FD->getIdentifier() == II_ftell) {
168  Ftell(C, CE);
169  return true;
170  }
171  if (FD->getIdentifier() == II_rewind) {
172  Rewind(C, CE);
173  return true;
174  }
175  if (FD->getIdentifier() == II_fgetpos) {
176  Fgetpos(C, CE);
177  return true;
178  }
179  if (FD->getIdentifier() == II_fsetpos) {
180  Fsetpos(C, CE);
181  return true;
182  }
183  if (FD->getIdentifier() == II_clearerr) {
184  Clearerr(C, CE);
185  return true;
186  }
187  if (FD->getIdentifier() == II_feof) {
188  Feof(C, CE);
189  return true;
190  }
191  if (FD->getIdentifier() == II_ferror) {
192  Ferror(C, CE);
193  return true;
194  }
195  if (FD->getIdentifier() == II_fileno) {
196  Fileno(C, CE);
197  return true;
198  }
199 
200  return false;
201 }
202 
203 void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const {
204  OpenFileAux(C, CE);
205 }
206 
207 void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const {
208  OpenFileAux(C, CE);
209 }
210 
211 void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const {
212  ProgramStateRef state = C.getState();
213  SValBuilder &svalBuilder = C.getSValBuilder();
215  DefinedSVal RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
216  C.blockCount())
217  .castAs<DefinedSVal>();
218  state = state->BindExpr(CE, C.getLocationContext(), RetVal);
219 
221  // Bifurcate the state into two: one with a valid FILE* pointer, the other
222  // with a NULL.
223  ProgramStateRef stateNotNull, stateNull;
224  std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
225 
226  if (SymbolRef Sym = RetVal.getAsSymbol()) {
227  // if RetVal is not NULL, set the symbol's state to Opened.
228  stateNotNull =
229  stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE));
230  stateNull =
231  stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE));
232 
233  C.addTransition(stateNotNull);
234  C.addTransition(stateNull);
235  }
236 }
237 
238 void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const {
239  ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C);
240  if (state)
241  C.addTransition(state);
242 }
243 
244 void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const {
245  ProgramStateRef state = C.getState();
246  if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()),
247  state, C))
248  return;
249 }
250 
251 void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const {
252  ProgramStateRef state = C.getState();
253  if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()),
254  state, C))
255  return;
256 }
257 
258 void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
259  ProgramStateRef state = C.getState();
260  if (!(state = CheckNullStream(state->getSVal(CE->getArg(0),
261  C.getLocationContext()), state, C)))
262  return;
263  // Check the legality of the 'whence' argument of 'fseek'.
264  SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext());
266 
267  if (!CI)
268  return;
269 
270  int64_t x = CI->getValue().getSExtValue();
271  if (x >= 0 && x <= 2)
272  return;
273 
274  if (ExplodedNode *N = C.addTransition(state)) {
275  if (!BT_illegalwhence)
276  BT_illegalwhence.reset(
277  new BuiltinBug(this, "Illegal whence argument",
278  "The whence argument to fseek() should be "
279  "SEEK_SET, SEEK_END, or SEEK_CUR."));
280  C.emitReport(llvm::make_unique<BugReport>(
281  *BT_illegalwhence, BT_illegalwhence->getDescription(), N));
282  }
283 }
284 
285 void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const {
286  ProgramStateRef state = C.getState();
287  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
288  state, C))
289  return;
290 }
291 
292 void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const {
293  ProgramStateRef state = C.getState();
294  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
295  state, C))
296  return;
297 }
298 
299 void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const {
300  ProgramStateRef state = C.getState();
301  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
302  state, C))
303  return;
304 }
305 
306 void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const {
307  ProgramStateRef state = C.getState();
308  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
309  state, C))
310  return;
311 }
312 
313 void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const {
314  ProgramStateRef state = C.getState();
315  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
316  state, C))
317  return;
318 }
319 
320 void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const {
321  ProgramStateRef state = C.getState();
322  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
323  state, C))
324  return;
325 }
326 
327 void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const {
328  ProgramStateRef state = C.getState();
329  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
330  state, C))
331  return;
332 }
333 
334 void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const {
335  ProgramStateRef state = C.getState();
336  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
337  state, C))
338  return;
339 }
340 
341 ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state,
342  CheckerContext &C) const {
344  if (!DV)
345  return nullptr;
346 
348  ProgramStateRef stateNotNull, stateNull;
349  std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
350 
351  if (!stateNotNull && stateNull) {
352  if (ExplodedNode *N = C.generateSink(stateNull)) {
353  if (!BT_nullfp)
354  BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
355  "Stream pointer might be NULL."));
356  C.emitReport(llvm::make_unique<BugReport>(
357  *BT_nullfp, BT_nullfp->getDescription(), N));
358  }
359  return nullptr;
360  }
361  return stateNotNull;
362 }
363 
364 ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
365  ProgramStateRef state,
366  CheckerContext &C) const {
367  SymbolRef Sym =
368  state->getSVal(CE->getArg(0), C.getLocationContext()).getAsSymbol();
369  if (!Sym)
370  return state;
371 
372  const StreamState *SS = state->get<StreamMap>(Sym);
373 
374  // If the file stream is not tracked, return.
375  if (!SS)
376  return state;
377 
378  // Check: Double close a File Descriptor could cause undefined behaviour.
379  // Conforming to man-pages
380  if (SS->isClosed()) {
381  ExplodedNode *N = C.generateSink();
382  if (N) {
383  if (!BT_doubleclose)
384  BT_doubleclose.reset(new BuiltinBug(
385  this, "Double fclose", "Try to close a file Descriptor already"
386  " closed. Cause undefined behaviour."));
387  C.emitReport(llvm::make_unique<BugReport>(
388  *BT_doubleclose, BT_doubleclose->getDescription(), N));
389  }
390  return nullptr;
391  }
392 
393  // Close the File Descriptor.
394  return state->set<StreamMap>(Sym, StreamState::getClosed(CE));
395 }
396 
397 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
398  CheckerContext &C) const {
399  // TODO: Clean up the state.
400  for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
401  E = SymReaper.dead_end(); I != E; ++I) {
402  SymbolRef Sym = *I;
403  ProgramStateRef state = C.getState();
404  const StreamState *SS = state->get<StreamMap>(Sym);
405  if (!SS)
406  continue;
407 
408  if (SS->isOpened()) {
409  ExplodedNode *N = C.generateSink();
410  if (N) {
411  if (!BT_ResourceLeak)
412  BT_ResourceLeak.reset(new BuiltinBug(
413  this, "Resource Leak",
414  "Opened File never closed. Potential Resource leak."));
415  C.emitReport(llvm::make_unique<BugReport>(
416  *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N));
417  }
418  }
419  }
420 }
421 
422 void ento::registerStreamChecker(CheckerManager &mgr) {
423  mgr.registerChecker<StreamChecker>();
424 }
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2216
bool operator==(CanQual< T > x, CanQual< U > y)
IdentifierInfo * getIdentifier() const
Definition: Decl.h:163
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph). Uses the default CheckerContex...
Value representing integer constant.
Definition: SVals.h:339
ExplodedNode * getPredecessor()
Returns the previous node in the exploded graph, which includes the state of the program before the c...
Symbolic value. These values used to capture symbolic execution of the program.
Definition: SymbolManager.h:42
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:89
unsigned blockCount() const
Returns the number of times the current block has been visited along the analyzed path...
IdentifierTable & Idents
Definition: ASTContext.h:439
dead_iterator dead_begin() const
const LocationContext * getLocationContext() const
ProgramStatePair assumeDual(ProgramStateRef State, DefinedSVal Cond)
ExplodedNode * generateSink(ProgramStateRef State=nullptr, ExplodedNode *Pred=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a sink node. Generating a sink stops exploration of the given path.
ID
Defines the set of possible language-specific address spaces.
Definition: AddressSpaces.h:27
const ProgramStateRef & getState() const
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
Definition: SVals.h:86
SymbolSetTy::const_iterator dead_iterator
Kind getKind() const
Definition: DeclBase.h:375
#define bool
Definition: stdbool.h:31
ConstraintManager & getConstraintManager()
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
Kind
CHECKER * registerChecker()
Used to register checkers.
DefinedOrUnknownSVal conjureSymbolVal(const void *symbolTag, const Expr *expr, const LocationContext *LCtx, unsigned count)
Create a new symbol with a unique 'name'.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
dead_iterator dead_end() const
A class responsible for cleaning up unused symbols.
REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData, SymbolRef, MacOSKeychainAPIChecker::AllocationState) static bool isEnclosingFunctionParam(const Expr *E)
X
Definition: SemaDecl.cpp:11429
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef. Otherwise, return 0.
Definition: SVals.cpp:111
SValBuilder & getSValBuilder()
const LocationContext * getLocationContext() const