Coverage Report

Created: 2022-01-22 13:19

/Users/buildslave/jenkins/workspace/coverage/llvm-project/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/CallDescription.h"
18
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
21
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
22
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
23
#include <functional>
24
25
using namespace clang;
26
using namespace ento;
27
using namespace std::placeholders;
28
29
//===----------------------------------------------------------------------===//
30
// Definition of state data structures.
31
//===----------------------------------------------------------------------===//
32
33
namespace {
34
35
struct FnDescription;
36
37
/// State of the stream error flags.
38
/// Sometimes it is not known to the checker what error flags are set.
39
/// This is indicated by setting more than one flag to true.
40
/// This is an optimization to avoid state splits.
41
/// A stream can either be in FEOF or FERROR but not both at the same time.
42
/// Multiple flags are set to handle the corresponding states together.
43
struct StreamErrorState {
44
  /// The stream can be in state where none of the error flags set.
45
  bool NoError = true;
46
  /// The stream can be in state where the EOF indicator is set.
47
  bool FEof = false;
48
  /// The stream can be in state where the error indicator is set.
49
  bool FError = false;
50
51
135
  bool isNoError() const { return NoError && !FEof && !FError; }
52
407
  bool isFEof() const { return !NoError && 
FEof129
&&
!FError63
; }
53
0
  bool isFError() const { return !NoError && !FEof && FError; }
54
55
144
  bool operator==(const StreamErrorState &ES) const {
56
144
    return NoError == ES.NoError && 
FEof == ES.FEof81
&&
FError == ES.FError79
;
57
144
  }
58
59
28
  bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); }
60
61
24
  StreamErrorState operator|(const StreamErrorState &E) const {
62
24
    return {NoError || 
E.NoError10
, FEof ||
E.FEof7
, FError || E.FError};
63
24
  }
64
65
158
  StreamErrorState operator&(const StreamErrorState &E) const {
66
158
    return {NoError && 
E.NoError84
, FEof &&
E.FEof50
, FError &&
E.FError58
};
67
158
  }
68
69
65
  StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; }
70
71
  /// Returns if the StreamErrorState is a valid object.
72
513
  operator bool() const { return NoError || 
FEof198
||
FError132
; }
73
74
0
  void Profile(llvm::FoldingSetNodeID &ID) const {
75
0
    ID.AddBoolean(NoError);
76
0
    ID.AddBoolean(FEof);
77
0
    ID.AddBoolean(FError);
78
0
  }
79
};
80
81
const StreamErrorState ErrorNone{true, false, false};
82
const StreamErrorState ErrorFEof{false, true, false};
83
const StreamErrorState ErrorFError{false, false, true};
84
85
/// Full state information about a stream pointer.
86
struct StreamState {
87
  /// The last file operation called in the stream.
88
  const FnDescription *LastOperation;
89
90
  /// State of a stream symbol.
91
  /// FIXME: We need maybe an "escaped" state later.
92
  enum KindTy {
93
    Opened, /// Stream is opened.
94
    Closed, /// Closed stream (an invalid stream pointer after it was closed).
95
    OpenFailed /// The last open operation has failed.
96
  } State;
97
98
  /// State of the error flags.
99
  /// Ignored in non-opened stream state but must be NoError.
100
  StreamErrorState const ErrorState;
101
102
  /// Indicate if the file has an "indeterminate file position indicator".
103
  /// This can be set at a failing read or write or seek operation.
104
  /// If it is set no more read or write is allowed.
105
  /// This value is not dependent on the stream error flags:
106
  /// The error flag may be cleared with `clearerr` but the file position
107
  /// remains still indeterminate.
108
  /// This value applies to all error states in ErrorState except FEOF.
109
  /// An EOF+indeterminate state is the same as EOF state.
110
  bool const FilePositionIndeterminate = false;
111
112
  StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES,
113
              bool IsFilePositionIndeterminate)
114
      : LastOperation(L), State(S), ErrorState(ES),
115
344
        FilePositionIndeterminate(IsFilePositionIndeterminate) {
116
344
    assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
117
344
           "FilePositionIndeterminate should be false in FEof case.");
118
0
    assert((State == Opened || ErrorState.isNoError()) &&
119
344
           "ErrorState should be None in non-opened stream state.");
120
344
  }
121
122
332
  bool isOpened() const { return State == Opened; }
123
220
  bool isClosed() const { return State == Closed; }
124
213
  bool isOpenFailed() const { return State == OpenFailed; }
125
126
102
  bool operator==(const StreamState &X) const {
127
    // In not opened state error state should always NoError, so comparison
128
    // here is no problem.
129
102
    return LastOperation == X.LastOperation && State == X.State &&
130
102
           ErrorState == X.ErrorState &&
131
102
           
FilePositionIndeterminate == X.FilePositionIndeterminate66
;
132
102
  }
133
134
  static StreamState getOpened(const FnDescription *L,
135
                               const StreamErrorState &ES = ErrorNone,
136
209
                               bool IsFilePositionIndeterminate = false) {
137
209
    return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
138
209
  }
139
76
  static StreamState getClosed(const FnDescription *L) {
140
76
    return StreamState{L, Closed, {}, false};
141
76
  }
