Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- StreamChecker.cpp -----------------------------------------*- C++ -*--//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// This file defines checkers that model and check stream handling functions.
10
//
11
//===----------------------------------------------------------------------===//
12
13
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
15
#include "clang/StaticAnalyzer/Core/Checker.h"
16
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
17
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
20
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
21
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.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
26
  StreamState(Kind k, const Stmt *s) : K(k), S(s) {}
33
34
19
  bool isOpened() const { return K == Opened; }
35
9
  bool isClosed() const { return K == Closed; }
36
  //bool isOpenFailed() const { return K == OpenFailed; }
37
  //bool isEscaped() const { return K == Escaped; }
38
39
0
  bool operator==(const StreamState &X) const {
40
0
    return K == X.K && S == X.S;
41
0
  }
42
43
9
  static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); }
44
8
  static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); }
45
9
  static StreamState getOpenFailed(const Stmt *s) {
46
9
    return StreamState(OpenFailed, s);
47
9
  }
48
0
  static StreamState getEscaped(const Stmt *s) {
49
0
    return StreamState(Escaped, s);
50
0
  }
51
52
26
  void Profile(llvm::FoldingSetNodeID &ID) const {
53
26
    ID.AddInteger(K);
54
26
    ID.AddPointer(S);
55
26
  }
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
5
      II_ferror(nullptr), II_fileno(nullptr) {}
74
75
  bool evalCall(const CallEvent &Call, 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
61
bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
108
61
  const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
109
61
  if (!FD || FD->getKind() != Decl::Function)
110
6
    return false;
111
55
112
55
  const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
113
55
  if (!CE)
114
0
    return false;
115
55
116
55
  ASTContext &Ctx = C.getASTContext();
117
55
  if (!II_fopen)
118
4
    II_fopen = &Ctx.Idents.get("fopen");
119
55
  if (!II_tmpfile)
120
4
    II_tmpfile = &Ctx.Idents.get("tmpfile");
121
55
  if (!II_fclose)
122
4
    II_fclose = &Ctx.Idents.get("fclose");
123
55
  if (!II_fread)
124
4
    II_fread = &Ctx.Idents.get("fread");
125
55
  if (!II_fwrite)
126
4
    II_fwrite = &Ctx.Idents.get("fwrite");
127
55
  if (!II_fseek)
128
4
    II_fseek = &Ctx.Idents.get("fseek");
129
55
  if (!II_ftell)
130
4
    II_ftell = &Ctx.Idents.get("ftell");
131
55
  if (!II_rewind)
132
4
    II_rewind = &Ctx.Idents.get("rewind");
133
55
  if (!II_fgetpos)
134
4
    II_fgetpos = &Ctx.Idents.get("fgetpos");
135
55
  if (!II_fsetpos)
136
4
    II_fsetpos = &Ctx.Idents.get("fsetpos");
137
55
  if (!II_clearerr)
138
4
    II_clearerr = &Ctx.Idents.get("clearerr");
139
55
  if (!II_feof)
140
4
    II_feof = &Ctx.Idents.get("feof");
141
55
  if (!II_ferror)
142
4
    II_ferror = &Ctx.Idents.get("ferror");
143
55
  if (!II_fileno)
144
4
    II_fileno = &Ctx.Idents.get("fileno");
145
55
146
55
  if (FD->getIdentifier() == II_fopen) {
147
8
    Fopen(C, CE);
148
8
    return true;
149
8
  }
150
47
  if (FD->getIdentifier() == II_tmpfile) {
151
1
    Tmpfile(C, CE);
152
1
    return true;
153
1
  }
154
46
  if (FD->getIdentifier() == II_fclose) {
155
13
    Fclose(C, CE);
156
13
    return true;
157
13
  }
158
33
  if (FD->getIdentifier() == II_fread) {
159
2
    Fread(C, CE);
160
2
    return true;
161
2
  }
162
31
  if (FD->getIdentifier() == II_fwrite) {
163
0
    Fwrite(C, CE);
164
0
    return true;
165
0
  }
166
31
  if (FD->getIdentifier() == II_fseek) {
167
5
    Fseek(C, CE);
168
5
    return true;
169
5
  }
170
26
  if (FD->getIdentifier() == II_ftell) {
171
4
    Ftell(C, CE);
172
4
    return true;
173
4
  }
174
22
  if (FD->getIdentifier() == II_rewind) {
175
2
    Rewind(C, CE);
176
2
    return true;
177
2
  }
178
20
  if (FD->getIdentifier() == II_fgetpos) {
179
0
    Fgetpos(C, CE);
180
0
    return true;
181
0
  }
182
20
  if (FD->getIdentifier() == II_fsetpos) {
183
0
    Fsetpos(C, CE);
184
0
    return true;
185
0
  }
186
20
  if (FD->getIdentifier() == II_clearerr) {
187
0
    Clearerr(C, CE);
188
0
    return true;
189
0
  }
190
20
  if (FD->getIdentifier() == II_feof) {
191
0
    Feof(C, CE);
192
0
    return true;
193
0
  }
194
20
  if (FD->getIdentifier() == II_ferror) {
195
0
    Ferror(C, CE);
196
0
    return true;
197
0
  }
198
20
  if (FD->getIdentifier() == II_fileno) {
199
0
    Fileno(C, CE);
200
0
    return true;
201
0
  }
202
20
203
20
  return false;
204
20
}
205
206
8
void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const {
207
8
  OpenFileAux(C, CE);
208
8
}
209
210
1
void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const {
211
1
  OpenFileAux(C, CE);
212
1
}
213
214
9
void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const {
215
9
  ProgramStateRef state = C.getState();
216
9
  SValBuilder &svalBuilder = C.getSValBuilder();
217
9
  const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
218
9
  DefinedSVal RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
219
9
                                                    C.blockCount())
