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/CheckerHelpers.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
22#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
23#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
24#include <functional>
25#include <optional>
26
27using namespace clang;
28using namespace ento;
29using namespace std::placeholders;
30
31//===----------------------------------------------------------------------===//
32// Definition of state data structures.
33//===----------------------------------------------------------------------===//
34
35namespace {
36
37struct FnDescription;
38
39/// State of the stream error flags.
40/// Sometimes it is not known to the checker what error flags are set.
41/// This is indicated by setting more than one flag to true.
42/// This is an optimization to avoid state splits.
43/// A stream can either be in FEOF or FERROR but not both at the same time.
44/// Multiple flags are set to handle the corresponding states together.
45struct StreamErrorState {
46  /// The stream can be in state where none of the error flags set.
47  bool NoError = true;
48  /// The stream can be in state where the EOF indicator is set.
49  bool FEof = false;
50  /// The stream can be in state where the error indicator is set.
51  bool FError = false;
52
53  bool isNoError() const { return NoError && !FEof && !FError; }
54  bool isFEof() const { return !NoError && FEof && !FError; }
55  bool isFError() const { return !NoError && !FEof && FError; }
56
57  bool operator==(const StreamErrorState &ES) const {
58    return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
59  }
60
61  bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); }
62
63  StreamErrorState operator|(const StreamErrorState &E) const {
64    return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
65  }
66
67  StreamErrorState operator&(const StreamErrorState &E) const {
68    return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
69  }
70
71  StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; }
72
73  /// Returns if the StreamErrorState is a valid object.
74  operator bool() const { return NoError || FEof || FError; }
75
76  void Profile(llvm::FoldingSetNodeID &ID) const {
77    ID.AddBoolean(NoError);
78    ID.AddBoolean(FEof);
79    ID.AddBoolean(FError);
80  }
81};
82
83const StreamErrorState ErrorNone{true, false, false};
84const StreamErrorState ErrorFEof{false, true, false};
85const StreamErrorState ErrorFError{false, false, true};
86
87/// Full state information about a stream pointer.
88struct StreamState {
89  /// The last file operation called in the stream.
90  /// Can be nullptr.
91  const FnDescription *LastOperation;
92
93  /// State of a stream symbol.
94  enum KindTy {
95    Opened, /// Stream is opened.
96    Closed, /// Closed stream (an invalid stream pointer after it was closed).
97    OpenFailed /// The last open operation has failed.
98  } State;
99
100  /// State of the error flags.
101  /// Ignored in non-opened stream state but must be NoError.
102  StreamErrorState const ErrorState;
103
104  /// Indicate if the file has an "indeterminate file position indicator".
105  /// This can be set at a failing read or write or seek operation.
106  /// If it is set no more read or write is allowed.
107  /// This value is not dependent on the stream error flags:
108  /// The error flag may be cleared with `clearerr` but the file position
109  /// remains still indeterminate.
110  /// This value applies to all error states in ErrorState except FEOF.
111  /// An EOF+indeterminate state is the same as EOF state.
112  bool const FilePositionIndeterminate = false;
113
114  StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES,
115              bool IsFilePositionIndeterminate)
116      : LastOperation(L), State(S), ErrorState(ES),
117        FilePositionIndeterminate(IsFilePositionIndeterminate) {
118    assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
119           "FilePositionIndeterminate should be false in FEof case.");
120    assert((State == Opened || ErrorState.isNoError()) &&
121           "ErrorState should be None in non-opened stream state.");
122  }
123
124  bool isOpened() const { return State == Opened; }
125  bool isClosed() const { return State == Closed; }
126  bool isOpenFailed() const { return State == OpenFailed; }
127
128  bool operator==(const StreamState &X) const {
129    // In not opened state error state should always NoError, so comparison
130    // here is no problem.
131    return LastOperation == X.LastOperation && State == X.State &&
132           ErrorState == X.ErrorState &&
133           FilePositionIndeterminate == X.FilePositionIndeterminate;
134  }
135
136  static StreamState getOpened(const FnDescription *L,
137                               const StreamErrorState &ES = ErrorNone,
138                               bool IsFilePositionIndeterminate = false) {
139    return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
140  }
141  static StreamState getClosed(const FnDescription *L) {
142    return StreamState{L, Closed, {}, false};
143  }
144  static StreamState getOpenFailed(const FnDescription *L) {
145    return StreamState{L, OpenFailed, {}, false};
146  }
147
148  void Profile(llvm::FoldingSetNodeID &ID) const {
149    ID.AddPointer(LastOperation);
150    ID.AddInteger(State);
151    ErrorState.Profile(ID);
152    ID.AddBoolean(FilePositionIndeterminate);
153  }
154};
155
156} // namespace
157
158//===----------------------------------------------------------------------===//
159// StreamChecker class and utility functions.
160//===----------------------------------------------------------------------===//
161
162namespace {
163
164class StreamChecker;
165using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
166                                   const CallEvent &, CheckerContext &)>;
167
168using ArgNoTy = unsigned int;
169static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
170
171struct FnDescription {
172  FnCheck PreFn;
173  FnCheck EvalFn;
174  ArgNoTy StreamArgNo;
175};
176
177/// Get the value of the stream argument out of the passed call event.
178/// The call should contain a function that is described by Desc.
179SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {
180  assert(Desc && Desc->StreamArgNo != ArgNone &&
181         "Try to get a non-existing stream argument.");
182  return Call.getArgSVal(Desc->StreamArgNo);
183}
184
185/// Create a conjured symbol return value for a call expression.
186DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) {
187  assert(CE && "Expecting a call expression.");
188
189  const LocationContext *LCtx = C.getLocationContext();
190  return C.getSValBuilder()
191      .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
192      .castAs<DefinedSVal>();
193}
194
195ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
196                                  const CallExpr *CE) {
197  DefinedSVal RetVal = makeRetVal(C, CE);
198  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
199  State = State->assume(RetVal, true);
200  assert(State && "Assumption on new value should not fail.");
201  return State;
202}
203
204ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
205                        CheckerContext &C, const CallExpr *CE) {
206  State = State->BindExpr(CE, C.getLocationContext(),
207                          C.getSValBuilder().makeIntVal(Value, CE->getType()));
208  return State;
209}
210
211class StreamChecker : public Checker<check::PreCall, eval::Call,
212                                     check::DeadSymbols, check::PointerEscape> {
213  BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
214  BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
215  BugType BT_UseAfterOpenFailed{this, "Invalid stream",
216                                "Stream handling error"};
217  BugType BT_IndeterminatePosition{this, "Invalid stream state",
218                                   "Stream handling error"};
219  BugType BT_IllegalWhence{this, "Illegal whence argument",
220                           "Stream handling error"};
221  BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};
222  BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error",
223                          /*SuppressOnSink =*/true};
224
225public:
226  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
227  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
228  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
229  ProgramStateRef checkPointerEscape(ProgramStateRef State,
230                                     const InvalidatedSymbols &Escaped,
231                                     const CallEvent *Call,
232                                     PointerEscapeKind Kind) const;
233
234  /// If true, evaluate special testing stream functions.
235  bool TestMode = false;
236
237  const BugType *getBT_StreamEof() const { return &BT_StreamEof; }
238
239private:
240  CallDescriptionMap<FnDescription> FnDescriptions = {
241      {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
242      {{{"fdopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
243      {{{"freopen"}, 3},
244       {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
245      {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
246      {{{"fclose"}, 1},
247       {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
248      {{{"fread"}, 4},
249       {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
250        std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
251      {{{"fwrite"}, 4},
252       {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
253        std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
254      {{{"fgetc"}, 1},
255       {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
256        std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
257      {{{"fgets"}, 3},
258       {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
259        std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}},
260      {{{"fputc"}, 2},
261       {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
262        std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
263      {{{"fputs"}, 2},
264       {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
265        std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}},
266      {{{"fprintf"}},
267       {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
268        std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
269      {{{"fscanf"}},
270       {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
271        std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
272      {{{"ungetc"}, 2},
273       {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
274        std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}},
275      {{{"getdelim"}, 4},
276       {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
277        std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 3}},
278      {{{"getline"}, 3},
279       {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
280        std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 2}},
281      {{{"fseek"}, 3},
282       {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
283      {{{"fseeko"}, 3},
284       {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
285      {{{"ftell"}, 1},
286       {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
287      {{{"ftello"}, 1},
288       {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
289      {{{"fflush"}, 1},
290       {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
291      {{{"rewind"}, 1},
292       {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
293      {{{"fgetpos"}, 2},
294       {&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}},
295      {{{"fsetpos"}, 2},
296       {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
297      {{{"clearerr"}, 1},
298       {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
299      {{{"feof"}, 1},
300       {&StreamChecker::preDefault,
301        std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
302        0}},
303      {{{"ferror"}, 1},
304       {&StreamChecker::preDefault,
305        std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
306        0}},
307      {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}},
308  };
309
310  CallDescriptionMap<FnDescription> FnTestDescriptions = {
311      {{{"StreamTesterChecker_make_feof_stream"}, 1},
312       {nullptr,
313        std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof),
314        0}},
315      {{{"StreamTesterChecker_make_ferror_stream"}, 1},
316       {nullptr,
317        std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
318                  ErrorFError),
319        0}},
320  };
321
322  /// Expanded value of EOF, empty before initialization.
323  mutable std::optional<int> EofVal;
324  /// Expanded value of SEEK_SET, 0 if not found.
325  mutable int SeekSetVal = 0;
326  /// Expanded value of SEEK_CUR, 1 if not found.
327  mutable int SeekCurVal = 1;
328  /// Expanded value of SEEK_END, 2 if not found.
329  mutable int SeekEndVal = 2;
330
331  void evalFopen(const FnDescription *Desc, const CallEvent &Call,
332                 CheckerContext &C) const;
333
334  void preFreopen(const FnDescription *Desc, const CallEvent &Call,
335                  CheckerContext &C) const;
336  void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
337                   CheckerContext &C) const;
338
339  void evalFclose(const FnDescription *Desc, const CallEvent &Call,
340                  CheckerContext &C) const;
341
342  void preReadWrite(const FnDescription *Desc, const CallEvent &Call,
343                    CheckerContext &C, bool IsRead) const;
344
345  void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
346                       CheckerContext &C, bool IsFread) const;
347
348  void evalFgetx(const FnDescription *Desc, const CallEvent &Call,
349                 CheckerContext &C, bool SingleChar) const;
350
351  void evalFputx(const FnDescription *Desc, const CallEvent &Call,
352                 CheckerContext &C, bool IsSingleChar) const;
353
354  void evalFprintf(const FnDescription *Desc, const CallEvent &Call,
355                   CheckerContext &C) const;
356
357  void evalFscanf(const FnDescription *Desc, const CallEvent &Call,
358                  CheckerContext &C) const;
359
360  void evalUngetc(const FnDescription *Desc, const CallEvent &Call,
361                  CheckerContext &C) const;
362
363  void evalGetdelim(const FnDescription *Desc, const CallEvent &Call,
364                    CheckerContext &C) const;
365
366  void preFseek(const FnDescription *Desc, const CallEvent &Call,
367                CheckerContext &C) const;
368  void evalFseek(const FnDescription *Desc, const CallEvent &Call,
369                 CheckerContext &C) const;
370
371  void evalFgetpos(const FnDescription *Desc, const CallEvent &Call,
372                   CheckerContext &C) const;
373
374  void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
375                   CheckerContext &C) const;
376
377  void evalFtell(const FnDescription *Desc, const CallEvent &Call,
378                 CheckerContext &C) const;
379
380  void evalRewind(const FnDescription *Desc, const CallEvent &Call,
381                  CheckerContext &C) const;
382
383  void preDefault(const FnDescription *Desc, const CallEvent &Call,
384                  CheckerContext &C) const;
385
386  void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
387                    CheckerContext &C) const;
388
389  void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
390                      CheckerContext &C,
391                      const StreamErrorState &ErrorKind) const;
392
393  void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
394                         CheckerContext &C,
395                         const StreamErrorState &ErrorKind) const;
396
397  void preFflush(const FnDescription *Desc, const CallEvent &Call,
398                 CheckerContext &C) const;
399
400  void evalFflush(const FnDescription *Desc, const CallEvent &Call,
401                  CheckerContext &C) const;
402
403  /// Check that the stream (in StreamVal) is not NULL.
404  /// If it can only be NULL a fatal error is emitted and nullptr returned.
405  /// Otherwise the return value is a new state where the stream is constrained
406  /// to be non-null.
407  ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
408                                      CheckerContext &C,
409                                      ProgramStateRef State) const;
410
411  /// Check that the stream is the opened state.
412  /// If the stream is known to be not opened an error is generated
413  /// and nullptr returned, otherwise the original state is returned.
414  ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,
415                                     ProgramStateRef State) const;
416
417  /// Check that the stream has not an invalid ("indeterminate") file position,
418  /// generate warning for it.
419  /// (EOF is not an invalid position.)
420  /// The returned state can be nullptr if a fatal error was generated.
421  /// It can return non-null state if the stream has not an invalid position or
422  /// there is execution path with non-invalid position.
423  ProgramStateRef
424  ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,
425                                    ProgramStateRef State) const;
426
427  /// Check the legality of the 'whence' argument of 'fseek'.
428  /// Generate error and return nullptr if it is found to be illegal.
429  /// Otherwise returns the state.
430  /// (State is not changed here because the "whence" value is already known.)
431  ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
432                                           ProgramStateRef State) const;
433
434  /// Generate warning about stream in EOF state.
435  /// There will be always a state transition into the passed State,
436  /// by the new non-fatal error node or (if failed) a normal transition,
437  /// to ensure uniform handling.
438  void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
439                         ProgramStateRef State) const;
440
441  /// Emit resource leak warnings for the given symbols.
442  /// Createn a non-fatal error node for these, and returns it (if any warnings
443  /// were generated). Return value is non-null.
444  ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
445                            CheckerContext &C, ExplodedNode *Pred) const;
446
447  /// Find the description data of the function called by a call event.
448  /// Returns nullptr if no function is recognized.
449  const FnDescription *lookupFn(const CallEvent &Call) const {
450    // Recognize "global C functions" with only integral or pointer arguments
451    // (and matching name) as stream functions.
452    if (!Call.isGlobalCFunction())
453      return nullptr;
454    for (auto *P : Call.parameters()) {
455      QualType T = P->getType();
456      if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
457        return nullptr;
458    }
459
460    return FnDescriptions.lookup(Call);
461  }
462
463  /// Generate a message for BugReporterVisitor if the stored symbol is
464  /// marked as interesting by the actual bug report.
465  const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym,
466                                  const std::string &Message) const {
467    return C.getNoteTag([this, StreamSym,
468                         Message](PathSensitiveBugReport &BR) -> std::string {
469      if (BR.isInteresting(StreamSym) && &BR.getBugType() == &BT_ResourceLeak)
470        return Message;
471      return "";
472    });
473  }
474
475  const NoteTag *constructSetEofNoteTag(CheckerContext &C,
476                                        SymbolRef StreamSym) const {
477    return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
478      if (!BR.isInteresting(StreamSym) ||
479          &BR.getBugType() != this->getBT_StreamEof())
480        return "";
481
482      BR.markNotInteresting(StreamSym);
483
484      return "Assuming stream reaches end-of-file here";
485    });
486  }
487
488  void initMacroValues(CheckerContext &C) const {
489    if (EofVal)
490      return;
491
492    if (const std::optional<int> OptInt =
493            tryExpandAsInteger("EOF", C.getPreprocessor()))
494      EofVal = *OptInt;
495    else
496      EofVal = -1;
497    if (const std::optional<int> OptInt =
498            tryExpandAsInteger("SEEK_SET", C.getPreprocessor()))
499      SeekSetVal = *OptInt;
500    if (const std::optional<int> OptInt =
501            tryExpandAsInteger("SEEK_END", C.getPreprocessor()))
502      SeekEndVal = *OptInt;
503    if (const std::optional<int> OptInt =
504            tryExpandAsInteger("SEEK_CUR", C.getPreprocessor()))
505      SeekCurVal = *OptInt;
506  }
507
508  /// Searches for the ExplodedNode where the file descriptor was acquired for
509  /// StreamSym.
510  static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
511                                                SymbolRef StreamSym,
512                                                CheckerContext &C);
513};
514
515} // end anonymous namespace
516
517// This map holds the state of a stream.
518// The stream is identified with a SymbolRef that is created when a stream
519// opening function is modeled by the checker.
520REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
521
522inline void assertStreamStateOpened(const StreamState *SS) {
523  assert(SS->isOpened() && "Stream is expected to be opened");
524}
525
526const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
527                                                      SymbolRef StreamSym,
528                                                      CheckerContext &C) {
529  ProgramStateRef State = N->getState();
530  // When bug type is resource leak, exploded node N may not have state info
531  // for leaked file descriptor, but predecessor should have it.
532  if (!State->get<StreamMap>(StreamSym))
533    N = N->getFirstPred();
534
535  const ExplodedNode *Pred = N;
536  while (N) {
537    State = N->getState();
538    if (!State->get<StreamMap>(StreamSym))
539      return Pred;
540    Pred = N;
541    N = N->getFirstPred();
542  }
543
544  return nullptr;
545}
546
547//===----------------------------------------------------------------------===//
548// Methods of StreamChecker.
549//===----------------------------------------------------------------------===//
550
551void StreamChecker::checkPreCall(const CallEvent &Call,
552                                 CheckerContext &C) const {
553  initMacroValues(C);
554
555  const FnDescription *Desc = lookupFn(Call);
556  if (!Desc || !Desc->PreFn)
557    return;
558
559  Desc->PreFn(this, Desc, Call, C);
560}
561
562bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
563  const FnDescription *Desc = lookupFn(Call);
564  if (!Desc && TestMode)
565    Desc = FnTestDescriptions.lookup(Call);
566  if (!Desc || !Desc->EvalFn)
567    return false;
568
569  Desc->EvalFn(this, Desc, Call, C);
570
571  return C.isDifferent();
572}
573
574void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
575                              CheckerContext &C) const {
576  ProgramStateRef State = C.getState();
577  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
578  if (!CE)
579    return;
580
581  DefinedSVal RetVal = makeRetVal(C, CE);
582  SymbolRef RetSym = RetVal.getAsSymbol();
583  assert(RetSym && "RetVal must be a symbol here.");
584
585  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
586
587  // Bifurcate the state into two: one with a valid FILE* pointer, the other
588  // with a NULL.
589  ProgramStateRef StateNotNull, StateNull;
590  std::tie(StateNotNull, StateNull) =
591      C.getConstraintManager().assumeDual(State, RetVal);
592
593  StateNotNull =
594      StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
595  StateNull =
596      StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
597
598  C.addTransition(StateNotNull,
599                  constructNoteTag(C, RetSym, "Stream opened here"));
600  C.addTransition(StateNull);
601}
602
603void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
604                               CheckerContext &C) const {
605  // Do not allow NULL as passed stream pointer but allow a closed stream.
606  ProgramStateRef State = C.getState();
607  State = ensureStreamNonNull(getStreamArg(Desc, Call),
608                              Call.getArgExpr(Desc->StreamArgNo), C, State);
609  if (!State)
610    return;
611
612  C.addTransition(State);
613}
614
615void StreamChecker::evalFreopen(const FnDescription *Desc,
616                                const CallEvent &Call,
617                                CheckerContext &C) const {
618  ProgramStateRef State = C.getState();
619
620  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
621  if (!CE)
622    return;
623
624  std::optional<DefinedSVal> StreamVal =
625      getStreamArg(Desc, Call).getAs<DefinedSVal>();
626  if (!StreamVal)
627    return;
628
629  SymbolRef StreamSym = StreamVal->getAsSymbol();
630  // Do not care about concrete values for stream ("(FILE *)0x12345"?).
631  // FIXME: Can be stdin, stdout, stderr such values?
632  if (!StreamSym)
633    return;
634
635  // Do not handle untracked stream. It is probably escaped.
636  if (!State->get<StreamMap>(StreamSym))
637    return;
638
639  // Generate state for non-failed case.
640  // Return value is the passed stream pointer.
641  // According to the documentations, the stream is closed first
642  // but any close error is ignored. The state changes to (or remains) opened.
643  ProgramStateRef StateRetNotNull =
644      State->BindExpr(CE, C.getLocationContext(), *StreamVal);
645  // Generate state for NULL return value.
646  // Stream switches to OpenFailed state.
647  ProgramStateRef StateRetNull =
648      State->BindExpr(CE, C.getLocationContext(),
649                      C.getSValBuilder().makeNullWithType(CE->getType()));
650
651  StateRetNotNull =
652      StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
653  StateRetNull =
654      StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
655
656  C.addTransition(StateRetNotNull,
657                  constructNoteTag(C, StreamSym, "Stream reopened here"));
658  C.addTransition(StateRetNull);
659}
660
661void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
662                               CheckerContext &C) const {
663  ProgramStateRef State = C.getState();
664  SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
665  if (!Sym)
666    return;
667
668  const StreamState *SS = State->get<StreamMap>(Sym);
669  if (!SS)
670    return;
671
672  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
673  if (!CE)
674    return;
675
676  assertStreamStateOpened(SS);
677
678  // Close the File Descriptor.
679  // Regardless if the close fails or not, stream becomes "closed"
680  // and can not be used any more.
681  State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
682
683  // Return 0 on success, EOF on failure.
684  SValBuilder &SVB = C.getSValBuilder();
685  ProgramStateRef StateSuccess = State->BindExpr(
686      CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy));
687  ProgramStateRef StateFailure =
688      State->BindExpr(CE, C.getLocationContext(),
689                      SVB.makeIntVal(*EofVal, C.getASTContext().IntTy));
690
691  C.addTransition(StateSuccess);
692  C.addTransition(StateFailure);
693}
694
695void StreamChecker::preReadWrite(const FnDescription *Desc,
696                                 const CallEvent &Call, CheckerContext &C,
697                                 bool IsRead) const {
698  ProgramStateRef State = C.getState();
699  SVal StreamVal = getStreamArg(Desc, Call);
700  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
701                              State);
702  if (!State)
703    return;
704  State = ensureStreamOpened(StreamVal, C, State);
705  if (!State)
706    return;
707  State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
708  if (!State)
709    return;
710
711  if (!IsRead) {
712    C.addTransition(State);
713    return;
714  }
715
716  SymbolRef Sym = StreamVal.getAsSymbol();
717  if (Sym && State->get<StreamMap>(Sym)) {
718    const StreamState *SS = State->get<StreamMap>(Sym);
719    if (SS->ErrorState & ErrorFEof)
720      reportFEofWarning(Sym, C, State);
721  } else {
722    C.addTransition(State);
723  }
724}
725
726void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
727                                    const CallEvent &Call, CheckerContext &C,
728                                    bool IsFread) const {
729  ProgramStateRef State = C.getState();
730  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
731  if (!StreamSym)
732    return;
733
734  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
735  if (!CE)
736    return;
737
738  std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
739  if (!SizeVal)
740    return;
741  std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
742  if (!NMembVal)
743    return;
744
745  const StreamState *OldSS = State->get<StreamMap>(StreamSym);
746  if (!OldSS)
747    return;
748
749  assertStreamStateOpened(OldSS);
750
751  // C'99 standard, ��7.19.8.1.3, the return value of fread:
752  // The fread function returns the number of elements successfully read, which
753  // may be less than nmemb if a read error or end-of-file is encountered. If
754  // size or nmemb is zero, fread returns zero and the contents of the array and
755  // the state of the stream remain unchanged.
756
757  if (State->isNull(*SizeVal).isConstrainedTrue() ||
758      State->isNull(*NMembVal).isConstrainedTrue()) {
759    // This is the "size or nmemb is zero" case.
760    // Just return 0, do nothing more (not clear the error flags).
761    State = bindInt(0, State, C, CE);
762    C.addTransition(State);
763    return;
764  }
765
766  // Generate a transition for the success state.
767  // If we know the state to be FEOF at fread, do not add a success state.
768  if (!IsFread || (OldSS->ErrorState != ErrorFEof)) {
769    ProgramStateRef StateNotFailed =
770        State->BindExpr(CE, C.getLocationContext(), *NMembVal);
771    StateNotFailed =
772        StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
773    C.addTransition(StateNotFailed);
774  }
775
776  // Add transition for the failed state.
777  NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
778  ProgramStateRef StateFailed =
779      State->BindExpr(CE, C.getLocationContext(), RetVal);
780  SValBuilder &SVB = C.getSValBuilder();
781  auto Cond =
782      SVB.evalBinOpNN(State, BO_LT, RetVal, *NMembVal, SVB.getConditionType())
783          .getAs<DefinedOrUnknownSVal>();
784  if (!Cond)
785    return;
786  StateFailed = StateFailed->assume(*Cond, true);
787  if (!StateFailed)
788    return;
789
790  StreamErrorState NewES;
791  if (IsFread)
792    NewES =
793        (OldSS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError;
794  else
795    NewES = ErrorFError;
796  // If a (non-EOF) error occurs, the resulting value of the file position
797  // indicator for the stream is indeterminate.
798  StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
799  StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
800  if (IsFread && OldSS->ErrorState != ErrorFEof)
801    C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
802  else
803    C.addTransition(StateFailed);
804}
805
806void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call,
807                              CheckerContext &C, bool SingleChar) const {
808  ProgramStateRef State = C.getState();
809  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
810  if (!StreamSym)
811    return;
812
813  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
814  if (!CE)
815    return;
816
817  const StreamState *OldSS = State->get<StreamMap>(StreamSym);
818  if (!OldSS)
819    return;
820
821  assertStreamStateOpened(OldSS);
822
823  // `fgetc` returns the read character on success, otherwise returns EOF.
824  // `fgets` returns the read buffer address on success, otherwise returns NULL.
825
826  if (OldSS->ErrorState != ErrorFEof) {
827    if (SingleChar) {
828      // Generate a transition for the success state of `fgetc`.
829      NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
830      ProgramStateRef StateNotFailed =
831          State->BindExpr(CE, C.getLocationContext(), RetVal);
832      SValBuilder &SVB = C.getSValBuilder();
833      ASTContext &ASTC = C.getASTContext();
834      // The returned 'unsigned char' of `fgetc` is converted to 'int',
835      // so we need to check if it is in range [0, 255].
836      auto CondLow =
837          SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy),
838                        SVB.getConditionType())
839              .getAs<DefinedOrUnknownSVal>();
840      auto CondHigh =
841          SVB.evalBinOp(State, BO_LE, RetVal,
842                        SVB.makeIntVal(SVB.getBasicValueFactory()
843                                           .getMaxValue(ASTC.UnsignedCharTy)
844                                           .getLimitedValue(),
845                                       ASTC.IntTy),
846                        SVB.getConditionType())
847              .getAs<DefinedOrUnknownSVal>();
848      if (!CondLow || !CondHigh)
849        return;
850      StateNotFailed = StateNotFailed->assume(*CondLow, true);
851      if (!StateNotFailed)
852        return;
853      StateNotFailed = StateNotFailed->assume(*CondHigh, true);
854      if (!StateNotFailed)
855        return;
856      C.addTransition(StateNotFailed);
857    } else {
858      // Generate a transition for the success state of `fgets`.
859      std::optional<DefinedSVal> GetBuf =
860          Call.getArgSVal(0).getAs<DefinedSVal>();
861      if (!GetBuf)
862        return;
863      ProgramStateRef StateNotFailed =
864          State->BindExpr(CE, C.getLocationContext(), *GetBuf);
865      StateNotFailed = StateNotFailed->set<StreamMap>(
866          StreamSym, StreamState::getOpened(Desc));
867      C.addTransition(StateNotFailed);
868    }
869  }
870
871  // Add transition for the failed state.
872  ProgramStateRef StateFailed;
873  if (SingleChar)
874    StateFailed = bindInt(*EofVal, State, C, CE);
875  else
876    StateFailed =
877        State->BindExpr(CE, C.getLocationContext(),
878                        C.getSValBuilder().makeNullWithType(CE->getType()));
879
880  // If a (non-EOF) error occurs, the resulting value of the file position
881  // indicator for the stream is indeterminate.
882  StreamErrorState NewES =
883      OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError;
884  StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
885  StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
886  if (OldSS->ErrorState != ErrorFEof)
887    C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
888  else
889    C.addTransition(StateFailed);
890}
891
892void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call,
893                              CheckerContext &C, bool IsSingleChar) const {
894  ProgramStateRef State = C.getState();
895  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
896  if (!StreamSym)
897    return;
898
899  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
900  if (!CE)
901    return;
902
903  const StreamState *OldSS = State->get<StreamMap>(StreamSym);
904  if (!OldSS)
905    return;
906
907  assertStreamStateOpened(OldSS);
908
909  // `fputc` returns the written character on success, otherwise returns EOF.
910  // `fputs` returns a non negative value on sucecess, otherwise returns EOF.
911
912  if (IsSingleChar) {
913    // Generate a transition for the success state of `fputc`.
914    std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
915    if (!PutVal)
916      return;
917    ProgramStateRef StateNotFailed =
918        State->BindExpr(CE, C.getLocationContext(), *PutVal);
919    StateNotFailed =
920        StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
921    C.addTransition(StateNotFailed);
922  } else {
923    // Generate a transition for the success state of `fputs`.
924    NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
925    ProgramStateRef StateNotFailed =
926        State->BindExpr(CE, C.getLocationContext(), RetVal);
927    SValBuilder &SVB = C.getSValBuilder();
928    auto &ASTC = C.getASTContext();
929    auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy),
930                              SVB.getConditionType())
931                    .getAs<DefinedOrUnknownSVal>();
932    if (!Cond)
933      return;
934    StateNotFailed = StateNotFailed->assume(*Cond, true);
935    if (!StateNotFailed)
936      return;
937    StateNotFailed =
938        StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
939    C.addTransition(StateNotFailed);
940  }
941
942  // Add transition for the failed state. The resulting value of the file
943  // position indicator for the stream is indeterminate.
944  ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
945  StreamState NewSS = StreamState::getOpened(Desc, ErrorFError, true);
946  StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
947  C.addTransition(StateFailed);
948}
949
950void StreamChecker::evalFprintf(const FnDescription *Desc,
951                                const CallEvent &Call,
952                                CheckerContext &C) const {
953  ProgramStateRef State = C.getState();
954  if (Call.getNumArgs() < 2)
955    return;
956  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
957  if (!StreamSym)
958    return;
959
960  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
961  if (!CE)
962    return;
963
964  const StreamState *OldSS = State->get<StreamMap>(StreamSym);
965  if (!OldSS)
966    return;
967
968  assertStreamStateOpened(OldSS);
969
970  NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
971  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
972  SValBuilder &SVB = C.getSValBuilder();
973  auto &ACtx = C.getASTContext();
974  auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ACtx.IntTy),
975                            SVB.getConditionType())
976                  .getAs<DefinedOrUnknownSVal>();
977  if (!Cond)
978    return;
979  ProgramStateRef StateNotFailed, StateFailed;
980  std::tie(StateNotFailed, StateFailed) = State->assume(*Cond);
981
982  StateNotFailed =
983      StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
984  C.addTransition(StateNotFailed);
985
986  // Add transition for the failed state. The resulting value of the file
987  // position indicator for the stream is indeterminate.
988  StateFailed = StateFailed->set<StreamMap>(
989      StreamSym, StreamState::getOpened(Desc, ErrorFError, true));
990  C.addTransition(StateFailed);
991}
992
993void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call,
994                               CheckerContext &C) const {
995  ProgramStateRef State = C.getState();
996  if (Call.getNumArgs() < 2)
997    return;
998  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
999  if (!StreamSym)
1000    return;
1001
1002  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1003  if (!CE)
1004    return;
1005
1006  const StreamState *OldSS = State->get<StreamMap>(StreamSym);
1007  if (!OldSS)
1008    return;
1009
1010  assertStreamStateOpened(OldSS);
1011
1012  SValBuilder &SVB = C.getSValBuilder();
1013  ASTContext &ACtx = C.getASTContext();
1014
1015  // Add the success state.
1016  // In this context "success" means there is not an EOF or other read error
1017  // before any item is matched in 'fscanf'. But there may be match failure,
1018  // therefore return value can be 0 or greater.
1019  // It is not specified what happens if some items (not all) are matched and
1020  // then EOF or read error happens. Now this case is handled like a "success"
1021  // case, and no error flags are set on the stream. This is probably not
1022  // accurate, and the POSIX documentation does not tell more.
1023  if (OldSS->ErrorState != ErrorFEof) {
1024    NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
1025    ProgramStateRef StateNotFailed =
1026        State->BindExpr(CE, C.getLocationContext(), RetVal);
1027    auto RetGeZero =
1028        SVB.evalBinOp(StateNotFailed, BO_GE, RetVal,
1029                      SVB.makeZeroVal(ACtx.IntTy), SVB.getConditionType())
1030            .getAs<DefinedOrUnknownSVal>();
1031    if (!RetGeZero)
1032      return;
1033    StateNotFailed = StateNotFailed->assume(*RetGeZero, true);
1034
1035    C.addTransition(StateNotFailed);
1036  }
1037
1038  // Add transition for the failed state.
1039  // Error occurs if nothing is matched yet and reading the input fails.
1040  // Error can be EOF, or other error. At "other error" FERROR or 'errno' can
1041  // be set but it is not further specified if all are required to be set.
1042  // Documentation does not mention, but file position will be set to
1043  // indeterminate similarly as at 'fread'.
1044  ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
1045  StreamErrorState NewES = (OldSS->ErrorState == ErrorFEof)
1046                               ? ErrorFEof
1047                               : ErrorNone | ErrorFEof | ErrorFError;
1048  StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
1049  StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
1050  if (OldSS->ErrorState != ErrorFEof)
1051    C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
1052  else
1053    C.addTransition(StateFailed);
1054}
1055
1056void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent &Call,
1057                               CheckerContext &C) const {
1058  ProgramStateRef State = C.getState();
1059  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1060  if (!StreamSym)
1061    return;
1062
1063  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1064  if (!CE)
1065    return;
1066
1067  const StreamState *OldSS = State->get<StreamMap>(StreamSym);
1068  if (!OldSS)
1069    return;
1070
1071  assertStreamStateOpened(OldSS);
1072
1073  // Generate a transition for the success state.
1074  std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
1075  if (!PutVal)
1076    return;
1077  ProgramStateRef StateNotFailed =
1078      State->BindExpr(CE, C.getLocationContext(), *PutVal);
1079  StateNotFailed =
1080      StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
1081  C.addTransition(StateNotFailed);
1082
1083  // Add transition for the failed state.
1084  // Failure of 'ungetc' does not result in feof or ferror state.
1085  // If the PutVal has value of EofVal the function should "fail", but this is
1086  // the same transition as the success state.
1087  // In this case only one state transition is added by the analyzer (the two
1088  // new states may be similar).
1089  ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
1090  StateFailed =
1091      StateFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
1092  C.addTransition(StateFailed);
1093}
1094
1095void StreamChecker::evalGetdelim(const FnDescription *Desc,
1096                                 const CallEvent &Call,
1097                                 CheckerContext &C) const {
1098  ProgramStateRef State = C.getState();
1099  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1100  if (!StreamSym)
1101    return;
1102
1103  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1104  if (!CE)
1105    return;
1106
1107  const StreamState *OldSS = State->get<StreamMap>(StreamSym);
1108  if (!OldSS)
1109    return;
1110
1111  assertStreamStateOpened(OldSS);
1112
1113  // Upon successful completion, the getline() and getdelim() functions shall
1114  // return the number of bytes written into the buffer.
1115  // If the end-of-file indicator for the stream is set, the function shall
1116  // return -1.
1117  // If an error occurs, the function shall return -1 and set 'errno'.
1118
1119  // Add transition for the successful state.
1120  if (OldSS->ErrorState != ErrorFEof) {
1121    NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
1122    ProgramStateRef StateNotFailed =
1123        State->BindExpr(CE, C.getLocationContext(), RetVal);
1124    SValBuilder &SVB = C.getSValBuilder();
1125    auto Cond =
1126        SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(CE->getType()),
1127                      SVB.getConditionType())
1128            .getAs<DefinedOrUnknownSVal>();
1129    if (!Cond)
1130      return;
1131    StateNotFailed = StateNotFailed->assume(*Cond, true);
1132    if (!StateNotFailed)
1133      return;
1134    C.addTransition(StateNotFailed);
1135  }
1136
1137  // Add transition for the failed state.
1138  // If a (non-EOF) error occurs, the resulting value of the file position
1139  // indicator for the stream is indeterminate.
1140  ProgramStateRef StateFailed = bindInt(-1, State, C, CE);
1141  StreamErrorState NewES =
1142      OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError;
1143  StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
1144  StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
1145  if (OldSS->ErrorState != ErrorFEof)
1146    C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
1147  else
1148    C.addTransition(StateFailed);
1149}
1150
1151void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
1152                             CheckerContext &C) const {
1153  ProgramStateRef State = C.getState();
1154  SVal StreamVal = getStreamArg(Desc, Call);
1155  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1156                              State);
1157  if (!State)
1158    return;
1159  State = ensureStreamOpened(StreamVal, C, State);
1160  if (!State)
1161    return;
1162  State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
1163  if (!State)
1164    return;
1165
1166  C.addTransition(State);
1167}
1168
1169void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
1170                              CheckerContext &C) const {
1171  ProgramStateRef State = C.getState();
1172  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1173  if (!StreamSym)
1174    return;
1175
1176  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1177  if (!CE)
1178    return;
1179
1180  // Ignore the call if the stream is not tracked.
1181  if (!State->get<StreamMap>(StreamSym))
1182    return;
1183
1184  const llvm::APSInt *PosV =
1185      C.getSValBuilder().getKnownValue(State, Call.getArgSVal(1));
1186  const llvm::APSInt *WhenceV =
1187      C.getSValBuilder().getKnownValue(State, Call.getArgSVal(2));
1188
1189  DefinedSVal RetVal = makeRetVal(C, CE);
1190
1191  // Make expression result.
1192  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
1193
1194  // Bifurcate the state into failed and non-failed.
1195  // Return zero on success, nonzero on error.
1196  ProgramStateRef StateNotFailed, StateFailed;
1197  std::tie(StateFailed, StateNotFailed) =
1198      C.getConstraintManager().assumeDual(State, RetVal);
1199
1200  // Reset the state to opened with no error.
1201  StateNotFailed =
1202      StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
1203  // We get error.
1204  // It is possible that fseek fails but sets none of the error flags.
1205  // If fseek failed, assume that the file position becomes indeterminate in any
1206  // case.
1207  StreamErrorState NewErrS = ErrorNone | ErrorFError;
1208  // Setting the position to start of file never produces EOF error.
1209  if (!(PosV && *PosV == 0 && WhenceV && *WhenceV == SeekSetVal))
1210    NewErrS = NewErrS | ErrorFEof;
1211  StateFailed = StateFailed->set<StreamMap>(
1212      StreamSym, StreamState::getOpened(Desc, NewErrS, true));
1213
1214  C.addTransition(StateNotFailed);
1215  C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
1216}
1217
1218void StreamChecker::evalFgetpos(const FnDescription *Desc,
1219                                const CallEvent &Call,
1220                                CheckerContext &C) const {
1221  ProgramStateRef State = C.getState();
1222  SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
1223  if (!Sym)
1224    return;
1225
1226  // Do not evaluate if stream is not found.
1227  if (!State->get<StreamMap>(Sym))
1228    return;
1229
1230  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1231  if (!CE)
1232    return;
1233
1234  DefinedSVal RetVal = makeRetVal(C, CE);
1235  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
1236  ProgramStateRef StateNotFailed, StateFailed;
1237  std::tie(StateFailed, StateNotFailed) =
1238      C.getConstraintManager().assumeDual(State, RetVal);
1239
1240  // This function does not affect the stream state.
1241  // Still we add success and failure state with the appropriate return value.
1242  // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
1243  C.addTransition(StateNotFailed);
1244  C.addTransition(StateFailed);
1245}
1246
1247void StreamChecker::evalFsetpos(const FnDescription *Desc,
1248                                const CallEvent &Call,
1249                                CheckerContext &C) const {
1250  ProgramStateRef State = C.getState();
1251  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1252  if (!StreamSym)
1253    return;
1254
1255  const StreamState *SS = State->get<StreamMap>(StreamSym);
1256  if (!SS)
1257    return;
1258
1259  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1260  if (!CE)
1261    return;
1262
1263  assertStreamStateOpened(SS);
1264
1265  DefinedSVal RetVal = makeRetVal(C, CE);
1266  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
1267  ProgramStateRef StateNotFailed, StateFailed;
1268  std::tie(StateFailed, StateNotFailed) =
1269      C.getConstraintManager().assumeDual(State, RetVal);
1270
1271  StateNotFailed = StateNotFailed->set<StreamMap>(
1272      StreamSym, StreamState::getOpened(Desc, ErrorNone, false));
1273
1274  // At failure ferror could be set.
1275  // The standards do not tell what happens with the file position at failure.
1276  // But we can assume that it is dangerous to make a next I/O operation after
1277  // the position was not set correctly (similar to 'fseek').
1278  StateFailed = StateFailed->set<StreamMap>(
1279      StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
1280
1281  C.addTransition(StateNotFailed);
1282  C.addTransition(StateFailed);
1283}
1284
1285void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
1286                              CheckerContext &C) const {
1287  ProgramStateRef State = C.getState();
1288  SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
1289  if (!Sym)
1290    return;
1291
1292  if (!State->get<StreamMap>(Sym))
1293    return;
1294
1295  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1296  if (!CE)
1297    return;
1298
1299  SValBuilder &SVB = C.getSValBuilder();
1300  NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
1301  ProgramStateRef StateNotFailed =
1302      State->BindExpr(CE, C.getLocationContext(), RetVal);
1303  auto Cond =
1304      SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(Call.getResultType()),
1305                    SVB.getConditionType())
1306          .getAs<DefinedOrUnknownSVal>();
1307  if (!Cond)
1308    return;
1309  StateNotFailed = StateNotFailed->assume(*Cond, true);
1310  if (!StateNotFailed)
1311    return;
1312
1313  ProgramStateRef StateFailed = State->BindExpr(
1314      CE, C.getLocationContext(), SVB.makeIntVal(-1, Call.getResultType()));
1315
1316  // This function does not affect the stream state.
1317  // Still we add success and failure state with the appropriate return value.
1318  // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
1319  C.addTransition(StateNotFailed);
1320  C.addTransition(StateFailed);
1321}
1322
1323void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call,
1324                               CheckerContext &C) const {
1325  ProgramStateRef State = C.getState();
1326  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1327  if (!StreamSym)
1328    return;
1329
1330  const StreamState *SS = State->get<StreamMap>(StreamSym);
1331  if (!SS)
1332    return;
1333
1334  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1335  if (!CE)
1336    return;
1337
1338  assertStreamStateOpened(SS);
1339
1340  State = State->set<StreamMap>(StreamSym,
1341                                StreamState::getOpened(Desc, ErrorNone, false));
1342
1343  C.addTransition(State);
1344}
1345
1346void StreamChecker::evalClearerr(const FnDescription *Desc,
1347                                 const CallEvent &Call,
1348                                 CheckerContext &C) const {
1349  ProgramStateRef State = C.getState();
1350  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1351  if (!StreamSym)
1352    return;
1353
1354  const StreamState *SS = State->get<StreamMap>(StreamSym);
1355  if (!SS)
1356    return;
1357
1358  assertStreamStateOpened(SS);
1359
1360  // FilePositionIndeterminate is not cleared.
1361  State = State->set<StreamMap>(
1362      StreamSym,
1363      StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate));
1364  C.addTransition(State);
1365}
1366
1367void StreamChecker::evalFeofFerror(const FnDescription *Desc,
1368                                   const CallEvent &Call, CheckerContext &C,
1369                                   const StreamErrorState &ErrorKind) const {
1370  ProgramStateRef State = C.getState();
1371  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1372  if (!StreamSym)
1373    return;
1374
1375  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1376  if (!CE)
1377    return;
1378
1379  const StreamState *SS = State->get<StreamMap>(StreamSym);
1380  if (!SS)
1381    return;
1382
1383  assertStreamStateOpened(SS);
1384
1385  if (SS->ErrorState & ErrorKind) {
1386    // Execution path with error of ErrorKind.
1387    // Function returns true.
1388    // From now on it is the only one error state.
1389    ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE);
1390    C.addTransition(TrueState->set<StreamMap>(
1391        StreamSym, StreamState::getOpened(Desc, ErrorKind,
1392                                          SS->FilePositionIndeterminate &&
1393                                              !ErrorKind.isFEof())));
1394  }
1395  if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) {
1396    // Execution path(s) with ErrorKind not set.
1397    // Function returns false.
1398    // New error state is everything before minus ErrorKind.
1399    ProgramStateRef FalseState = bindInt(0, State, C, CE);
1400    C.addTransition(FalseState->set<StreamMap>(
1401        StreamSym,
1402        StreamState::getOpened(
1403            Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof())));
1404  }
1405}
1406
1407void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
1408                               CheckerContext &C) const {
1409  ProgramStateRef State = C.getState();
1410  SVal StreamVal = getStreamArg(Desc, Call);
1411  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1412                              State);
1413  if (!State)
1414    return;
1415  State = ensureStreamOpened(StreamVal, C, State);
1416  if (!State)
1417    return;
1418
1419  C.addTransition(State);
1420}
1421
1422void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
1423                                      const CallEvent &Call, CheckerContext &C,
1424                                      const StreamErrorState &ErrorKind) const {
1425  ProgramStateRef State = C.getState();
1426  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1427  assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
1428  const StreamState *SS = State->get<StreamMap>(StreamSym);
1429  assert(SS && "Stream should be tracked by the checker.");
1430  State = State->set<StreamMap>(
1431      StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind));
1432  C.addTransition(State);
1433}
1434
1435void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call,
1436                              CheckerContext &C) const {
1437  ProgramStateRef State = C.getState();
1438  SVal StreamVal = getStreamArg(Desc, Call);
1439  std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>();
1440  if (!Stream)
1441    return;
1442
1443  ProgramStateRef StateNotNull, StateNull;
1444  std::tie(StateNotNull, StateNull) =
1445      C.getConstraintManager().assumeDual(State, *Stream);
1446  if (StateNotNull && !StateNull)
1447    ensureStreamOpened(StreamVal, C, StateNotNull);
1448}
1449
1450void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call,
1451                               CheckerContext &C) const {
1452  ProgramStateRef State = C.getState();
1453  SVal StreamVal = getStreamArg(Desc, Call);
1454  std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>();
1455  if (!Stream)
1456    return;
1457
1458  // Skip if the stream can be both NULL and non-NULL.
1459  ProgramStateRef StateNotNull, StateNull;
1460  std::tie(StateNotNull, StateNull) =
1461      C.getConstraintManager().assumeDual(State, *Stream);
1462  if (StateNotNull && StateNull)
1463    return;
1464  if (StateNotNull && !StateNull)
1465    State = StateNotNull;
1466  else
1467    State = StateNull;
1468
1469  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1470  if (!CE)
1471    return;
1472
1473  // `fflush` returns EOF on failure, otherwise returns 0.
1474  ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
1475  ProgramStateRef StateNotFailed = bindInt(0, State, C, CE);
1476
1477  // Clear error states if `fflush` returns 0, but retain their EOF flags.
1478  auto ClearErrorInNotFailed = [&StateNotFailed, Desc](SymbolRef Sym,
1479                                                       const StreamState *SS) {
1480    if (SS->ErrorState & ErrorFError) {
1481      StreamErrorState NewES =
1482          (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone;
1483      StreamState NewSS = StreamState::getOpened(Desc, NewES, false);
1484      StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS);
1485    }
1486  };
1487
1488  if (StateNotNull && !StateNull) {
1489    // Skip if the input stream's state is unknown, open-failed or closed.
1490    if (SymbolRef StreamSym = StreamVal.getAsSymbol()) {
1491      const StreamState *SS = State->get<StreamMap>(StreamSym);
1492      if (SS) {
1493        assert(SS->isOpened() && "Stream is expected to be opened");
1494        ClearErrorInNotFailed(StreamSym, SS);
1495      } else
1496        return;
1497    }
1498  } else {
1499    // Clear error states for all streams.
1500    const StreamMapTy &Map = StateNotFailed->get<StreamMap>();
1501    for (const auto &I : Map) {
1502      SymbolRef Sym = I.first;
1503      const StreamState &SS = I.second;
1504      if (SS.isOpened())
1505        ClearErrorInNotFailed(Sym, &SS);
1506    }
1507  }
1508
1509  C.addTransition(StateNotFailed);
1510  C.addTransition(StateFailed);
1511}
1512
1513ProgramStateRef
1514StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
1515                                   CheckerContext &C,
1516                                   ProgramStateRef State) const {
1517  auto Stream = StreamVal.getAs<DefinedSVal>();
1518  if (!Stream)
1519    return State;
1520
1521  ConstraintManager &CM = C.getConstraintManager();
1522
1523  ProgramStateRef StateNotNull, StateNull;
1524  std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream);
1525
1526  if (!StateNotNull && StateNull) {
1527    if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
1528      auto R = std::make_unique<PathSensitiveBugReport>(
1529          BT_FileNull, "Stream pointer might be NULL.", N);
1530      if (StreamE)
1531        bugreporter::trackExpressionValue(N, StreamE, *R);
1532      C.emitReport(std::move(R));
1533    }
1534    return nullptr;
1535  }
1536
1537  return StateNotNull;
1538}
1539
1540ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
1541                                                  CheckerContext &C,
1542                                                  ProgramStateRef State) const {
1543  SymbolRef Sym = StreamVal.getAsSymbol();
1544  if (!Sym)
1545    return State;
1546
1547  const StreamState *SS = State->get<StreamMap>(Sym);
1548  if (!SS)
1549    return State;
1550
1551  if (SS->isClosed()) {
1552    // Using a stream pointer after 'fclose' causes undefined behavior
1553    // according to cppreference.com .
1554    ExplodedNode *N = C.generateErrorNode();
1555    if (N) {
1556      C.emitReport(std::make_unique<PathSensitiveBugReport>(
1557          BT_UseAfterClose,
1558          "Stream might be already closed. Causes undefined behaviour.", N));
1559      return nullptr;
1560    }
1561
1562    return State;
1563  }
1564
1565  if (SS->isOpenFailed()) {
1566    // Using a stream that has failed to open is likely to cause problems.
1567    // This should usually not occur because stream pointer is NULL.
1568    // But freopen can cause a state when stream pointer remains non-null but
1569    // failed to open.
1570    ExplodedNode *N = C.generateErrorNode();
1571    if (N) {
1572      C.emitReport(std::make_unique<PathSensitiveBugReport>(
1573          BT_UseAfterOpenFailed,
1574          "Stream might be invalid after "
1575          "(re-)opening it has failed. "
1576          "Can cause undefined behaviour.",
1577          N));
1578      return nullptr;
1579    }
1580  }
1581
1582  return State;
1583}
1584
1585ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
1586    SVal StreamVal, CheckerContext &C, ProgramStateRef State) const {
1587  static const char *BugMessage =
1588      "File position of the stream might be 'indeterminate' "
1589      "after a failed operation. "
1590      "Can cause undefined behavior.";
1591
1592  SymbolRef Sym = StreamVal.getAsSymbol();
1593  if (!Sym)
1594    return State;
1595
1596  const StreamState *SS = State->get<StreamMap>(Sym);
1597  if (!SS)
1598    return State;
1599
1600  assert(SS->isOpened() && "First ensure that stream is opened.");
1601
1602  if (SS->FilePositionIndeterminate) {
1603    if (SS->ErrorState & ErrorFEof) {
1604      // The error is unknown but may be FEOF.
1605      // Continue analysis with the FEOF error state.
1606      // Report warning because the other possible error states.
1607      ExplodedNode *N = C.generateNonFatalErrorNode(State);
1608      if (!N)
1609        return nullptr;
1610
1611      C.emitReport(std::make_unique<PathSensitiveBugReport>(
1612          BT_IndeterminatePosition, BugMessage, N));
1613      return State->set<StreamMap>(
1614          Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));
1615    }
1616
1617    // Known or unknown error state without FEOF possible.
1618    // Stop analysis, report error.
1619    ExplodedNode *N = C.generateErrorNode(State);
1620    if (N)
1621      C.emitReport(std::make_unique<PathSensitiveBugReport>(
1622          BT_IndeterminatePosition, BugMessage, N));
1623
1624    return nullptr;
1625  }
1626
1627  return State;
1628}
1629
1630ProgramStateRef
1631StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
1632                                        ProgramStateRef State) const {
1633  std::optional<nonloc::ConcreteInt> CI =
1634      WhenceVal.getAs<nonloc::ConcreteInt>();
1635  if (!CI)
1636    return State;
1637
1638  int64_t X = CI->getValue().getSExtValue();
1639  if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal)
1640    return State;
1641
1642  if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1643    C.emitReport(std::make_unique<PathSensitiveBugReport>(
1644        BT_IllegalWhence,
1645        "The whence argument to fseek() should be "
1646        "SEEK_SET, SEEK_END, or SEEK_CUR.",
1647        N));
1648    return nullptr;
1649  }
1650
1651  return State;
1652}
1653
1654void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
1655                                      ProgramStateRef State) const {
1656  if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1657    auto R = std::make_unique<PathSensitiveBugReport>(
1658        BT_StreamEof,
1659        "Read function called when stream is in EOF state. "
1660        "Function has no effect.",
1661        N);
1662    R->markInteresting(StreamSym);
1663    C.emitReport(std::move(R));
1664    return;
1665  }
1666  C.addTransition(State);
1667}
1668
1669ExplodedNode *
1670StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
1671                           CheckerContext &C, ExplodedNode *Pred) const {
1672  ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);
1673  if (!Err)
1674    return Pred;
1675
1676  for (SymbolRef LeakSym : LeakedSyms) {
1677    // Resource leaks can result in multiple warning that describe the same kind
1678    // of programming error:
1679    //  void f() {
1680    //    FILE *F = fopen("a.txt");
1681    //    if (rand()) // state split
1682    //      return; // warning
1683    //  } // warning
1684    // While this isn't necessarily true (leaking the same stream could result
1685    // from a different kinds of errors), the reduction in redundant reports
1686    // makes this a worthwhile heuristic.
1687    // FIXME: Add a checker option to turn this uniqueing feature off.
1688    const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
1689    assert(StreamOpenNode && "Could not find place of stream opening.");
1690
1691    PathDiagnosticLocation LocUsedForUniqueing;
1692    if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics())
1693      LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
1694          StreamStmt, C.getSourceManager(),
1695          StreamOpenNode->getLocationContext());
1696
1697    std::unique_ptr<PathSensitiveBugReport> R =
1698        std::make_unique<PathSensitiveBugReport>(
1699            BT_ResourceLeak,
1700            "Opened stream never closed. Potential resource leak.", Err,
1701            LocUsedForUniqueing,
1702            StreamOpenNode->getLocationContext()->getDecl());
1703    R->markInteresting(LeakSym);
1704    C.emitReport(std::move(R));
1705  }
1706
1707  return Err;
1708}
1709
1710void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1711                                     CheckerContext &C) const {
1712  ProgramStateRef State = C.getState();
1713
1714  llvm::SmallVector<SymbolRef, 2> LeakedSyms;
1715
1716  const StreamMapTy &Map = State->get<StreamMap>();
1717  for (const auto &I : Map) {
1718    SymbolRef Sym = I.first;
1719    const StreamState &SS = I.second;
1720    if (!SymReaper.isDead(Sym))
1721      continue;
1722    if (SS.isOpened())
1723      LeakedSyms.push_back(Sym);
1724    State = State->remove<StreamMap>(Sym);
1725  }
1726
1727  ExplodedNode *N = C.getPredecessor();
1728  if (!LeakedSyms.empty())
1729    N = reportLeaks(LeakedSyms, C, N);
1730
1731  C.addTransition(State, N);
1732}
1733
1734ProgramStateRef StreamChecker::checkPointerEscape(
1735    ProgramStateRef State, const InvalidatedSymbols &Escaped,
1736    const CallEvent *Call, PointerEscapeKind Kind) const {
1737  // Check for file-handling system call that is not handled by the checker.
1738  // FIXME: The checker should be updated to handle all system calls that take
1739  // 'FILE*' argument. These are now ignored.
1740  if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader())
1741    return State;
1742
1743  for (SymbolRef Sym : Escaped) {
1744    // The symbol escaped.
1745    // From now the stream can be manipulated in unknown way to the checker,
1746    // it is not possible to handle it any more.
1747    // Optimistically, assume that the corresponding file handle will be closed
1748    // somewhere else.
1749    // Remove symbol from state so the following stream calls on this symbol are
1750    // not handled by the checker.
1751    State = State->remove<StreamMap>(Sym);
1752  }
1753  return State;
1754}
1755
1756//===----------------------------------------------------------------------===//
1757// Checker registration.
1758//===----------------------------------------------------------------------===//
1759
1760void ento::registerStreamChecker(CheckerManager &Mgr) {
1761  Mgr.registerChecker<StreamChecker>();
1762}
1763
1764bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
1765  return true;
1766}
1767
1768void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
1769  auto *Checker = Mgr.getChecker<StreamChecker>();
1770  Checker->TestMode = true;
1771}
1772
1773bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
1774  return true;
1775}
1776