142
59
  static StreamState getOpenFailed(const FnDescription *L) {
143
59
    return StreamState{L, OpenFailed, {}, false};
144
59
  }
145
146
355
  void Profile(llvm::FoldingSetNodeID &ID) const {
147
355
    ID.AddPointer(LastOperation);
148
355
    ID.AddInteger(State);
149
355
    ID.AddInteger(ErrorState);
150
355
    ID.AddBoolean(FilePositionIndeterminate);
151
355
  }
152
};
153
154
} // namespace
155
156
//===----------------------------------------------------------------------===//
157
// StreamChecker class and utility functions.
158
//===----------------------------------------------------------------------===//
159
160
namespace {
161
162
class StreamChecker;
163
using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
164
                                   const CallEvent &, CheckerContext &)>;
165
166
using ArgNoTy = unsigned int;
167
static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
168
169
struct FnDescription {
170
  FnCheck PreFn;
171
  FnCheck EvalFn;
172
  ArgNoTy StreamArgNo;
173
};
174
175
/// Get the value of the stream argument out of the passed call event.
176
/// The call should contain a function that is described by Desc.
177
458
SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {
178
458
  assert(Desc && Desc->StreamArgNo != ArgNone &&
179
458
         "Try to get a non-existing stream argument.");
180
0
  return Call.getArgSVal(Desc->StreamArgNo);
181
458
}
182
183
/// Create a conjured symbol return value for a call expression.
184
112
DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) {
185
112
  assert(CE && "Expecting a call expression.");
186
187
0
  const LocationContext *LCtx = C.getLocationContext();
188
112
  return C.getSValBuilder()
189
112
      .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
190
112
      .castAs<DefinedSVal>();
191
112
}
192
193
ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
194
26
                                  const CallExpr *CE) {
195
26
  DefinedSVal RetVal = makeRetVal(C, CE);
196
26
  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
197
26
  State = State->assume(RetVal, true);
198
26
  assert(State && "Assumption on new value should not fail.");
199
0
  return State;
200
26
}
201
202
ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
203
67
                        CheckerContext &C, const CallExpr *CE) {
204
67
  State = State->BindExpr(CE, C.getLocationContext(),
205
67
                          C.getSValBuilder().makeIntVal(Value, false));
206
67
  return State;
207
67
}
208
209
class StreamChecker : public Checker<check::PreCall, eval::Call,
210
                                     check::DeadSymbols, check::PointerEscape> {
211
  BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
212
  BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
213
  BugType BT_UseAfterOpenFailed{this, "Invalid stream",
214
                                "Stream handling error"};
215
  BugType BT_IndeterminatePosition{this, "Invalid stream state",
216
                                   "Stream handling error"};
217
  BugType BT_IllegalWhence{this, "Illegal whence argument",
218
                           "Stream handling error"};
219
  BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};
220
  BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error",
221
                          /*SuppressOnSink =*/true};
222
223
public:
224
  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
225
  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
226
  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
227
  ProgramStateRef checkPointerEscape(ProgramStateRef State,
228
                                     const InvalidatedSymbols &Escaped,
229
                                     const CallEvent *Call,
230
                                     PointerEscapeKind Kind) const;
231
232
  /// If true, evaluate special testing stream functions.
233
  bool TestMode = false;
234
235
4
  const BugType *getBT_StreamEof() const { return &BT_StreamEof; }
236
237
private:
238
  CallDescriptionMap<FnDescription> FnDescriptions = {
239
      {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
240
      {{"freopen", 3},
241
       {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
242
      {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
243
      {{"fclose", 1},
244
       {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
245
      {{"fread", 4},
246
       {&StreamChecker::preFread,
247
        std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
248
      {{"fwrite", 4},
249
       {&StreamChecker::preFwrite,
250
        std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
251
      {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
252
      {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}},
253
      {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}},
254
      {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}},
255
      {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}},
256
      {{"clearerr", 1},
257
       {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
258
      {{"feof", 1},
259
       {&StreamChecker::preDefault,
260
        std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
261
        0}},
262
      {{"ferror", 1},
263
       {&StreamChecker::preDefault,
264
        std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
265
        0}},
266
      {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}},
267
  };
268
269
  CallDescriptionMap<FnDescription> FnTestDescriptions = {
270
      {{"StreamTesterChecker_make_feof_stream", 1},
271
       {nullptr,
272
        std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof),
273
        0}},
274
      {{"StreamTesterChecker_make_ferror_stream", 1},
275
       {nullptr,
276
        std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
277
                  ErrorFError),
278
        0}},
279
  };
280
281
  void evalFopen(const FnDescription *Desc, const CallEvent &Call,
282
                 CheckerContext &C) const;
283
284
  void preFreopen(const FnDescription *Desc, const CallEvent &Call,
285
                  CheckerContext &C) const;
286
  void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
287
                   CheckerContext &C) const;
288
289
  void evalFclose(const FnDescription *Desc, const CallEvent &Call,
290
                  CheckerContext &C) const;
291
292
  void preFread(const FnDescription *Desc, const CallEvent &Call,
293
                CheckerContext &C) const;
294
295
  void preFwrite(const FnDescription *Desc, const CallEvent &Call,
296
                 CheckerContext &C) const;
297
298
  void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
299
                       CheckerContext &C, bool IsFread) const;
300
301
  void preFseek(const FnDescription *Desc, const CallEvent &Call,
302
                CheckerContext &C) const;
303
  void evalFseek(const FnDescription *Desc, const CallEvent &Call,
304
                 CheckerContext &C) const;
305
306
  void preDefault(const FnDescription *Desc, const CallEvent &Call,
307
                  CheckerContext &C) const;
308
309
  void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
310
                    CheckerContext &C) const;
311
312
  void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
313
                      CheckerContext &C,
314
                      const StreamErrorState &ErrorKind) const;
315
316
  void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
317
                         CheckerContext &C,
318
                         const StreamErrorState &ErrorKind) const;
319
320
  /// Check that the stream (in StreamVal) is not NULL.
321
  /// If it can only be NULL a fatal error is emitted and nullptr returned.
322
  /// Otherwise the return value is a new state where the stream is constrained
323
  /// to be non-null.
324
  ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
325
                                      CheckerContext &C,
326
                                      ProgramStateRef State) const;