220
9
      .castAs<DefinedSVal>();
221
9
  state = state->BindExpr(CE, C.getLocationContext(), RetVal);
222
9
223
9
  ConstraintManager &CM = C.getConstraintManager();
224
9
  // Bifurcate the state into two: one with a valid FILE* pointer, the other
225
9
  // with a NULL.
226
9
  ProgramStateRef stateNotNull, stateNull;
227
9
  std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
228
9
229
9
  if (SymbolRef Sym = RetVal.getAsSymbol()) {
230
9
    // if RetVal is not NULL, set the symbol's state to Opened.
231
9
    stateNotNull =
232
9
      stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE));
233
9
    stateNull =
234
9
      stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE));
235
9
236
9
    C.addTransition(stateNotNull);
237
9
    C.addTransition(stateNull);
238
9
  }
239
9
}
240
241
13
void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const {
242
13
  ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C);
243
13
  if (state)
244
12
    C.addTransition(state);
245
13
}
246
247
2
void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const {
248
2
  ProgramStateRef state = C.getState();
249
2
  if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C))
250
1
    return;
251
2
}
252
253
0
void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const {
254
0
  ProgramStateRef state = C.getState();
255
0
  if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C))
256
0
    return;
257
0
}
258
259
5
void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
260
5
  ProgramStateRef state = C.getState();
261
5
  if (!(state = CheckNullStream(C.getSVal(CE->getArg(0)), state, C)))
262
1
    return;
263
4
  // Check the legality of the 'whence' argument of 'fseek'.
264
4
  SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext());
265
4
  Optional<nonloc::ConcreteInt> CI = Whence.getAs<nonloc::ConcreteInt>();
266
4
267
4
  if (!CI)
268
1
    return;
269
3
270
3
  int64_t x = CI->getValue().getSExtValue();
271
3
  if (x >= 0 && x <= 2)
272
2
    return;
273
1
274
1
  if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
275
1
    if (!BT_illegalwhence)
276
1
      BT_illegalwhence.reset(
277
1
          new BuiltinBug(this, "Illegal whence argument",
278
1
                         "The whence argument to fseek() should be "
279
1
                         "SEEK_SET, SEEK_END, or SEEK_CUR."));
280
1
    C.emitReport(llvm::make_unique<BugReport>(
281
1
        *BT_illegalwhence, BT_illegalwhence->getDescription(), N));
282
1
  }
