clang  6.0.0svn
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 
23 using namespace clang;
24 using namespace ento;
25 
26 namespace {
27 
28 struct StreamState {
29  enum Kind { Opened, Closed, OpenFailed, Escaped } K;
30  const Stmt *S;
31 
32  StreamState(Kind k, const Stmt *s) : K(k), S(s) {}
33 
34  bool isOpened() const { return K == Opened; }
35  bool isClosed() const { return K == Closed; }
36  //bool isOpenFailed() const { return K == OpenFailed; }
37  //bool isEscaped() const { return K == Escaped; }
38 
39  bool operator==(const StreamState &X) const {
40  return K == X.K && S == X.S;
41  }
42 
43  static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); }
44  static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); }
45  static StreamState getOpenFailed(const Stmt *s) {
46  return StreamState(OpenFailed, s);
47  }
48  static StreamState getEscaped(const Stmt *s) {
49  return StreamState(Escaped, s);
50  }
51 
52  void Profile(llvm::FoldingSetNodeID &ID) const {
53  ID.AddInteger(K);
54  ID.AddPointer(S);
55  }
56 };
57 
58 class StreamChecker : public Checker<eval::Call,
59  check::DeadSymbols > {
60  mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread,
61  *II_fwrite,
62  *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
63  *II_clearerr, *II_feof, *II_ferror, *II_fileno;
64  mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
65  BT_doubleclose, BT_ResourceLeak;
66 
67 public:
68  StreamChecker()
69  : II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr),
70  II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr),
71  II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr),
72  II_fsetpos(nullptr), II_clearerr(nullptr), II_feof(nullptr),
73  II_ferror(nullptr), II_fileno(nullptr) {}
74 
75  bool evalCall(const CallExpr *CE, CheckerContext &C) const;
76  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
77 
78 private:
79  void Fopen(CheckerContext &C, const CallExpr *CE) const;
80  void Tmpfile(CheckerContext &C, const CallExpr *CE) const;
81  void Fclose(CheckerContext &C, const CallExpr *CE) const;
82  void Fread(CheckerContext &C, const CallExpr *CE) const;
83  void Fwrite(CheckerContext &C, const CallExpr *CE) const;
84  void Fseek(CheckerContext &C, const CallExpr *CE) const;
85  void Ftell(CheckerContext &C, const CallExpr *CE) const;
86  void Rewind(CheckerContext &C, const CallExpr *CE) const;
87  void Fgetpos(CheckerContext &C, const CallExpr *CE) const;
88  void Fsetpos(CheckerContext &C, const CallExpr *CE) const;
89  void Clearerr(CheckerContext &C, const CallExpr *CE) const;
90  void Feof(CheckerContext &C, const CallExpr *CE) const;
91  void Ferror(CheckerContext &C, const CallExpr *CE) const;
92  void Fileno(CheckerContext &C, const CallExpr *CE) const;
93 
94  void OpenFileAux(CheckerContext &C, const CallExpr *CE) const;
95 
96  ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state,
97  CheckerContext &C) const;
98  ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state,
99  CheckerContext &C) const;
100 };
101 
102 } // end anonymous namespace
103 
104 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
105 
106 
107 bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
108  const FunctionDecl *FD = C.getCalleeDecl(CE);
109  if (!FD || FD->getKind() != Decl::Function)
110  return false;
111 
112  ASTContext &Ctx = C.getASTContext();
113  if (!II_fopen)
114  II_fopen = &Ctx.Idents.get("fopen");
115  if (!II_tmpfile)
116  II_tmpfile = &Ctx.Idents.get("tmpfile");
117  if (!II_fclose)
118  II_fclose = &Ctx.Idents.get("fclose");
119  if (!II_fread)
120  II_fread = &Ctx.Idents.get("fread");
121  if (!II_fwrite)
122  II_fwrite = &Ctx.Idents.get("fwrite");
123  if (!II_fseek)
124  II_fseek = &Ctx.Idents.get("fseek");
125  if (!II_ftell)
126  II_ftell = &Ctx.Idents.get("ftell");
127  if (!II_rewind)
128  II_rewind = &Ctx.Idents.get("rewind");
129  if (!II_fgetpos)
130  II_fgetpos = &Ctx.Idents.get("fgetpos");
131  if (!II_fsetpos)
132  II_fsetpos = &Ctx.Idents.get("fsetpos");
133  if (!II_clearerr)
134  II_clearerr = &Ctx.Idents.get("clearerr");
135  if (!II_feof)
136  II_feof = &Ctx.Idents.get("feof");
137  if (!II_ferror)
138  II_ferror = &Ctx.Idents.get("ferror");
139  if (!II_fileno)
140  II_fileno = &Ctx.Idents.get("fileno");
141 
142  if (FD->getIdentifier() == II_fopen) {
143  Fopen(C, CE);
144  return true;
145  }
146  if (FD->getIdentifier() == II_tmpfile) {
147  Tmpfile(C, CE);
148  return true;
149  }
150  if (FD->getIdentifier() == II_fclose) {
151  Fclose(C, CE);
152  return true;
153  }
154  if (FD->getIdentifier() == II_fread) {
155  Fread(C, CE);
156  return true;
157  }
158  if (FD->getIdentifier() == II_fwrite) {
159  Fwrite(C, CE);
160  return true;
161  }
162  if (FD->getIdentifier() == II_fseek) {
163  Fseek(C, CE);
164  return true;
165  }
166  if (FD->getIdentifier() == II_ftell) {
167  Ftell(C, CE);
168  return true;
169  }
170  if (FD->getIdentifier() == II_rewind) {
171  Rewind(C, CE);
172  return true;
173  }
174  if (FD->getIdentifier() == II_fgetpos) {
175  Fgetpos(C, CE);
176  return true;
177  }
178  if (FD->getIdentifier() == II_fsetpos) {
179  Fsetpos(C, CE);
180  return true;
181  }
182  if (FD->getIdentifier() == II_clearerr) {
183  Clearerr(C, CE);
184  return true;
185  }
186  if (FD->getIdentifier() == II_feof) {
187  Feof(C, CE);
188  return true;
189  }
190  if (FD->getIdentifier() == II_ferror) {
191  Ferror(C, CE);
192  return true;
193  }
194  if (FD->getIdentifier() == II_fileno) {
195  Fileno(C, CE);
196  return true;
197  }
198 
199  return false;
200 }
201 
202 void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const {
203  OpenFileAux(C, CE);
204 }
205 
206 void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const {
207  OpenFileAux(C, CE);
208 }
209 
210 void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const {
212  SValBuilder &svalBuilder = C.getSValBuilder();
214  DefinedSVal RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
215  C.blockCount())
216  .castAs<DefinedSVal>();
217  state = state->BindExpr(CE, C.getLocationContext(), RetVal);
218 
220  // Bifurcate the state into two: one with a valid FILE* pointer, the other
221  // with a NULL.
222  ProgramStateRef stateNotNull, stateNull;
223  std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
224 
225  if (SymbolRef Sym = RetVal.getAsSymbol()) {
226  // if RetVal is not NULL, set the symbol's state to Opened.
227  stateNotNull =
228  stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE));
229  stateNull =
230  stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE));
231 
232  C.addTransition(stateNotNull);
233  C.addTransition(stateNull);
234  }
235 }
236 
237 void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const {
238  ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C);
239  if (state)
240  C.addTransition(state);
241 }
242 
243 void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const {
244  ProgramStateRef state = C.getState();
245  if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()),
246  state, C))
247  return;
248 }
249 
250 void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const {
251  ProgramStateRef state = C.getState();
252  if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()),
253  state, C))
254  return;
255 }
256 
257 void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
258  ProgramStateRef state = C.getState();
259  if (!(state = CheckNullStream(state->getSVal(CE->getArg(0),
260  C.getLocationContext()), state, C)))
261  return;
262  // Check the legality of the 'whence' argument of 'fseek'.
263  SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext());
265 
266  if (!CI)
267  return;
268 
269  int64_t x = CI->getValue().getSExtValue();
270  if (x >= 0 && x <= 2)
271  return;
272 
273  if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
274  if (!BT_illegalwhence)
275  BT_illegalwhence.reset(
276  new BuiltinBug(this, "Illegal whence argument",
277  "The whence argument to fseek() should be "
278  "SEEK_SET, SEEK_END, or SEEK_CUR."));
279  C.emitReport(llvm::make_unique<BugReport>(
280  *BT_illegalwhence, BT_illegalwhence->getDescription(), N));
281  }
282 }
283 
284 void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const {
285  ProgramStateRef state = C.getState();
286  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
287  state, C))
288  return;
289 }
290 
291 void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const {
292  ProgramStateRef state = C.getState();
293  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
294  state, C))
295  return;
296 }
297 
298 void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const {
299  ProgramStateRef state = C.getState();
300  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
301  state, C))
302  return;
303 }
304 
305 void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const {
306  ProgramStateRef state = C.getState();
307  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
308  state, C))
309  return;
310 }
311 
312 void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const {
313  ProgramStateRef state = C.getState();
314  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
315  state, C))
316  return;
317 }
318 
319 void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const {
320  ProgramStateRef state = C.getState();
321  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
322  state, C))
323  return;
324 }
325 
326 void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const {
327  ProgramStateRef state = C.getState();
328  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
329  state, C))
330  return;
331 }
332 
333 void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const {
334  ProgramStateRef state = C.getState();
335  if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
336  state, C))
337  return;
338 }
339 
340 ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state,
341  CheckerContext &C) const {
343  if (!DV)
344  return nullptr;
345 
347  ProgramStateRef stateNotNull, stateNull;
348  std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
349 
350  if (!stateNotNull && stateNull) {
351  if (ExplodedNode *N = C.generateErrorNode(stateNull)) {
352  if (!BT_nullfp)
353  BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
354  "Stream pointer might be NULL."));
355  C.emitReport(llvm::make_unique<BugReport>(
356  *BT_nullfp, BT_nullfp->getDescription(), N));
357  }
358  return nullptr;
359  }
360  return stateNotNull;
361 }
362 
363 ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
364  ProgramStateRef state,
365  CheckerContext &C) const {
366  SymbolRef Sym =
367  state->getSVal(CE->getArg(0), C.getLocationContext()).getAsSymbol();
368  if (!Sym)
369  return state;
370 
371  const StreamState *SS = state->get<StreamMap>(Sym);
372 
373  // If the file stream is not tracked, return.
374  if (!SS)
375  return state;
376 
377  // Check: Double close a File Descriptor could cause undefined behaviour.
378  // Conforming to man-pages
379  if (SS->isClosed()) {
381  if (N) {
382  if (!BT_doubleclose)
383  BT_doubleclose.reset(new BuiltinBug(
384  this, "Double fclose", "Try to close a file Descriptor already"
385  " closed. Cause undefined behaviour."));
386  C.emitReport(llvm::make_unique<BugReport>(
387  *BT_doubleclose, BT_doubleclose->getDescription(), N));
388  }
389  return nullptr;
390  }
391 
392  // Close the File Descriptor.
393  return state->set<StreamMap>(Sym, StreamState::getClosed(CE));
394 }
395 
396 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
397  CheckerContext &C) const {
398  // TODO: Clean up the state.
399  for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
400  E = SymReaper.dead_end(); I != E; ++I) {
401  SymbolRef Sym = *I;
402  ProgramStateRef state = C.getState();
403  const StreamState *SS = state->get<StreamMap>(Sym);
404  if (!SS)
405  continue;
406 
407  if (SS->isOpened()) {
409  if (N) {
410  if (!BT_ResourceLeak)
411  BT_ResourceLeak.reset(new BuiltinBug(
412  this, "Resource Leak",
413  "Opened File never closed. Potential Resource leak."));
414  C.emitReport(llvm::make_unique<BugReport>(
415  *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N));
416  }
417  }
418  }
419 }
420 
421 void ento::registerStreamChecker(CheckerManager &mgr) {
422  mgr.registerChecker<StreamChecker>();
423 }
FunctionDecl - An instance of this class is created to represent a function declaration or definition...
Definition: Decl.h:1698
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:2278
bool operator==(CanQual< T > x, CanQual< U > y)
Stmt - This represents one statement.
Definition: Stmt.h:66
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:353
const FunctionDecl * getCalleeDecl(const CallExpr *CE) const
Get the declaration of the called function (path-sensitive).
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
IdentifierInfo * getIdentifier() const
getIdentifier - Get the identifier that names this declaration, if there is one.
Definition: Decl.h:265
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:149
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:537
const LocationContext * getLocationContext() const
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:116
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.
SymbolSetTy::const_iterator dead_iterator
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
Definition: SVals.h:100
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 &#39;name&#39;.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
Definition: SVals.h:63
A class responsible for cleaning up unused symbols.
unsigned blockCount() const
Returns the number of times the current block has been visited along the analyzed path...
Dataflow Directional Tag Classes.
Kind getKind() const
Definition: DeclBase.h:419
dead_iterator dead_begin() const
const ProgramStateRef & getState() const
dead_iterator dead_end() const
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Definition: SemaDecl.cpp:13010
SValBuilder & getSValBuilder()
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2209
const LocationContext * getLocationContext() const