327
328
  /// Check that the stream is the opened state.
329
  /// If the stream is known to be not opened an error is generated
330
  /// and nullptr returned, otherwise the original state is returned.
331
  ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,
332
                                     ProgramStateRef State) const;
333
334
  /// Check that the stream has not an invalid ("indeterminate") file position,
335
  /// generate warning for it.
336
  /// (EOF is not an invalid position.)
337
  /// The returned state can be nullptr if a fatal error was generated.
338
  /// It can return non-null state if the stream has not an invalid position or
339
  /// there is execution path with non-invalid position.
340
  ProgramStateRef
341
  ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,
342
                                    ProgramStateRef State) const;
343
344
  /// Check the legality of the 'whence' argument of 'fseek'.
345
  /// Generate error and return nullptr if it is found to be illegal.
346
  /// Otherwise returns the state.
347
  /// (State is not changed here because the "whence" value is already known.)
348
  ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
349
                                           ProgramStateRef State) const;
350
351
  /// Generate warning about stream in EOF state.
352
  /// There will be always a state transition into the passed State,
353
  /// by the new non-fatal error node or (if failed) a normal transition,
354
  /// to ensure uniform handling.
355
  void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
356
                         ProgramStateRef State) const;
357
358
  /// Emit resource leak warnings for the given symbols.
359
  /// Createn a non-fatal error node for these, and returns it (if any warnings
360
  /// were generated). Return value is non-null.
361
  ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
362
                            CheckerContext &C, ExplodedNode *Pred) const;
363
364
  /// Find the description data of the function called by a call event.
365
  /// Returns nullptr if no function is recognized.
366
762
  const FnDescription *lookupFn(const CallEvent &Call) const {
367
    // Recognize "global C functions" with only integral or pointer arguments
368
    // (and matching name) as stream functions.
369
762
    if (!Call.isGlobalCFunction())
370
16
      return nullptr;
371
1.16k
    
for (auto P : Call.parameters())746
{
372
1.16k
      QualType T = P->getType();
373
1.16k
      if (!T->isIntegralOrEnumerationType() && 
!T->isPointerType()783
)
374
2
        return nullptr;
375
1.16k
    }
376
377
744
    return FnDescriptions.lookup(Call);
378
746
  }
379
380
  /// Generate a message for BugReporterVisitor if the stored symbol is
381
  /// marked as interesting by the actual bug report.
382
  // FIXME: Use lambda instead.
383
  struct NoteFn {
384
    const BugType *BT_ResourceLeak;
385
    SymbolRef StreamSym;
386
    std::string Message;
387
388
82
    std::string operator()(PathSensitiveBugReport &BR) const {
389
82
      if (BR.isInteresting(StreamSym) && 
&BR.getBugType() == BT_ResourceLeak12
)
390
10
        return Message;
391
392
72
      return "";
393
82
    }
394
  };
395
396
  const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym,
397
59
                                  const std::string &Message) const {
398
59
    return C.getNoteTag(NoteFn{&BT_ResourceLeak, StreamSym, Message});
399
59
  }
400
401
  const NoteTag *constructSetEofNoteTag(CheckerContext &C,
402
17
                                        SymbolRef StreamSym) const {
403
23
    return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
404
23
      if (!BR.isInteresting(StreamSym) ||
405
23
          
&BR.getBugType() != this->getBT_StreamEof()4
)
406
19
        return "";
407
408
4
      BR.markNotInteresting(StreamSym);
409
410
4
      return "Assuming stream reaches end-of-file here";
411
23
    });
412
17
  }
413
414
  /// Searches for the ExplodedNode where the file descriptor was acquired for
415
  /// StreamSym.
416
  static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
417
                                                SymbolRef StreamSym,
418
                                                CheckerContext &C);
419
};
420
421
} // end anonymous namespace
422
423
// This map holds the state of a stream.
424
// The stream is identified with a SymbolRef that is created when a stream
425
// opening function is modeled by the checker.
426
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
427
428
187
inline void assertStreamStateOpened(const StreamState *SS) {
429
187
  assert(SS->isOpened() &&
430
187
         "Previous create of error node for non-opened stream failed?");
431
187
}
432
433
const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
434
                                                      SymbolRef StreamSym,
