clang  7.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(C.getSVal(CE->getArg(3)), state, C))
246  return;
247 }
248 
249 void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const {
250  ProgramStateRef state = C.getState();
251  if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C))
252  return;
253 }
254 
255 void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
256  ProgramStateRef state = C.getState();
257  if (!(state = CheckNullStream(C.getSVal(CE->getArg(0)), state, C)))
258  return;
259  // Check the legality of the 'whence' argument of 'fseek'.
260  SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext());
262 
263  if (!CI)
264  return;
265 
266  int64_t x = CI->getValue().getSExtValue();
267  if (x >= 0 && x <= 2)
268  return;
269 
270  if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
271  if (!BT_illegalwhence)
272  BT_illegalwhence.reset(
273  new BuiltinBug(this, "Illegal whence argument",
274  "The whence argument to fseek() should be "
275  "SEEK_SET, SEEK_END, or SEEK_CUR."));
276  C.emitReport(llvm::make_unique<BugReport>(
277  *BT_illegalwhence, BT_illegalwhence->getDescription(), N));
278  }
279 }
280 
281 void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const {
282  ProgramStateRef state = C.getState();
283  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
284  return;
285 }
286 
287 void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const {
288  ProgramStateRef state = C.getState();
289  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
290  return;
291 }
292 
293 void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const {
294  ProgramStateRef state = C.getState();
295  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
296  return;
297 }
298 
299 void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const {
300  ProgramStateRef state = C.getState();
301  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
302  return;
303 }
304 
305 void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const {
306  ProgramStateRef state = C.getState();
307  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
308  return;
309 }
310 
311 void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const {
312  ProgramStateRef state = C.getState();
313  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
314  return;
315 }
316 
317 void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const {
318  ProgramStateRef state = C.getState();
319  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
320  return;
321 }
322 
323 void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const {
324  ProgramStateRef state = C.getState();
325  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
326  return;
327 }
328 
329 ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state,
330  CheckerContext &C) const {
332  if (!DV)
333  return nullptr;
334 
336  ProgramStateRef stateNotNull, stateNull;
337  std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
338 
339  if (!stateNotNull && stateNull) {
340  if (ExplodedNode *N = C.generateErrorNode(stateNull)) {
341  if (!BT_nullfp)
342  BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
343  "Stream pointer might be NULL."));
344  C.emitReport(llvm::make_unique<BugReport>(
345  *BT_nullfp, BT_nullfp->getDescription(), N));
346  }
347  return nullptr;
348  }
349  return stateNotNull;
350 }
351 
352 ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
353  ProgramStateRef state,
354  CheckerContext &C) const {
355  SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
356  if (!Sym)
357  return state;
358 
359  const StreamState *SS = state->get<StreamMap>(Sym);
360 
361  // If the file stream is not tracked, return.
362  if (!SS)
363  return state;
364 
365  // Check: Double close a File Descriptor could cause undefined behaviour.
366  // Conforming to man-pages
367  if (SS->isClosed()) {
369  if (N) {
370  if (!BT_doubleclose)
371  BT_doubleclose.reset(new BuiltinBug(
372  this, "Double fclose", "Try to close a file Descriptor already"
373  " closed. Cause undefined behaviour."));
374  C.emitReport(llvm::make_unique<BugReport>(
375  *BT_doubleclose, BT_doubleclose->getDescription(), N));
376  }
377  return nullptr;
378  }
379 
380  // Close the File Descriptor.
381  return state->set<StreamMap>(Sym, StreamState::getClosed(CE));
382 }
383 
384 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
385  CheckerContext &C) const {
386  // TODO: Clean up the state.
387  for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
388  E = SymReaper.dead_end(); I != E; ++I) {
389  SymbolRef Sym = *I;
390  ProgramStateRef state = C.getState();
391  const StreamState *SS = state->get<StreamMap>(Sym);
392  if (!SS)
393  continue;
394 
395  if (SS->isOpened()) {
397  if (N) {
398  if (!BT_ResourceLeak)
399  BT_ResourceLeak.reset(new BuiltinBug(
400  this, "Resource Leak",
401  "Opened File never closed. Potential Resource leak."));
402  C.emitReport(llvm::make_unique<BugReport>(
403  *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N));
404  }
405  }
406  }
407 }
408 
409 void ento::registerStreamChecker(CheckerManager &mgr) {
410  mgr.registerChecker<StreamChecker>();
411 }
Represents a function declaration or definition.
Definition: Decl.h:1696
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:2287
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...
SVal getSVal(const Stmt *S) const
Get the value of arbitrary expressions at this point in the path.
Symbolic value.
Definition: SymExpr.h:29
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:269
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:13401
SValBuilder & getSValBuilder()
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2218
const LocationContext * getLocationContext() const