clang  3.9.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 {
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.generateNonFatalErrorNode(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.generateErrorNode(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()) {
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()) {
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 }
FunctionDecl - An instance of this class is created to represent a function declaration or definition...
Definition: Decl.h:1561
ExplodedNode * generateErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2217
bool operator==(CanQual< T > x, CanQual< U > y)
IdentifierInfo * getIdentifier() const
getIdentifier - Get the identifier that names this declaration, if there is one.
Definition: Decl.h:232
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
Value representing integer constant.
Definition: SVals.h:341
ExplodedNode * getPredecessor()
Returns the previous node in the exploded graph, which includes the state of the program before the c...
Symbolic value.
Definition: SymExpr.h:29
One of these records is kept for each identifier that is lexed.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:92
unsigned blockCount() const
Returns the number of times the current block has been visited along the analyzed path...
i32 captured_struct **param SharedsTy A type which contains references the shared variables *param Shareds Context with the list of shared variables from the p *TaskFunction *param Data Additional data for task generation like final * state
IdentifierTable & Idents
Definition: ASTContext.h:459
dead_iterator dead_begin() const
detail::InMemoryDirectory::const_iterator I
const LocationContext * getLocationContext() const
ProgramStatePair assumeDual(ProgramStateRef State, DefinedSVal Cond)
Returns a pair of states (StTrue, StFalse) where the given condition is assumed to be true or false...
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
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
#define bool
Definition: stdbool.h:31
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
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.
const std::string ID
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
Definition: SVals.h:46
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx)
Definition: Type.h:4262
dead_iterator dead_end() const
A class responsible for cleaning up unused symbols.
detail::InMemoryDirectory::const_iterator E
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Definition: SemaDecl.cpp:12171
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:111
SValBuilder & getSValBuilder()
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2148
const LocationContext * getLocationContext() const