435
11
                                                      CheckerContext &C) {
436
11
  ProgramStateRef State = N->getState();
437
  // When bug type is resource leak, exploded node N may not have state info
438
  // for leaked file descriptor, but predecessor should have it.
439
11
  if (!State->get<StreamMap>(StreamSym))
440
0
    N = N->getFirstPred();
441
442
11
  const ExplodedNode *Pred = N;
443
327
  while (N) {
444
327
    State = N->getState();
445
327
    if (!State->get<StreamMap>(StreamSym))
446
11
      return Pred;
447
316
    Pred = N;
448
316
    N = N->getFirstPred();
449
316
  }
450
451
0
  return nullptr;
452
11
}
453
454
//===----------------------------------------------------------------------===//
455
// Methods of StreamChecker.
456
//===----------------------------------------------------------------------===//
457
458
void StreamChecker::checkPreCall(const CallEvent &Call,
459
398
                                 CheckerContext &C) const {
460
398
  const FnDescription *Desc = lookupFn(Call);
461
398
  if (!Desc || 
!Desc->PreFn302
)
462
150
    return;
463
464
248
  Desc->PreFn(this, Desc, Call, C);
465
248
}
466
467
364
bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
468
364
  const FnDescription *Desc = lookupFn(Call);
469
364
  if (!Desc && 
TestMode94
)
470
51
    Desc = FnTestDescriptions.lookup(Call);
471
364
  if (!Desc || 
!Desc->EvalFn274
)
472
100
    return false;
473
474
264
  Desc->EvalFn(this, Desc, Call, C);
475
476
264
  return C.isDifferent();
477
364
}
478
479
void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
480
54
                              CheckerContext &C) const {
481
54
  ProgramStateRef State = C.getState();
482
54
  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
483
54
  if (!CE)
484
0
    return;
485
486
54
  DefinedSVal RetVal = makeRetVal(C, CE);
487
54
  SymbolRef RetSym = RetVal.getAsSymbol();
488
54
  assert(RetSym && "RetVal must be a symbol here.");
489
490
0
  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
491
492
  // Bifurcate the state into two: one with a valid FILE* pointer, the other
493
  // with a NULL.
494
54
  ProgramStateRef StateNotNull, StateNull;
495
54
  std::tie(StateNotNull, StateNull) =
496
54
      C.getConstraintManager().assumeDual(State, RetVal);
497
498
54
  StateNotNull =
499
54
      StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
500
54
  StateNull =
501
54
      StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
502
503
54
  C.addTransition(StateNotNull,
504
54
                  constructNoteTag(C, RetSym, "Stream opened here"));
505
54
  C.addTransition(StateNull);
506
54
}
507
508
void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
509
7
                               CheckerContext &C) const {
510
  // Do not allow NULL as passed stream pointer but allow a closed stream.
511
7
  ProgramStateRef State = C.getState();
512
7
  State = ensureStreamNonNull(getStreamArg(Desc, Call),
513
7
                              Call.getArgExpr(Desc->StreamArgNo), C, State);
514
7
  if (!State)
515
1
    return;
516
517
6
  C.addTransition(State);
518
6
}
519
520
void StreamChecker::evalFreopen(const FnDescription *Desc,
521
                                const CallEvent &Call,
522
6
                                CheckerContext &C) const {
523
6
  ProgramStateRef State = C.getState();
524
525
6
  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
526
6
  if (!CE)
527
0
    return;
528
529
6
  Optional<DefinedSVal> StreamVal =
530
6
      getStreamArg(Desc, Call).getAs<DefinedSVal>();
531
6
  if (!StreamVal)
532
0
    return;
533
534
6
  SymbolRef StreamSym = StreamVal->getAsSymbol();
535
  // Do not care about concrete values for stream ("(FILE *)0x12345"?).
536
  // FIXME: Can be stdin, stdout, stderr such values?
537
6
  if (!StreamSym)
538
0
    return;
539
540
  // Do not handle untracked stream. It is probably escaped.
541
6
  if (!State->get<StreamMap>(StreamSym))
542
1
    return;
543
544
  // Generate state for non-failed case.
545
  // Return value is the passed stream pointer.
546
  // According to the documentations, the stream is closed first
547
  // but any close error is ignored. The state changes to (or remains) opened.
548
5
  ProgramStateRef StateRetNotNull =
549
5
      State->BindExpr(CE, C.getLocationContext(), *StreamVal);
550
  // Generate state for NULL return value.
551
  // Stream switches to OpenFailed state.
552
5
  ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(),
553
5
                                                 C.getSValBuilder().makeNull());
554
555
5
  StateRetNotNull =
556
5
      StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
557
5
  StateRetNull =
558
5
      StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
559
560
5
  C.addTransition(StateRetNotNull,
561
5
                  constructNoteTag(C, StreamSym, "Stream reopened here"));
562
5
  C.addTransition(StateRetNull);
