StreamChecker.cpp revision ba37d3b2ef37c3591a4f673215d78cb9cc928de3
1069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project//===-- StreamChecker.cpp -----------------------------------------*- C++ -*--//
2069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project//
3069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project//                     The LLVM Compiler Infrastructure
4069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project//
5069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project// This file is distributed under the University of Illinois Open Source
6069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project// License. See LICENSE.TXT for details.
7069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project//
8069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project//===----------------------------------------------------------------------===//
9069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project//
10069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project// This file defines checkers that model and check stream handling functions.
11069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project//
12069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project//===----------------------------------------------------------------------===//
13069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
14069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project#include "ExprEngineExperimentalChecks.h"
15069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project#include "clang/StaticAnalyzer/BugReporter/BugType.h"
16069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h"
17069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project#include "clang/StaticAnalyzer/PathSensitive/GRState.h"
18069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h"
19069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.h"
20069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project#include "llvm/ADT/ImmutableMap.h"
21069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
22069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectusing namespace clang;
23069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectusing namespace ento;
24069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
25069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectnamespace {
26069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
27069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectstruct StreamState {
28069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  enum Kind { Opened, Closed, OpenFailed, Escaped } K;
29069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  const Stmt *S;
30069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
31069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  StreamState(Kind k, const Stmt *s) : K(k), S(s) {}
32069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
33069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  bool isOpened() const { return K == Opened; }
34069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  bool isClosed() const { return K == Closed; }
35069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  //bool isOpenFailed() const { return K == OpenFailed; }
36069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  //bool isEscaped() const { return K == Escaped; }
37069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
38069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  bool operator==(const StreamState &X) const {
39069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    return K == X.K && S == X.S;
40069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  }
41d42abb2fd917184764daf22f5f299e848b8701d7Narayan Kamath
42d42abb2fd917184764daf22f5f299e848b8701d7Narayan Kamath  static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); }
43d42abb2fd917184764daf22f5f299e848b8701d7Narayan Kamath  static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); }
44d42abb2fd917184764daf22f5f299e848b8701d7Narayan Kamath  static StreamState getOpenFailed(const Stmt *s) {
45069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    return StreamState(OpenFailed, s);
46d42abb2fd917184764daf22f5f299e848b8701d7Narayan Kamath  }
47069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  static StreamState getEscaped(const Stmt *s) {
48069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    return StreamState(Escaped, s);
49069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  }
50069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
51069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  void Profile(llvm::FoldingSetNodeID &ID) const {
52069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    ID.AddInteger(K);
53069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    ID.AddPointer(S);
54069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  }
55069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project};
56069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
57069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectclass StreamChecker : public CheckerVisitor<StreamChecker> {
58069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, *II_fwrite,
59069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                 *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
60069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                 *II_clearerr, *II_feof, *II_ferror, *II_fileno;
61069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  BuiltinBug *BT_nullfp, *BT_illegalwhence, *BT_doubleclose, *BT_ResourceLeak;
62069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
63069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectpublic:
64069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project  StreamChecker()
65069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    : II_fopen(0), II_tmpfile(0) ,II_fclose(0), II_fread(0), II_fwrite(0),
66069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project      II_fseek(0), II_ftell(0), II_rewind(0), II_fgetpos(0), II_fsetpos(0),
67069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project      II_clearerr(0), II_feof(0), II_ferror(0), II_fileno(0),
68      BT_nullfp(0), BT_illegalwhence(0), BT_doubleclose(0),
69      BT_ResourceLeak(0) {}
70
71  static void *getTag() {
72    static int x;
73    return &x;
74  }
75
76  virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE);
77  void evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper);
78  void evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, ExprEngine &Eng);
79  void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S);
80
81private:
82  void Fopen(CheckerContext &C, const CallExpr *CE);
83  void Tmpfile(CheckerContext &C, const CallExpr *CE);
84  void Fclose(CheckerContext &C, const CallExpr *CE);
85  void Fread(CheckerContext &C, const CallExpr *CE);
86  void Fwrite(CheckerContext &C, const CallExpr *CE);
87  void Fseek(CheckerContext &C, const CallExpr *CE);
88  void Ftell(CheckerContext &C, const CallExpr *CE);
89  void Rewind(CheckerContext &C, const CallExpr *CE);
90  void Fgetpos(CheckerContext &C, const CallExpr *CE);
91  void Fsetpos(CheckerContext &C, const CallExpr *CE);
92  void Clearerr(CheckerContext &C, const CallExpr *CE);
93  void Feof(CheckerContext &C, const CallExpr *CE);
94  void Ferror(CheckerContext &C, const CallExpr *CE);
95  void Fileno(CheckerContext &C, const CallExpr *CE);
96
97  void OpenFileAux(CheckerContext &C, const CallExpr *CE);
98
99  const GRState *CheckNullStream(SVal SV, const GRState *state,
100                                 CheckerContext &C);
101  const GRState *CheckDoubleClose(const CallExpr *CE, const GRState *state,
102                                 CheckerContext &C);
103};
104
105} // end anonymous namespace
106
107namespace clang {
108namespace ento {
109  template <>
110  struct GRStateTrait<StreamState>
111    : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, StreamState> > {
112    static void *GDMIndex() { return StreamChecker::getTag(); }
113  };
114}
115}
116
117void ento::RegisterStreamChecker(ExprEngine &Eng) {
118  Eng.registerCheck(new StreamChecker());
119}
120
121bool StreamChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) {
122  const GRState *state = C.getState();
123  const Expr *Callee = CE->getCallee();
124  SVal L = state->getSVal(Callee);
125  const FunctionDecl *FD = L.getAsFunctionDecl();
126  if (!FD)
127    return false;
128
129  ASTContext &Ctx = C.getASTContext();
130  if (!II_fopen)
131    II_fopen = &Ctx.Idents.get("fopen");
132  if (!II_tmpfile)
133    II_tmpfile = &Ctx.Idents.get("tmpfile");
134  if (!II_fclose)
135    II_fclose = &Ctx.Idents.get("fclose");
136  if (!II_fread)
137    II_fread = &Ctx.Idents.get("fread");
138  if (!II_fwrite)
139    II_fwrite = &Ctx.Idents.get("fwrite");
140  if (!II_fseek)
141    II_fseek = &Ctx.Idents.get("fseek");
142  if (!II_ftell)
143    II_ftell = &Ctx.Idents.get("ftell");
144  if (!II_rewind)
145    II_rewind = &Ctx.Idents.get("rewind");
146  if (!II_fgetpos)
147    II_fgetpos = &Ctx.Idents.get("fgetpos");
148  if (!II_fsetpos)
149    II_fsetpos = &Ctx.Idents.get("fsetpos");
150  if (!II_clearerr)
151    II_clearerr = &Ctx.Idents.get("clearerr");
152  if (!II_feof)
153    II_feof = &Ctx.Idents.get("feof");
154  if (!II_ferror)
155    II_ferror = &Ctx.Idents.get("ferror");
156  if (!II_fileno)
157    II_fileno = &Ctx.Idents.get("fileno");
158
159  if (FD->getIdentifier() == II_fopen) {
160    Fopen(C, CE);
161    return true;
162  }
163  if (FD->getIdentifier() == II_tmpfile) {
164    Tmpfile(C, CE);
165    return true;
166  }
167  if (FD->getIdentifier() == II_fclose) {
168    Fclose(C, CE);
169    return true;
170  }
171  if (FD->getIdentifier() == II_fread) {
172    Fread(C, CE);
173    return true;
174  }
175  if (FD->getIdentifier() == II_fwrite) {
176    Fwrite(C, CE);
177    return true;
178  }
179  if (FD->getIdentifier() == II_fseek) {
180    Fseek(C, CE);
181    return true;
182  }
183  if (FD->getIdentifier() == II_ftell) {
184    Ftell(C, CE);
185    return true;
186  }
187  if (FD->getIdentifier() == II_rewind) {
188    Rewind(C, CE);
189    return true;
190  }
191  if (FD->getIdentifier() == II_fgetpos) {
192    Fgetpos(C, CE);
193    return true;
194  }
195  if (FD->getIdentifier() == II_fsetpos) {
196    Fsetpos(C, CE);
197    return true;
198  }
199  if (FD->getIdentifier() == II_clearerr) {
200    Clearerr(C, CE);
201    return true;
202  }
203  if (FD->getIdentifier() == II_feof) {
204    Feof(C, CE);
205    return true;
206  }
207  if (FD->getIdentifier() == II_ferror) {
208    Ferror(C, CE);
209    return true;
210  }
211  if (FD->getIdentifier() == II_fileno) {
212    Fileno(C, CE);
213    return true;
214  }
215
216  return false;
217}
218
219void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) {
220  OpenFileAux(C, CE);
221}
222
223void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) {
224  OpenFileAux(C, CE);
225}
226
227void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) {
228  const GRState *state = C.getState();
229  unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
230  SValBuilder &svalBuilder = C.getSValBuilder();
231  DefinedSVal RetVal =
232    cast<DefinedSVal>(svalBuilder.getConjuredSymbolVal(0, CE, Count));
233  state = state->BindExpr(CE, RetVal);
234
235  ConstraintManager &CM = C.getConstraintManager();
236  // Bifurcate the state into two: one with a valid FILE* pointer, the other
237  // with a NULL.
238  const GRState *stateNotNull, *stateNull;
239  llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
240
241  if (SymbolRef Sym = RetVal.getAsSymbol()) {
242    // if RetVal is not NULL, set the symbol's state to Opened.
243    stateNotNull =
244      stateNotNull->set<StreamState>(Sym,StreamState::getOpened(CE));
245    stateNull =
246      stateNull->set<StreamState>(Sym, StreamState::getOpenFailed(CE));
247
248    C.addTransition(stateNotNull);
249    C.addTransition(stateNull);
250  }
251}
252
253void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) {
254  const GRState *state = CheckDoubleClose(CE, C.getState(), C);
255  if (state)
256    C.addTransition(state);
257}
258
259void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) {
260  const GRState *state = C.getState();
261  if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C))
262    return;
263}
264
265void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) {
266  const GRState *state = C.getState();
267  if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C))
268    return;
269}
270
271void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) {
272  const GRState *state = C.getState();
273  if (!(state = CheckNullStream(state->getSVal(CE->getArg(0)), state, C)))
274    return;
275  // Check the legality of the 'whence' argument of 'fseek'.
276  SVal Whence = state->getSVal(CE->getArg(2));
277  const nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Whence);
278
279  if (!CI)
280    return;
281
282  int64_t x = CI->getValue().getSExtValue();
283  if (x >= 0 && x <= 2)
284    return;
285
286  if (ExplodedNode *N = C.generateNode(state)) {
287    if (!BT_illegalwhence)
288      BT_illegalwhence = new BuiltinBug("Illegal whence argument",
289					"The whence argument to fseek() should be "
290					"SEEK_SET, SEEK_END, or SEEK_CUR.");
291    BugReport *R = new BugReport(*BT_illegalwhence,
292				 BT_illegalwhence->getDescription(), N);
293    C.EmitReport(R);
294  }
295}
296
297void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) {
298  const GRState *state = C.getState();
299  if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
300    return;
301}
302
303void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) {
304  const GRState *state = C.getState();
305  if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
306    return;
307}
308
309void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) {
310  const GRState *state = C.getState();
311  if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
312    return;
313}
314
315void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) {
316  const GRState *state = C.getState();
317  if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
318    return;
319}
320
321void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) {
322  const GRState *state = C.getState();
323  if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
324    return;
325}
326
327void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) {
328  const GRState *state = C.getState();
329  if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
330    return;
331}
332
333void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) {
334  const GRState *state = C.getState();
335  if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
336    return;
337}
338
339void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) {
340  const GRState *state = C.getState();
341  if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
342    return;
343}
344
345const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state,
346                                    CheckerContext &C) {
347  const DefinedSVal *DV = dyn_cast<DefinedSVal>(&SV);
348  if (!DV)
349    return 0;
350
351  ConstraintManager &CM = C.getConstraintManager();
352  const GRState *stateNotNull, *stateNull;
353  llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
354
355  if (!stateNotNull && stateNull) {
356    if (ExplodedNode *N = C.generateSink(stateNull)) {
357      if (!BT_nullfp)
358        BT_nullfp = new BuiltinBug("NULL stream pointer",
359                                     "Stream pointer might be NULL.");
360      BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N);
361      C.EmitReport(R);
362    }
363    return 0;
364  }
365  return stateNotNull;
366}
367
368const GRState *StreamChecker::CheckDoubleClose(const CallExpr *CE,
369                                               const GRState *state,
370                                               CheckerContext &C) {
371  SymbolRef Sym = state->getSVal(CE->getArg(0)).getAsSymbol();
372  if (!Sym)
373    return state;
374
375  const StreamState *SS = state->get<StreamState>(Sym);
376
377  // If the file stream is not tracked, return.
378  if (!SS)
379    return state;
380
381  // Check: Double close a File Descriptor could cause undefined behaviour.
382  // Conforming to man-pages
383  if (SS->isClosed()) {
384    ExplodedNode *N = C.generateSink();
385    if (N) {
386      if (!BT_doubleclose)
387        BT_doubleclose = new BuiltinBug("Double fclose",
388                                        "Try to close a file Descriptor already"
389                                        " closed. Cause undefined behaviour.");
390      BugReport *R = new BugReport(*BT_doubleclose,
391                                   BT_doubleclose->getDescription(), N);
392      C.EmitReport(R);
393    }
394    return NULL;
395  }
396
397  // Close the File Descriptor.
398  return state->set<StreamState>(Sym, StreamState::getClosed(CE));
399}
400
401void StreamChecker::evalDeadSymbols(CheckerContext &C,SymbolReaper &SymReaper) {
402  for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
403         E = SymReaper.dead_end(); I != E; ++I) {
404    SymbolRef Sym = *I;
405    const GRState *state = C.getState();
406    const StreamState *SS = state->get<StreamState>(Sym);
407    if (!SS)
408      return;
409
410    if (SS->isOpened()) {
411      ExplodedNode *N = C.generateSink();
412      if (N) {
413        if (!BT_ResourceLeak)
414          BT_ResourceLeak = new BuiltinBug("Resource Leak",
415                          "Opened File never closed. Potential Resource leak.");
416        BugReport *R = new BugReport(*BT_ResourceLeak,
417                                     BT_ResourceLeak->getDescription(), N);
418        C.EmitReport(R);
419      }
420    }
421  }
422}
423
424void StreamChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag,
425                                ExprEngine &Eng) {
426  const GRState *state = B.getState();
427  typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap;
428  SymMap M = state->get<StreamState>();
429
430  for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) {
431    StreamState SS = I->second;
432    if (SS.isOpened()) {
433      ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor());
434      if (N) {
435        if (!BT_ResourceLeak)
436          BT_ResourceLeak = new BuiltinBug("Resource Leak",
437                          "Opened File never closed. Potential Resource leak.");
438        BugReport *R = new BugReport(*BT_ResourceLeak,
439                                     BT_ResourceLeak->getDescription(), N);
440        Eng.getBugReporter().EmitReport(R);
441      }
442    }
443  }
444}
445
446void StreamChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) {
447  const Expr *RetE = S->getRetValue();
448  if (!RetE)
449    return;
450
451  const GRState *state = C.getState();
452  SymbolRef Sym = state->getSVal(RetE).getAsSymbol();
453
454  if (!Sym)
455    return;
456
457  const StreamState *SS = state->get<StreamState>(Sym);
458  if(!SS)
459    return;
460
461  if (SS->isOpened())
462    state = state->set<StreamState>(Sym, StreamState::getEscaped(S));
463
464  C.addTransition(state);
465}
466