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