563
5
}
564
565
void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
566
77
                               CheckerContext &C) const {
567
77
  ProgramStateRef State = C.getState();
568
77
  SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
569
77
  if (!Sym)
570
0
    return;
571
572
77
  const StreamState *SS = State->get<StreamMap>(Sym);
573
77
  if (!SS)
574
1
    return;
575
576
76
  assertStreamStateOpened(SS);
577
578
  // Close the File Descriptor.
579
  // Regardless if the close fails or not, stream becomes "closed"
580
  // and can not be used any more.
581
76
  State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
582
583
76
  C.addTransition(State);
584
76
}
585
586
void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call,
587
26
                             CheckerContext &C) const {
588
26
  ProgramStateRef State = C.getState();
589
26
  SVal StreamVal = getStreamArg(Desc, Call);
590
26
  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
591
26
                              State);
592
26
  if (!State)
593
2
    return;
594
24
  State = ensureStreamOpened(StreamVal, C, State);
595
24
  if (!State)
596
2
    return;
597
22
  State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
598
22
  if (!State)
599
1
    return;
600
601
21
  SymbolRef Sym = StreamVal.getAsSymbol();
602
21
  if (Sym && State->get<StreamMap>(Sym)) {
603
21
    const StreamState *SS = State->get<StreamMap>(Sym);
604
21
    if (SS->ErrorState & ErrorFEof)
605
6
      reportFEofWarning(Sym, C, State);
606
21
  } else {
607
0
    C.addTransition(State);
608
0
  }
609
21
}
610
611
void StreamChecker::preFwrite(const FnDescription *Desc, const CallEvent &Call,
612
30
                              CheckerContext &C) const {
613
30
  ProgramStateRef State = C.getState();
614
30
  SVal StreamVal = getStreamArg(Desc, Call);
615
30
  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
616
30
                              State);
617
30
  if (!State)
618
1
    return;
619
29
  State = ensureStreamOpened(StreamVal, C, State);
620
29
  if (!State)
621
1
    return;
622
28
  State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
623
28
  if (!State)
624
6
    return;
625
626
22
  C.addTransition(State);
627
22
}
628
629
void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
630
                                    const CallEvent &Call, CheckerContext &C,
631
43
                                    bool IsFread) const {
632
43
  ProgramStateRef State = C.getState();
633
43
  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
634
43
  if (!StreamSym)
635
0
    return;
636
637
43
  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
638
43
  if (!CE)
639
0
    return;
640
641
43
  Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
642
43
  if (!SizeVal)
643
0
    return;
644
43
  Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
645
43
  if (!NMembVal)
646
0
    return;
647
648
43
  const StreamState *OldSS = State->get<StreamMap>(StreamSym);
649
43
  if (!OldSS)
650
4
    return;
651
652
39
  assertStreamStateOpened(OldSS);
653
654
  // C'99 standard, ยง7.19.8.1.3, the return value of fread:
655
  // The fread function returns the number of elements successfully read, which
656
  // may be less than nmemb if a read error or end-of-file is encountered. If
657
  // size or nmemb is zero, fread returns zero and the contents of the array and
658
  // the state of the stream remain unchanged.
659
660
39
  if (State->isNull(*SizeVal).isConstrainedTrue() ||
661
39
      
State->isNull(*NMembVal).isConstrainedTrue()31
) {
662
    // This is the "size or nmemb is zero" case.
663
    // Just return 0, do nothing more (not clear the error flags).
664
14
    State = bindInt(0, State, C, CE);
665
14
    C.addTransition(State);
666
14
    return;
667
14
  }
668
669
  // Generate a transition for the success state.
670
  // If we know the state to be FEOF at fread, do not add a success state.
671
25
  if (!IsFread || 
(OldSS->ErrorState != ErrorFEof)14
) {
672
21
    ProgramStateRef StateNotFailed =
673
21
        State->BindExpr(CE, C.getLocationContext(), *NMembVal);
674
21
    if (StateNotFailed) {
675
21
      StateNotFailed = StateNotFailed->set<StreamMap>(
676
21
          StreamSym, StreamState::getOpened(Desc));
677
21
      C.addTransition(StateNotFailed);
678
21
    }
679
21
  }
680
681
  // Add transition for the failed state.
682
25
  Optional<NonLoc> RetVal = makeRetVal(C, CE).castAs<NonLoc>();
683
25
  assert(RetVal && "Value should be NonLoc.");
684
0
  ProgramStateRef StateFailed =
685
25
      State->BindExpr(CE, C.getLocationContext(), *RetVal);
686
25
  if (!StateFailed)
687
0
    return;
688
25
  auto Cond = C.getSValBuilder()
689
25
                  .evalBinOpNN(State, BO_LT, *RetVal, *NMembVal,
690
25
                               C.getASTContext().IntTy)
691
25
                  .getAs<DefinedOrUnknownSVal>();
692
25
  if (!Cond)
693
0
    return;
694
25
  StateFailed = StateFailed->assume(*Cond, true);
695
25
  if (!StateFailed)
696
0
    return;
697
698
25
  StreamErrorState NewES;
699
25
  if (IsFread)
700
14
    NewES =
701
14
        (OldSS->ErrorState == ErrorFEof) ? 
ErrorFEof4
:
ErrorFEof | ErrorFError10
;
702
11
  else
703
11
    NewES = ErrorFError;
704
  // If a (non-EOF) error occurs, the resulting value of the file position
705
  // indicator for the stream is indeterminate.
706
25
  StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
707
25
  StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
708
25
  if (IsFread && 
OldSS->ErrorState != ErrorFEof14
)
709
10
    C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
710
15
  else
711
15
    C.addTransition(StateFailed);
712
25
}
713
714
void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
715
9
                             CheckerContext &C) const {
716
9
  ProgramStateRef State = C.getState();
717
9
  SVal StreamVal = getStreamArg(Desc, Call);
718
9
  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
719
9
                              State);