283
1
}
284
285
4
void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const {
286
4
  ProgramStateRef state = C.getState();
287
4
  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
288
2
    return;
289
4
}
290
291
2
void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const {
292
2
  ProgramStateRef state = C.getState();
293
2
  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
294
1
    return;
295
2
}
296
297
0
void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const {
298
0
  ProgramStateRef state = C.getState();
299
0
  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
300
0
    return;
301
0
}
302
303
0
void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const {
304
0
  ProgramStateRef state = C.getState();
305
0
  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
306
0
    return;
307
0
}
308
309
0
void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const {
310
0
  ProgramStateRef state = C.getState();
311
0
  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
312
0
    return;
313
0
}
314
315
0
void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const {
316
0
  ProgramStateRef state = C.getState();
317
0
  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
318
0
    return;
319
0
}
320
321
0
void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const {
322
0
  ProgramStateRef state = C.getState();
323
0
  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
324
0
    return;
325
0
}
326
327
0
void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const {
328
0
  ProgramStateRef state = C.getState();
329
0
  if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
330
0
    return;
331
0
}
332
333
ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state,
334
13
                                    CheckerContext &C) const {
335
13
  Optional<DefinedSVal> DV = SV.getAs<DefinedSVal>();
336
13
  if (!DV)
337
0
    return nullptr;
338
13
339
13
  ConstraintManager &CM = C.getConstraintManager();
340
13
  ProgramStateRef stateNotNull, stateNull;
341
13
  std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
342
13
343
13
  if (!stateNotNull && 
stateNull5
) {
344
5
    if (ExplodedNode *N = C.generateErrorNode(stateNull)) {
345
5
      if (!BT_nullfp)
346
1
        BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
347
1
                                       "Stream pointer might be NULL."));
348
5
      C.emitReport(llvm::make_unique<BugReport>(
349
5
          *BT_nullfp, BT_nullfp->getDescription(), N));
350
5
    }
351
5
    return nullptr;
352
5
  }
353
8
  return stateNotNull;
354
8
}
355
356
ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
357
                                               ProgramStateRef state,
358
13
                                               CheckerContext &C) const {
359
13
  SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
360
13
  if (!Sym)
361
3
    return state;
362
10
363
10
  const StreamState *SS = state->get<StreamMap>(Sym);
364
10
365
10
  // If the file stream is not tracked, return.
366
10
  if (!SS)
367
1
    return state;
368
9
369
9
  // Check: Double close a File Descriptor could cause undefined behaviour.
370
9
  // Conforming to man-pages
371
9
  if (SS->isClosed()) {
372
1
    ExplodedNode *N = C.generateErrorNode();
373
1
    if (N) {
374
1
      if (!BT_doubleclose)
375
1
        BT_doubleclose.reset(new BuiltinBug(
376
1
            this, "Double fclose", "Try to close a file Descriptor already"
377
1
                                   " closed. Cause undefined behaviour."));
378
1
      C.emitReport(llvm::make_unique<BugReport>(
379
1
          *BT_doubleclose, BT_doubleclose->getDescription(), N));
380
1
    }
381
1
    return nullptr;
382
1
  }
383
8
384
8
  // Close the File Descriptor.
385
8
  return state->set<StreamMap>(Sym, StreamState::getClosed(CE));
386
8
}
387
388
void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
389
263
                                     CheckerContext &C) const {
390
263
  ProgramStateRef state = C.getState();
391
263
392
263
  // TODO: Clean up the state.
393
263
  const StreamMapTy &Map = state->get<StreamMap>();
394
263
  for (const auto &I: Map) {
395
67
    SymbolRef Sym = I.first;
396
67
    const StreamState &SS = I.second;
397
67
    if (!SymReaper.isDead(Sym) || 
!SS.isOpened()19
)
398
66
      continue;
399
1
400
1
    ExplodedNode *N = C.generateErrorNode();
401
1
    if (!N)
402
0
      return;
403
1
404
1
    if (!BT_ResourceLeak)
405
1
      BT_ResourceLeak.reset(
406
1
          new BuiltinBug(this, "Resource Leak",
407
1
                         "Opened File never closed. Potential Resource leak."));
408
1
    C.emitReport(llvm::make_unique<BugReport>(
409
1
        *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N));
410
1
  }
411
263
}
412
413
5
void ento::registerStreamChecker(CheckerManager &mgr) {
414
5
  mgr.registerChecker<StreamChecker>();
415
5
}
416
417
5
bool ento::shouldRegisterStreamChecker(const LangOptions &LO) {
418
5
  return true;
419
5
}