720
9
  if (!State)
721
1
    return;
722
8
  State = ensureStreamOpened(StreamVal, C, State);
723
8
  if (!State)
724
0
    return;
725
8
  State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
726
8
  if (!State)
727
2
    return;
728
729
6
  C.addTransition(State);
730
6
}
731
732
void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
733
8
                              CheckerContext &C) const {
734
8
  ProgramStateRef State = C.getState();
735
8
  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
736
8
  if (!StreamSym)
737
0
    return;
738
739
8
  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
740
8
  if (!CE)
741
0
    return;
742
743
  // Ignore the call if the stream is not tracked.
744
8
  if (!State->get<StreamMap>(StreamSym))
745
1
    return;
746
747
7
  DefinedSVal RetVal = makeRetVal(C, CE);
748
749
  // Make expression result.
750
7
  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
751
752
  // Bifurcate the state into failed and non-failed.
753
  // Return zero on success, nonzero on error.
754
7
  ProgramStateRef StateNotFailed, StateFailed;
755
7
  std::tie(StateFailed, StateNotFailed) =
756
7
      C.getConstraintManager().assumeDual(State, RetVal);
757
758
  // Reset the state to opened with no error.
759
7
  StateNotFailed =
760
7
      StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
761
  // We get error.
762
  // It is possible that fseek fails but sets none of the error flags.
763
  // If fseek failed, assume that the file position becomes indeterminate in any
764
  // case.
765
7
  StateFailed = StateFailed->set<StreamMap>(
766
7
      StreamSym,
767
7
      StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true));
768
769
7
  C.addTransition(StateNotFailed);
770
7
  C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
771
7
}
772
773
void StreamChecker::evalClearerr(const FnDescription *Desc,
774
                                 const CallEvent &Call,
775
7
                                 CheckerContext &C) const {
776
7
  ProgramStateRef State = C.getState();
777
7
  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
778
7
  if (!StreamSym)
779
0
    return;
780
781
7
  const StreamState *SS = State->get<StreamMap>(StreamSym);
782
7
  if (!SS)
783
0
    return;
784
785
7
  assertStreamStateOpened(SS);
786
787
  // FilePositionIndeterminate is not cleared.
788
7
  State = State->set<StreamMap>(
789
7
      StreamSym,
790
7
      StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate));
791
7
  C.addTransition(State);
792
7
}
793
794
void StreamChecker::evalFeofFerror(const FnDescription *Desc,
795
                                   const CallEvent &Call, CheckerContext &C,
796
65
                                   const StreamErrorState &ErrorKind) const {
797
65
  ProgramStateRef State = C.getState();
798
65
  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
799
65
  if (!StreamSym)
800
0
    return;
801
802
65
  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
803
65
  if (!CE)
804
0
    return;
805
806
65
  const StreamState *SS = State->get<StreamMap>(StreamSym);
807
65
  if (!SS)
808
0
    return;
809
810
65
  assertStreamStateOpened(SS);
811
812
65
  if (SS->ErrorState & ErrorKind) {
813
    // Execution path with error of ErrorKind.
814
    // Function returns true.
815
    // From now on it is the only one error state.
816
26
    ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE);
817
26
    C.addTransition(TrueState->set<StreamMap>(
818
26
        StreamSym, StreamState::getOpened(Desc, ErrorKind,
819
26
                                          SS->FilePositionIndeterminate &&
820
26
                                              
!ErrorKind.isFEof()19
)));
821
26
  }
822
65
  if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) {
823
    // Execution path(s) with ErrorKind not set.
824
    // Function returns false.
825
    // New error state is everything before minus ErrorKind.
826
53
    ProgramStateRef FalseState = bindInt(0, State, C, CE);
827
53
    C.addTransition(FalseState->set<StreamMap>(
828
53
        StreamSym,
829
53
        StreamState::getOpened(
830
53
            Desc, NewES, SS->FilePositionIndeterminate && 
!NewES.isFEof()19
)));
831
53
  }
832
65
}
833
834
void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
835
176
                               CheckerContext &C) const {
836
176
  ProgramStateRef State = C.getState();
837
176
  SVal StreamVal = getStreamArg(Desc, Call);
838
176
  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
839
176
                              State);
840
176
  if (!State)
841
11
    return;
842
165
  State = ensureStreamOpened(StreamVal, C, State);
843
165
  if (!State)
844
6
    return;
845
846
159
  C.addTransition(State);
847
159
}
848
849
void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
850
                                      const CallEvent &Call, CheckerContext &C,
851
4
                                      const StreamErrorState &ErrorKind) const {
852
4
  ProgramStateRef State = C.getState();
853
4
  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
854
4
  assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
855
0
  const StreamState *SS = State->get<StreamMap>(StreamSym);
856
4
  assert(SS && "Stream should be tracked by the checker.");
857
0
  State = State->set<StreamMap>(
858
4
      StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind));
859
4
  C.addTransition(State);
860
4
}
861
862
ProgramStateRef
863
StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
864
                                   CheckerContext &C,
865
248
                                   ProgramStateRef State) const {
866
248
  auto Stream = StreamVal.getAs<DefinedSVal>();
867
248
  if (!Stream)
868
0
    return State;
869
870
248
  ConstraintManager &CM = C.getConstraintManager();
871
872
248
  ProgramStateRef StateNotNull, StateNull;
873
248
  std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream);
874
875
248
  if (!StateNotNull && 
StateNull16
) {
876
16
    if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
877
16
      auto R = std::make_unique<PathSensitiveBugReport>(
878
16
          BT_FileNull, "Stream pointer might be NULL.", N);
879
16
      if (StreamE)
880
16
        bugreporter::trackExpressionValue(N, StreamE, *R);
881
16
      C.emitReport(std::move(R));
882
16
    }
883
16
    return nullptr;
884
16
  }
885
886
232
  return StateNotNull;
887
248
}
888
889
ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
890
                                                  CheckerContext &C,
891
226
                                                  ProgramStateRef State) const {
892
226
  SymbolRef Sym = StreamVal.getAsSymbol();
893
226
  if (!Sym)
894
0
    return State;
895
896
226
  const StreamState *SS = State->get<StreamMap>(Sym);
897
226
  if (!SS)
898
6
    return State;
899
900
220
  if (SS->isClosed()) {
901
    // Using a stream pointer after 'fclose' causes undefined behavior
902
    // according to cppreference.com .
903
7
    ExplodedNode *N = C.generateErrorNode();
904
7
    if (N) {
905
7
      C.emitReport(std::make_unique<PathSensitiveBugReport>(
906
7
          BT_UseAfterClose,
907
7
          "Stream might be already closed. Causes undefined behaviour.", N));
908
7
      return nullptr;
909
7
    }
910
911
0
    return State;
912
7
  }
913
914
213
  if (SS->isOpenFailed()) {
915
    // Using a stream that has failed to open is likely to cause problems.
916
    // This should usually not occur because stream pointer is NULL.
917
    // But freopen can cause a state when stream pointer remains non-null but
918
    // failed to open.
919
2
    ExplodedNode *N = C.generateErrorNode();
920
2
    if (N) {
921
2
      C.emitReport(std::make_unique<PathSensitiveBugReport>(
922
2
          BT_UseAfterOpenFailed,
923
2
          "Stream might be invalid after "
924
2
          "(re-)opening it has failed. "
925
2
          "Can cause undefined behaviour.",
926
2
          N));
927
2
      return nullptr;
928
2
    }
929
0
    return State;
930
2
  }
931
932
211
  return State;
933
213
}
934
935
ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
936
50
    SVal StreamVal, CheckerContext &C, ProgramStateRef State) const {
937
50
  static const char *BugMessage =
938
50
      "File position of the stream might be 'indeterminate' "
939
50
      "after a failed operation. "
940
50
      "Can cause undefined behavior.";
941
942
50
  SymbolRef Sym = StreamVal.getAsSymbol();
943
50
  if (!Sym)
944
0
    return State;
945
946
50
  const StreamState *SS = State->get<StreamMap>(Sym);
947
50
  if (!SS)
948
4
    return State;
949
950
46
  assert(SS->isOpened() && "First ensure that stream is opened.");
951
952
46
  if (SS->FilePositionIndeterminate) {
953
7
    if (SS->ErrorState & ErrorFEof) {
954
      // The error is unknown but may be FEOF.
955
      // Continue analysis with the FEOF error state.
956
      // Report warning because the other possible error states.
957
0
      ExplodedNode *N = C.generateNonFatalErrorNode(State);
958
0
      if (!N)
959
0
        return nullptr;
960
961
0
      C.emitReport(std::make_unique<PathSensitiveBugReport>(
962
0
          BT_IndeterminatePosition, BugMessage, N));
963
0
      return State->set<StreamMap>(
964
0
          Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));
965
0
    }
966
967
    // Known or unknown error state without FEOF possible.
968
    // Stop analysis, report error.
969
7
    ExplodedNode *N = C.generateErrorNode(State);
970
7
    if (N)
971
7
      C.emitReport(std::make_unique<PathSensitiveBugReport>(
972
7
          BT_IndeterminatePosition, BugMessage, N));
973
974
7
    return nullptr;
975
7
  }
976
977
39
  return State;
978
46
}
979
980
ProgramStateRef
981
StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
982
8
                                        ProgramStateRef State) const {
983
8
  Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>();
984
8
  if (!CI)
985
1
    return State;
986
987
7
  int64_t X = CI->getValue().getSExtValue();
988
7
  if (X >= 0 && X <= 2)
989
5
    return State;
990
991
2
  if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
992
2
    C.emitReport(std::make_unique<PathSensitiveBugReport>(
993
2
        BT_IllegalWhence,
994
2
        "The whence argument to fseek() should be "
995
2
        "SEEK_SET, SEEK_END, or SEEK_CUR.",
996
2
        N));
997
2
    return nullptr;
998
2
  }
999
1000
0
  return State;
1001
2
}
1002
1003
void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
1004
6
                                      ProgramStateRef State) const {
1005
6
  if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1006
6
    auto R = std::make_unique<PathSensitiveBugReport>(
1007
6
        BT_StreamEof,
1008
6
        "Read function called when stream is in EOF state. "
1009
6
        "Function has no effect.",
1010
6
        N);
1011
6
    R->markInteresting(StreamSym);
1012
6
    C.emitReport(std::move(R));
1013
6
    return;
1014
6
  }
1015
0
  C.addTransition(State);
1016
0
}
1017
1018
ExplodedNode *
1019
StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
1020
10
                           CheckerContext &C, ExplodedNode *Pred) const {
1021
10
  ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);
1022
10
  if (!Err)
1023
0
    return Pred;
1024
1025
11
  
for (SymbolRef LeakSym : LeakedSyms)10
{
1026
    // Resource leaks can result in multiple warning that describe the same kind
1027
    // of programming error:
1028
    //  void f() {
1029
    //    FILE *F = fopen("a.txt");
1030
    //    if (rand()) // state split
1031
    //      return; // warning
1032
    //  } // warning
1033
    // While this isn't necessarily true (leaking the same stream could result
1034
    // from a different kinds of errors), the reduction in redundant reports
1035
    // makes this a worthwhile heuristic.
1036
    // FIXME: Add a checker option to turn this uniqueing feature off.
1037
11
    const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
1038
11
    assert(StreamOpenNode && "Could not find place of stream opening.");
1039
0
    PathDiagnosticLocation LocUsedForUniqueing =
1040
11
        PathDiagnosticLocation::createBegin(
1041
11
            StreamOpenNode->getStmtForDiagnostics(), C.getSourceManager(),
1042
11
            StreamOpenNode->getLocationContext());
1043
1044
11
    std::unique_ptr<PathSensitiveBugReport> R =
1045
11
        std::make_unique<PathSensitiveBugReport>(
1046
11
            BT_ResourceLeak,
1047
11
            "Opened stream never closed. Potential resource leak.", Err,
1048
11
            LocUsedForUniqueing,
1049
11
            StreamOpenNode->getLocationContext()->getDecl());
1050
11
    R->markInteresting(LeakSym);
1051
11
    C.emitReport(std::move(R));
1052
11
  }
1053
1054
10
  return Err;
1055
10
}
1056
1057
void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1058
974
                                     CheckerContext &C) const {
1059
974
  ProgramStateRef State = C.getState();
1060
1061
974
  llvm::SmallVector<SymbolRef, 2> LeakedSyms;
1062
1063
974
  const StreamMapTy &Map = State->get<StreamMap>();
1064
974
  for (const auto &I : Map) {
1065
715
    SymbolRef Sym = I.first;
1066
715
    const StreamState &SS = I.second;
1067
715
    if (!SymReaper.isDead(Sym))
1068
616
      continue;
1069
99
    if (SS.isOpened())
1070
11
      LeakedSyms.push_back(Sym);
1071
99
    State = State->remove<StreamMap>(Sym);
1072
99
  }
1073
1074
974
  ExplodedNode *N = C.getPredecessor();
1075
974
  if (!LeakedSyms.empty())
1076
10
    N = reportLeaks(LeakedSyms, C, N);
1077
1078
974
  C.addTransition(State, N);
1079
974
}
1080
1081
ProgramStateRef StreamChecker::checkPointerEscape(
1082
    ProgramStateRef State, const InvalidatedSymbols &Escaped,
1083
303
    const CallEvent *Call, PointerEscapeKind Kind) const {
1084
  // Check for file-handling system call that is not handled by the checker.
1085
  // FIXME: The checker should be updated to handle all system calls that take
1086
  // 'FILE*' argument. These are now ignored.
1087
303
  if (Kind == PSK_DirectEscapeOnCall && 
Call->isInSystemHeader()32
)
1088
19
    return State;
1089
1090
284
  for (SymbolRef Sym : Escaped) {
1091
    // The symbol escaped.
1092
    // From now the stream can be manipulated in unknown way to the checker,
1093
    // it is not possible to handle it any more.
1094
    // Optimistically, assume that the corresponding file handle will be closed
1095
    // somewhere else.
1096
    // Remove symbol from state so the following stream calls on this symbol are
1097
    // not handled by the checker.
1098
50
    State = State->remove<StreamMap>(Sym);
1099
50
  }
1100
284
  return State;
1101
303
}
1102
1103
//===----------------------------------------------------------------------===//
1104
// Checker registration.
1105
//===----------------------------------------------------------------------===//
1106
1107
11
void ento::registerStreamChecker(CheckerManager &Mgr) {
1108
11
  Mgr.registerChecker<StreamChecker>();
1109
11
}
1110
1111
24
bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
1112
24
  return true;
1113
24
}
1114
1115
1
void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
1116
1
  auto *Checker = Mgr.getChecker<StreamChecker>();
1117
1
  Checker->TestMode = true;
1118
1
}
1119
1120
2
bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
1121
2
  return true;
1122
2
}