xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp (revision 47df664c7acfd8abd082c0252d1793182c92dc3d)
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 
27 using namespace clang;
28 using namespace ento;
29 using namespace std::placeholders;
30 
31 //===----------------------------------------------------------------------===//
32 // Definition of state data structures.
33 //===----------------------------------------------------------------------===//
34 
35 namespace {
36 
37 struct 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.
45 struct 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 
83 const StreamErrorState ErrorNone{true, false, false};
84 const StreamErrorState ErrorFEof{false, true, false};
85 const StreamErrorState ErrorFError{false, false, true};
86 
87 /// Full state information about a stream pointer.
88 struct 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 
162 namespace {
163 
164 class StreamChecker;
165 using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
166                                    const CallEvent &, CheckerContext &)>;
167 
168 using ArgNoTy = unsigned int;
169 static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
170 
171 struct 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.
179 SVal 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.
186 DefinedSVal 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 
195 ProgramStateRef 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 
204 ProgramStateRef 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 
211 class 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 
225 public:
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 
239 private:
240   CallDescriptionMap<FnDescription> FnDescriptions = {
241       {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
242       {{{"freopen"}, 3},
243        {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
244       {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
245       {{{"fclose"}, 1},
246        {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
247       {{{"fread"}, 4},
248        {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
249         std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
250       {{{"fwrite"}, 4},
251        {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
252         std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
253       {{{"fgetc"}, 1},
254        {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
255         std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
256       {{{"fgets"}, 3},
257        {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
258         std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}},
259       {{{"fputc"}, 2},
260        {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
261         std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
262       {{{"fputs"}, 2},
263        {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
264         std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}},
265       {{{"fseek"}, 3},
266        {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
267       {{{"ftell"}, 1},
268        {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
269       {{{"rewind"}, 1},
270        {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
271       {{{"fgetpos"}, 2},
272        {&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}},
273       {{{"fsetpos"}, 2},
274        {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
275       {{{"clearerr"}, 1},
276        {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
277       {{{"feof"}, 1},
278        {&StreamChecker::preDefault,
279         std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
280         0}},
281       {{{"ferror"}, 1},
282        {&StreamChecker::preDefault,
283         std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
284         0}},
285       {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}},
286   };
287 
288   CallDescriptionMap<FnDescription> FnTestDescriptions = {
289       {{{"StreamTesterChecker_make_feof_stream"}, 1},
290        {nullptr,
291         std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof),
292         0}},
293       {{{"StreamTesterChecker_make_ferror_stream"}, 1},
294        {nullptr,
295         std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
296                   ErrorFError),
297         0}},
298   };
299 
300   /// Expanded value of EOF, empty before initialization.
301   mutable std::optional<int> EofVal;
302   /// Expanded value of SEEK_SET, 0 if not found.
303   mutable int SeekSetVal = 0;
304   /// Expanded value of SEEK_CUR, 1 if not found.
305   mutable int SeekCurVal = 1;
306   /// Expanded value of SEEK_END, 2 if not found.
307   mutable int SeekEndVal = 2;
308 
309   void evalFopen(const FnDescription *Desc, const CallEvent &Call,
310                  CheckerContext &C) const;
311 
312   void preFreopen(const FnDescription *Desc, const CallEvent &Call,
313                   CheckerContext &C) const;
314   void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
315                    CheckerContext &C) const;
316 
317   void evalFclose(const FnDescription *Desc, const CallEvent &Call,
318                   CheckerContext &C) const;
319 
320   void preReadWrite(const FnDescription *Desc, const CallEvent &Call,
321                     CheckerContext &C, bool IsRead) const;
322 
323   void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
324                        CheckerContext &C, bool IsFread) const;
325 
326   void evalFgetx(const FnDescription *Desc, const CallEvent &Call,
327                  CheckerContext &C, bool SingleChar) const;
328 
329   void evalFputx(const FnDescription *Desc, const CallEvent &Call,
330                  CheckerContext &C, bool IsSingleChar) const;
331 
332   void preFseek(const FnDescription *Desc, const CallEvent &Call,
333                 CheckerContext &C) const;
334   void evalFseek(const FnDescription *Desc, const CallEvent &Call,
335                  CheckerContext &C) const;
336 
337   void evalFgetpos(const FnDescription *Desc, const CallEvent &Call,
338                    CheckerContext &C) const;
339 
340   void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
341                    CheckerContext &C) const;
342 
343   void evalFtell(const FnDescription *Desc, const CallEvent &Call,
344                  CheckerContext &C) const;
345 
346   void evalRewind(const FnDescription *Desc, const CallEvent &Call,
347                   CheckerContext &C) const;
348 
349   void preDefault(const FnDescription *Desc, const CallEvent &Call,
350                   CheckerContext &C) const;
351 
352   void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
353                     CheckerContext &C) const;
354 
355   void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
356                       CheckerContext &C,
357                       const StreamErrorState &ErrorKind) const;
358 
359   void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
360                          CheckerContext &C,
361                          const StreamErrorState &ErrorKind) const;
362 
363   /// Check that the stream (in StreamVal) is not NULL.
364   /// If it can only be NULL a fatal error is emitted and nullptr returned.
365   /// Otherwise the return value is a new state where the stream is constrained
366   /// to be non-null.
367   ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
368                                       CheckerContext &C,
369                                       ProgramStateRef State) const;
370 
371   /// Check that the stream is the opened state.
372   /// If the stream is known to be not opened an error is generated
373   /// and nullptr returned, otherwise the original state is returned.
374   ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,
375                                      ProgramStateRef State) const;
376 
377   /// Check that the stream has not an invalid ("indeterminate") file position,
378   /// generate warning for it.
379   /// (EOF is not an invalid position.)
380   /// The returned state can be nullptr if a fatal error was generated.
381   /// It can return non-null state if the stream has not an invalid position or
382   /// there is execution path with non-invalid position.
383   ProgramStateRef
384   ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,
385                                     ProgramStateRef State) const;
386 
387   /// Check the legality of the 'whence' argument of 'fseek'.
388   /// Generate error and return nullptr if it is found to be illegal.
389   /// Otherwise returns the state.
390   /// (State is not changed here because the "whence" value is already known.)
391   ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
392                                            ProgramStateRef State) const;
393 
394   /// Generate warning about stream in EOF state.
395   /// There will be always a state transition into the passed State,
396   /// by the new non-fatal error node or (if failed) a normal transition,
397   /// to ensure uniform handling.
398   void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
399                          ProgramStateRef State) const;
400 
401   /// Emit resource leak warnings for the given symbols.
402   /// Createn a non-fatal error node for these, and returns it (if any warnings
403   /// were generated). Return value is non-null.
404   ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
405                             CheckerContext &C, ExplodedNode *Pred) const;
406 
407   /// Find the description data of the function called by a call event.
408   /// Returns nullptr if no function is recognized.
409   const FnDescription *lookupFn(const CallEvent &Call) const {
410     // Recognize "global C functions" with only integral or pointer arguments
411     // (and matching name) as stream functions.
412     if (!Call.isGlobalCFunction())
413       return nullptr;
414     for (auto *P : Call.parameters()) {
415       QualType T = P->getType();
416       if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
417         return nullptr;
418     }
419 
420     return FnDescriptions.lookup(Call);
421   }
422 
423   /// Generate a message for BugReporterVisitor if the stored symbol is
424   /// marked as interesting by the actual bug report.
425   const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym,
426                                   const std::string &Message) const {
427     return C.getNoteTag([this, StreamSym,
428                          Message](PathSensitiveBugReport &BR) -> std::string {
429       if (BR.isInteresting(StreamSym) && &BR.getBugType() == &BT_ResourceLeak)
430         return Message;
431       return "";
432     });
433   }
434 
435   const NoteTag *constructSetEofNoteTag(CheckerContext &C,
436                                         SymbolRef StreamSym) const {
437     return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
438       if (!BR.isInteresting(StreamSym) ||
439           &BR.getBugType() != this->getBT_StreamEof())
440         return "";
441 
442       BR.markNotInteresting(StreamSym);
443 
444       return "Assuming stream reaches end-of-file here";
445     });
446   }
447 
448   void initMacroValues(CheckerContext &C) const {
449     if (EofVal)
450       return;
451 
452     if (const std::optional<int> OptInt =
453             tryExpandAsInteger("EOF", C.getPreprocessor()))
454       EofVal = *OptInt;
455     else
456       EofVal = -1;
457     if (const std::optional<int> OptInt =
458             tryExpandAsInteger("SEEK_SET", C.getPreprocessor()))
459       SeekSetVal = *OptInt;
460     if (const std::optional<int> OptInt =
461             tryExpandAsInteger("SEEK_END", C.getPreprocessor()))
462       SeekEndVal = *OptInt;
463     if (const std::optional<int> OptInt =
464             tryExpandAsInteger("SEEK_CUR", C.getPreprocessor()))
465       SeekCurVal = *OptInt;
466   }
467 
468   /// Searches for the ExplodedNode where the file descriptor was acquired for
469   /// StreamSym.
470   static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
471                                                 SymbolRef StreamSym,
472                                                 CheckerContext &C);
473 };
474 
475 } // end anonymous namespace
476 
477 // This map holds the state of a stream.
478 // The stream is identified with a SymbolRef that is created when a stream
479 // opening function is modeled by the checker.
480 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
481 
482 inline void assertStreamStateOpened(const StreamState *SS) {
483   assert(SS->isOpened() && "Stream is expected to be opened");
484 }
485 
486 const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
487                                                       SymbolRef StreamSym,
488                                                       CheckerContext &C) {
489   ProgramStateRef State = N->getState();
490   // When bug type is resource leak, exploded node N may not have state info
491   // for leaked file descriptor, but predecessor should have it.
492   if (!State->get<StreamMap>(StreamSym))
493     N = N->getFirstPred();
494 
495   const ExplodedNode *Pred = N;
496   while (N) {
497     State = N->getState();
498     if (!State->get<StreamMap>(StreamSym))
499       return Pred;
500     Pred = N;
501     N = N->getFirstPred();
502   }
503 
504   return nullptr;
505 }
506 
507 //===----------------------------------------------------------------------===//
508 // Methods of StreamChecker.
509 //===----------------------------------------------------------------------===//
510 
511 void StreamChecker::checkPreCall(const CallEvent &Call,
512                                  CheckerContext &C) const {
513   initMacroValues(C);
514 
515   const FnDescription *Desc = lookupFn(Call);
516   if (!Desc || !Desc->PreFn)
517     return;
518 
519   Desc->PreFn(this, Desc, Call, C);
520 }
521 
522 bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
523   const FnDescription *Desc = lookupFn(Call);
524   if (!Desc && TestMode)
525     Desc = FnTestDescriptions.lookup(Call);
526   if (!Desc || !Desc->EvalFn)
527     return false;
528 
529   Desc->EvalFn(this, Desc, Call, C);
530 
531   return C.isDifferent();
532 }
533 
534 void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
535                               CheckerContext &C) const {
536   ProgramStateRef State = C.getState();
537   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
538   if (!CE)
539     return;
540 
541   DefinedSVal RetVal = makeRetVal(C, CE);
542   SymbolRef RetSym = RetVal.getAsSymbol();
543   assert(RetSym && "RetVal must be a symbol here.");
544 
545   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
546 
547   // Bifurcate the state into two: one with a valid FILE* pointer, the other
548   // with a NULL.
549   ProgramStateRef StateNotNull, StateNull;
550   std::tie(StateNotNull, StateNull) =
551       C.getConstraintManager().assumeDual(State, RetVal);
552 
553   StateNotNull =
554       StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
555   StateNull =
556       StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
557 
558   C.addTransition(StateNotNull,
559                   constructNoteTag(C, RetSym, "Stream opened here"));
560   C.addTransition(StateNull);
561 }
562 
563 void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
564                                CheckerContext &C) const {
565   // Do not allow NULL as passed stream pointer but allow a closed stream.
566   ProgramStateRef State = C.getState();
567   State = ensureStreamNonNull(getStreamArg(Desc, Call),
568                               Call.getArgExpr(Desc->StreamArgNo), C, State);
569   if (!State)
570     return;
571 
572   C.addTransition(State);
573 }
574 
575 void StreamChecker::evalFreopen(const FnDescription *Desc,
576                                 const CallEvent &Call,
577                                 CheckerContext &C) const {
578   ProgramStateRef State = C.getState();
579 
580   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
581   if (!CE)
582     return;
583 
584   std::optional<DefinedSVal> StreamVal =
585       getStreamArg(Desc, Call).getAs<DefinedSVal>();
586   if (!StreamVal)
587     return;
588 
589   SymbolRef StreamSym = StreamVal->getAsSymbol();
590   // Do not care about concrete values for stream ("(FILE *)0x12345"?).
591   // FIXME: Can be stdin, stdout, stderr such values?
592   if (!StreamSym)
593     return;
594 
595   // Do not handle untracked stream. It is probably escaped.
596   if (!State->get<StreamMap>(StreamSym))
597     return;
598 
599   // Generate state for non-failed case.
600   // Return value is the passed stream pointer.
601   // According to the documentations, the stream is closed first
602   // but any close error is ignored. The state changes to (or remains) opened.
603   ProgramStateRef StateRetNotNull =
604       State->BindExpr(CE, C.getLocationContext(), *StreamVal);
605   // Generate state for NULL return value.
606   // Stream switches to OpenFailed state.
607   ProgramStateRef StateRetNull =
608       State->BindExpr(CE, C.getLocationContext(),
609                       C.getSValBuilder().makeNullWithType(CE->getType()));
610 
611   StateRetNotNull =
612       StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
613   StateRetNull =
614       StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
615 
616   C.addTransition(StateRetNotNull,
617                   constructNoteTag(C, StreamSym, "Stream reopened here"));
618   C.addTransition(StateRetNull);
619 }
620 
621 void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
622                                CheckerContext &C) const {
623   ProgramStateRef State = C.getState();
624   SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
625   if (!Sym)
626     return;
627 
628   const StreamState *SS = State->get<StreamMap>(Sym);
629   if (!SS)
630     return;
631 
632   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
633   if (!CE)
634     return;
635 
636   assertStreamStateOpened(SS);
637 
638   // Close the File Descriptor.
639   // Regardless if the close fails or not, stream becomes "closed"
640   // and can not be used any more.
641   State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
642 
643   // Return 0 on success, EOF on failure.
644   SValBuilder &SVB = C.getSValBuilder();
645   ProgramStateRef StateSuccess = State->BindExpr(
646       CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy));
647   ProgramStateRef StateFailure =
648       State->BindExpr(CE, C.getLocationContext(),
649                       SVB.makeIntVal(*EofVal, C.getASTContext().IntTy));
650 
651   C.addTransition(StateSuccess);
652   C.addTransition(StateFailure);
653 }
654 
655 void StreamChecker::preReadWrite(const FnDescription *Desc,
656                                  const CallEvent &Call, CheckerContext &C,
657                                  bool IsRead) const {
658   ProgramStateRef State = C.getState();
659   SVal StreamVal = getStreamArg(Desc, Call);
660   State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
661                               State);
662   if (!State)
663     return;
664   State = ensureStreamOpened(StreamVal, C, State);
665   if (!State)
666     return;
667   State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
668   if (!State)
669     return;
670 
671   if (!IsRead) {
672     C.addTransition(State);
673     return;
674   }
675 
676   SymbolRef Sym = StreamVal.getAsSymbol();
677   if (Sym && State->get<StreamMap>(Sym)) {
678     const StreamState *SS = State->get<StreamMap>(Sym);
679     if (SS->ErrorState & ErrorFEof)
680       reportFEofWarning(Sym, C, State);
681   } else {
682     C.addTransition(State);
683   }
684 }
685 
686 void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
687                                     const CallEvent &Call, CheckerContext &C,
688                                     bool IsFread) const {
689   ProgramStateRef State = C.getState();
690   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
691   if (!StreamSym)
692     return;
693 
694   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
695   if (!CE)
696     return;
697 
698   std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
699   if (!SizeVal)
700     return;
701   std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
702   if (!NMembVal)
703     return;
704 
705   const StreamState *OldSS = State->get<StreamMap>(StreamSym);
706   if (!OldSS)
707     return;
708 
709   assertStreamStateOpened(OldSS);
710 
711   // C'99 standard, §7.19.8.1.3, the return value of fread:
712   // The fread function returns the number of elements successfully read, which
713   // may be less than nmemb if a read error or end-of-file is encountered. If
714   // size or nmemb is zero, fread returns zero and the contents of the array and
715   // the state of the stream remain unchanged.
716 
717   if (State->isNull(*SizeVal).isConstrainedTrue() ||
718       State->isNull(*NMembVal).isConstrainedTrue()) {
719     // This is the "size or nmemb is zero" case.
720     // Just return 0, do nothing more (not clear the error flags).
721     State = bindInt(0, State, C, CE);
722     C.addTransition(State);
723     return;
724   }
725 
726   // Generate a transition for the success state.
727   // If we know the state to be FEOF at fread, do not add a success state.
728   if (!IsFread || (OldSS->ErrorState != ErrorFEof)) {
729     ProgramStateRef StateNotFailed =
730         State->BindExpr(CE, C.getLocationContext(), *NMembVal);
731     StateNotFailed =
732         StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
733     C.addTransition(StateNotFailed);
734   }
735 
736   // Add transition for the failed state.
737   NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
738   ProgramStateRef StateFailed =
739       State->BindExpr(CE, C.getLocationContext(), RetVal);
740   SValBuilder &SVB = C.getSValBuilder();
741   auto Cond =
742       SVB.evalBinOpNN(State, BO_LT, RetVal, *NMembVal, SVB.getConditionType())
743           .getAs<DefinedOrUnknownSVal>();
744   if (!Cond)
745     return;
746   StateFailed = StateFailed->assume(*Cond, true);
747   if (!StateFailed)
748     return;
749 
750   StreamErrorState NewES;
751   if (IsFread)
752     NewES =
753         (OldSS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError;
754   else
755     NewES = ErrorFError;
756   // If a (non-EOF) error occurs, the resulting value of the file position
757   // indicator for the stream is indeterminate.
758   StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
759   StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
760   if (IsFread && OldSS->ErrorState != ErrorFEof)
761     C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
762   else
763     C.addTransition(StateFailed);
764 }
765 
766 void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call,
767                               CheckerContext &C, bool SingleChar) const {
768   ProgramStateRef State = C.getState();
769   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
770   if (!StreamSym)
771     return;
772 
773   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
774   if (!CE)
775     return;
776 
777   const StreamState *OldSS = State->get<StreamMap>(StreamSym);
778   if (!OldSS)
779     return;
780 
781   assertStreamStateOpened(OldSS);
782 
783   // `fgetc` returns the read character on success, otherwise returns EOF.
784   // `fgets` returns the read buffer address on success, otherwise returns NULL.
785 
786   if (OldSS->ErrorState != ErrorFEof) {
787     if (SingleChar) {
788       // Generate a transition for the success state of `fgetc`.
789       NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
790       ProgramStateRef StateNotFailed =
791           State->BindExpr(CE, C.getLocationContext(), RetVal);
792       SValBuilder &SVB = C.getSValBuilder();
793       ASTContext &ASTC = C.getASTContext();
794       // The returned 'unsigned char' of `fgetc` is converted to 'int',
795       // so we need to check if it is in range [0, 255].
796       auto CondLow =
797           SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy),
798                         SVB.getConditionType())
799               .getAs<DefinedOrUnknownSVal>();
800       auto CondHigh =
801           SVB.evalBinOp(State, BO_LE, RetVal,
802                         SVB.makeIntVal(SVB.getBasicValueFactory()
803                                            .getMaxValue(ASTC.UnsignedCharTy)
804                                            .getLimitedValue(),
805                                        ASTC.IntTy),
806                         SVB.getConditionType())
807               .getAs<DefinedOrUnknownSVal>();
808       if (!CondLow || !CondHigh)
809         return;
810       StateNotFailed = StateNotFailed->assume(*CondLow, true);
811       if (!StateNotFailed)
812         return;
813       StateNotFailed = StateNotFailed->assume(*CondHigh, true);
814       if (!StateNotFailed)
815         return;
816       C.addTransition(StateNotFailed);
817     } else {
818       // Generate a transition for the success state of `fgets`.
819       std::optional<DefinedSVal> GetBuf =
820           Call.getArgSVal(0).getAs<DefinedSVal>();
821       if (!GetBuf)
822         return;
823       ProgramStateRef StateNotFailed =
824           State->BindExpr(CE, C.getLocationContext(), *GetBuf);
825       StateNotFailed = StateNotFailed->set<StreamMap>(
826           StreamSym, StreamState::getOpened(Desc));
827       C.addTransition(StateNotFailed);
828     }
829   }
830 
831   // Add transition for the failed state.
832   ProgramStateRef StateFailed;
833   if (SingleChar)
834     StateFailed = bindInt(*EofVal, State, C, CE);
835   else
836     StateFailed =
837         State->BindExpr(CE, C.getLocationContext(),
838                         C.getSValBuilder().makeNullWithType(CE->getType()));
839 
840   // If a (non-EOF) error occurs, the resulting value of the file position
841   // indicator for the stream is indeterminate.
842   StreamErrorState NewES =
843       OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError;
844   StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
845   StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
846   if (OldSS->ErrorState != ErrorFEof)
847     C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
848   else
849     C.addTransition(StateFailed);
850 }
851 
852 void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call,
853                               CheckerContext &C, bool IsSingleChar) const {
854   ProgramStateRef State = C.getState();
855   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
856   if (!StreamSym)
857     return;
858 
859   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
860   if (!CE)
861     return;
862 
863   const StreamState *OldSS = State->get<StreamMap>(StreamSym);
864   if (!OldSS)
865     return;
866 
867   assertStreamStateOpened(OldSS);
868 
869   // `fputc` returns the written character on success, otherwise returns EOF.
870   // `fputs` returns a non negative value on sucecess, otherwise returns EOF.
871 
872   if (IsSingleChar) {
873     // Generate a transition for the success state of `fputc`.
874     std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
875     if (!PutVal)
876       return;
877     ProgramStateRef StateNotFailed =
878         State->BindExpr(CE, C.getLocationContext(), *PutVal);
879     StateNotFailed =
880         StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
881     C.addTransition(StateNotFailed);
882   } else {
883     // Generate a transition for the success state of `fputs`.
884     NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
885     ProgramStateRef StateNotFailed =
886         State->BindExpr(CE, C.getLocationContext(), RetVal);
887     SValBuilder &SVB = C.getSValBuilder();
888     auto &ASTC = C.getASTContext();
889     auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy),
890                               SVB.getConditionType())
891                     .getAs<DefinedOrUnknownSVal>();
892     if (!Cond)
893       return;
894     StateNotFailed = StateNotFailed->assume(*Cond, true);
895     if (!StateNotFailed)
896       return;
897     StateNotFailed =
898         StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
899     C.addTransition(StateNotFailed);
900   }
901 
902   // Add transition for the failed state. The resulting value of the file
903   // position indicator for the stream is indeterminate.
904   ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
905   StreamState NewSS = StreamState::getOpened(Desc, ErrorFError, true);
906   StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
907   C.addTransition(StateFailed);
908 }
909 
910 void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
911                              CheckerContext &C) const {
912   ProgramStateRef State = C.getState();
913   SVal StreamVal = getStreamArg(Desc, Call);
914   State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
915                               State);
916   if (!State)
917     return;
918   State = ensureStreamOpened(StreamVal, C, State);
919   if (!State)
920     return;
921   State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
922   if (!State)
923     return;
924 
925   C.addTransition(State);
926 }
927 
928 void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
929                               CheckerContext &C) const {
930   ProgramStateRef State = C.getState();
931   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
932   if (!StreamSym)
933     return;
934 
935   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
936   if (!CE)
937     return;
938 
939   // Ignore the call if the stream is not tracked.
940   if (!State->get<StreamMap>(StreamSym))
941     return;
942 
943   const llvm::APSInt *PosV =
944       C.getSValBuilder().getKnownValue(State, Call.getArgSVal(1));
945   const llvm::APSInt *WhenceV =
946       C.getSValBuilder().getKnownValue(State, Call.getArgSVal(2));
947 
948   DefinedSVal RetVal = makeRetVal(C, CE);
949 
950   // Make expression result.
951   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
952 
953   // Bifurcate the state into failed and non-failed.
954   // Return zero on success, nonzero on error.
955   ProgramStateRef StateNotFailed, StateFailed;
956   std::tie(StateFailed, StateNotFailed) =
957       C.getConstraintManager().assumeDual(State, RetVal);
958 
959   // Reset the state to opened with no error.
960   StateNotFailed =
961       StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
962   // We get error.
963   // It is possible that fseek fails but sets none of the error flags.
964   // If fseek failed, assume that the file position becomes indeterminate in any
965   // case.
966   StreamErrorState NewErrS = ErrorNone | ErrorFError;
967   // Setting the position to start of file never produces EOF error.
968   if (!(PosV && *PosV == 0 && WhenceV && *WhenceV == SeekSetVal))
969     NewErrS = NewErrS | ErrorFEof;
970   StateFailed = StateFailed->set<StreamMap>(
971       StreamSym, StreamState::getOpened(Desc, NewErrS, true));
972 
973   C.addTransition(StateNotFailed);
974   C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
975 }
976 
977 void StreamChecker::evalFgetpos(const FnDescription *Desc,
978                                 const CallEvent &Call,
979                                 CheckerContext &C) const {
980   ProgramStateRef State = C.getState();
981   SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
982   if (!Sym)
983     return;
984 
985   // Do not evaluate if stream is not found.
986   if (!State->get<StreamMap>(Sym))
987     return;
988 
989   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
990   if (!CE)
991     return;
992 
993   DefinedSVal RetVal = makeRetVal(C, CE);
994   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
995   ProgramStateRef StateNotFailed, StateFailed;
996   std::tie(StateFailed, StateNotFailed) =
997       C.getConstraintManager().assumeDual(State, RetVal);
998 
999   // This function does not affect the stream state.
1000   // Still we add success and failure state with the appropriate return value.
1001   // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
1002   C.addTransition(StateNotFailed);
1003   C.addTransition(StateFailed);
1004 }
1005 
1006 void StreamChecker::evalFsetpos(const FnDescription *Desc,
1007                                 const CallEvent &Call,
1008                                 CheckerContext &C) const {
1009   ProgramStateRef State = C.getState();
1010   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1011   if (!StreamSym)
1012     return;
1013 
1014   const StreamState *SS = State->get<StreamMap>(StreamSym);
1015   if (!SS)
1016     return;
1017 
1018   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1019   if (!CE)
1020     return;
1021 
1022   assertStreamStateOpened(SS);
1023 
1024   DefinedSVal RetVal = makeRetVal(C, CE);
1025   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
1026   ProgramStateRef StateNotFailed, StateFailed;
1027   std::tie(StateFailed, StateNotFailed) =
1028       C.getConstraintManager().assumeDual(State, RetVal);
1029 
1030   StateNotFailed = StateNotFailed->set<StreamMap>(
1031       StreamSym, StreamState::getOpened(Desc, ErrorNone, false));
1032 
1033   // At failure ferror could be set.
1034   // The standards do not tell what happens with the file position at failure.
1035   // But we can assume that it is dangerous to make a next I/O operation after
1036   // the position was not set correctly (similar to 'fseek').
1037   StateFailed = StateFailed->set<StreamMap>(
1038       StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
1039 
1040   C.addTransition(StateNotFailed);
1041   C.addTransition(StateFailed);
1042 }
1043 
1044 void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
1045                               CheckerContext &C) const {
1046   ProgramStateRef State = C.getState();
1047   SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
1048   if (!Sym)
1049     return;
1050 
1051   if (!State->get<StreamMap>(Sym))
1052     return;
1053 
1054   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1055   if (!CE)
1056     return;
1057 
1058   SValBuilder &SVB = C.getSValBuilder();
1059   NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
1060   ProgramStateRef StateNotFailed =
1061       State->BindExpr(CE, C.getLocationContext(), RetVal);
1062   auto Cond = SVB.evalBinOp(State, BO_GE, RetVal,
1063                             SVB.makeZeroVal(C.getASTContext().LongTy),
1064                             SVB.getConditionType())
1065                   .getAs<DefinedOrUnknownSVal>();
1066   if (!Cond)
1067     return;
1068   StateNotFailed = StateNotFailed->assume(*Cond, true);
1069   if (!StateNotFailed)
1070     return;
1071 
1072   ProgramStateRef StateFailed = State->BindExpr(
1073       CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy));
1074 
1075   C.addTransition(StateNotFailed);
1076   C.addTransition(StateFailed);
1077 }
1078 
1079 void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call,
1080                                CheckerContext &C) const {
1081   ProgramStateRef State = C.getState();
1082   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1083   if (!StreamSym)
1084     return;
1085 
1086   const StreamState *SS = State->get<StreamMap>(StreamSym);
1087   if (!SS)
1088     return;
1089 
1090   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1091   if (!CE)
1092     return;
1093 
1094   assertStreamStateOpened(SS);
1095 
1096   State = State->set<StreamMap>(StreamSym,
1097                                 StreamState::getOpened(Desc, ErrorNone, false));
1098 
1099   C.addTransition(State);
1100 }
1101 
1102 void StreamChecker::evalClearerr(const FnDescription *Desc,
1103                                  const CallEvent &Call,
1104                                  CheckerContext &C) const {
1105   ProgramStateRef State = C.getState();
1106   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1107   if (!StreamSym)
1108     return;
1109 
1110   const StreamState *SS = State->get<StreamMap>(StreamSym);
1111   if (!SS)
1112     return;
1113 
1114   assertStreamStateOpened(SS);
1115 
1116   // FilePositionIndeterminate is not cleared.
1117   State = State->set<StreamMap>(
1118       StreamSym,
1119       StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate));
1120   C.addTransition(State);
1121 }
1122 
1123 void StreamChecker::evalFeofFerror(const FnDescription *Desc,
1124                                    const CallEvent &Call, CheckerContext &C,
1125                                    const StreamErrorState &ErrorKind) const {
1126   ProgramStateRef State = C.getState();
1127   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1128   if (!StreamSym)
1129     return;
1130 
1131   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1132   if (!CE)
1133     return;
1134 
1135   const StreamState *SS = State->get<StreamMap>(StreamSym);
1136   if (!SS)
1137     return;
1138 
1139   assertStreamStateOpened(SS);
1140 
1141   if (SS->ErrorState & ErrorKind) {
1142     // Execution path with error of ErrorKind.
1143     // Function returns true.
1144     // From now on it is the only one error state.
1145     ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE);
1146     C.addTransition(TrueState->set<StreamMap>(
1147         StreamSym, StreamState::getOpened(Desc, ErrorKind,
1148                                           SS->FilePositionIndeterminate &&
1149                                               !ErrorKind.isFEof())));
1150   }
1151   if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) {
1152     // Execution path(s) with ErrorKind not set.
1153     // Function returns false.
1154     // New error state is everything before minus ErrorKind.
1155     ProgramStateRef FalseState = bindInt(0, State, C, CE);
1156     C.addTransition(FalseState->set<StreamMap>(
1157         StreamSym,
1158         StreamState::getOpened(
1159             Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof())));
1160   }
1161 }
1162 
1163 void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
1164                                CheckerContext &C) const {
1165   ProgramStateRef State = C.getState();
1166   SVal StreamVal = getStreamArg(Desc, Call);
1167   State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1168                               State);
1169   if (!State)
1170     return;
1171   State = ensureStreamOpened(StreamVal, C, State);
1172   if (!State)
1173     return;
1174 
1175   C.addTransition(State);
1176 }
1177 
1178 void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
1179                                       const CallEvent &Call, CheckerContext &C,
1180                                       const StreamErrorState &ErrorKind) const {
1181   ProgramStateRef State = C.getState();
1182   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1183   assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
1184   const StreamState *SS = State->get<StreamMap>(StreamSym);
1185   assert(SS && "Stream should be tracked by the checker.");
1186   State = State->set<StreamMap>(
1187       StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind));
1188   C.addTransition(State);
1189 }
1190 
1191 ProgramStateRef
1192 StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
1193                                    CheckerContext &C,
1194                                    ProgramStateRef State) const {
1195   auto Stream = StreamVal.getAs<DefinedSVal>();
1196   if (!Stream)
1197     return State;
1198 
1199   ConstraintManager &CM = C.getConstraintManager();
1200 
1201   ProgramStateRef StateNotNull, StateNull;
1202   std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream);
1203 
1204   if (!StateNotNull && StateNull) {
1205     if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
1206       auto R = std::make_unique<PathSensitiveBugReport>(
1207           BT_FileNull, "Stream pointer might be NULL.", N);
1208       if (StreamE)
1209         bugreporter::trackExpressionValue(N, StreamE, *R);
1210       C.emitReport(std::move(R));
1211     }
1212     return nullptr;
1213   }
1214 
1215   return StateNotNull;
1216 }
1217 
1218 ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
1219                                                   CheckerContext &C,
1220                                                   ProgramStateRef State) const {
1221   SymbolRef Sym = StreamVal.getAsSymbol();
1222   if (!Sym)
1223     return State;
1224 
1225   const StreamState *SS = State->get<StreamMap>(Sym);
1226   if (!SS)
1227     return State;
1228 
1229   if (SS->isClosed()) {
1230     // Using a stream pointer after 'fclose' causes undefined behavior
1231     // according to cppreference.com .
1232     ExplodedNode *N = C.generateErrorNode();
1233     if (N) {
1234       C.emitReport(std::make_unique<PathSensitiveBugReport>(
1235           BT_UseAfterClose,
1236           "Stream might be already closed. Causes undefined behaviour.", N));
1237       return nullptr;
1238     }
1239 
1240     return State;
1241   }
1242 
1243   if (SS->isOpenFailed()) {
1244     // Using a stream that has failed to open is likely to cause problems.
1245     // This should usually not occur because stream pointer is NULL.
1246     // But freopen can cause a state when stream pointer remains non-null but
1247     // failed to open.
1248     ExplodedNode *N = C.generateErrorNode();
1249     if (N) {
1250       C.emitReport(std::make_unique<PathSensitiveBugReport>(
1251           BT_UseAfterOpenFailed,
1252           "Stream might be invalid after "
1253           "(re-)opening it has failed. "
1254           "Can cause undefined behaviour.",
1255           N));
1256       return nullptr;
1257     }
1258   }
1259 
1260   return State;
1261 }
1262 
1263 ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
1264     SVal StreamVal, CheckerContext &C, ProgramStateRef State) const {
1265   static const char *BugMessage =
1266       "File position of the stream might be 'indeterminate' "
1267       "after a failed operation. "
1268       "Can cause undefined behavior.";
1269 
1270   SymbolRef Sym = StreamVal.getAsSymbol();
1271   if (!Sym)
1272     return State;
1273 
1274   const StreamState *SS = State->get<StreamMap>(Sym);
1275   if (!SS)
1276     return State;
1277 
1278   assert(SS->isOpened() && "First ensure that stream is opened.");
1279 
1280   if (SS->FilePositionIndeterminate) {
1281     if (SS->ErrorState & ErrorFEof) {
1282       // The error is unknown but may be FEOF.
1283       // Continue analysis with the FEOF error state.
1284       // Report warning because the other possible error states.
1285       ExplodedNode *N = C.generateNonFatalErrorNode(State);
1286       if (!N)
1287         return nullptr;
1288 
1289       C.emitReport(std::make_unique<PathSensitiveBugReport>(
1290           BT_IndeterminatePosition, BugMessage, N));
1291       return State->set<StreamMap>(
1292           Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));
1293     }
1294 
1295     // Known or unknown error state without FEOF possible.
1296     // Stop analysis, report error.
1297     ExplodedNode *N = C.generateErrorNode(State);
1298     if (N)
1299       C.emitReport(std::make_unique<PathSensitiveBugReport>(
1300           BT_IndeterminatePosition, BugMessage, N));
1301 
1302     return nullptr;
1303   }
1304 
1305   return State;
1306 }
1307 
1308 ProgramStateRef
1309 StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
1310                                         ProgramStateRef State) const {
1311   std::optional<nonloc::ConcreteInt> CI =
1312       WhenceVal.getAs<nonloc::ConcreteInt>();
1313   if (!CI)
1314     return State;
1315 
1316   int64_t X = CI->getValue().getSExtValue();
1317   if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal)
1318     return State;
1319 
1320   if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1321     C.emitReport(std::make_unique<PathSensitiveBugReport>(
1322         BT_IllegalWhence,
1323         "The whence argument to fseek() should be "
1324         "SEEK_SET, SEEK_END, or SEEK_CUR.",
1325         N));
1326     return nullptr;
1327   }
1328 
1329   return State;
1330 }
1331 
1332 void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
1333                                       ProgramStateRef State) const {
1334   if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1335     auto R = std::make_unique<PathSensitiveBugReport>(
1336         BT_StreamEof,
1337         "Read function called when stream is in EOF state. "
1338         "Function has no effect.",
1339         N);
1340     R->markInteresting(StreamSym);
1341     C.emitReport(std::move(R));
1342     return;
1343   }
1344   C.addTransition(State);
1345 }
1346 
1347 ExplodedNode *
1348 StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
1349                            CheckerContext &C, ExplodedNode *Pred) const {
1350   ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);
1351   if (!Err)
1352     return Pred;
1353 
1354   for (SymbolRef LeakSym : LeakedSyms) {
1355     // Resource leaks can result in multiple warning that describe the same kind
1356     // of programming error:
1357     //  void f() {
1358     //    FILE *F = fopen("a.txt");
1359     //    if (rand()) // state split
1360     //      return; // warning
1361     //  } // warning
1362     // While this isn't necessarily true (leaking the same stream could result
1363     // from a different kinds of errors), the reduction in redundant reports
1364     // makes this a worthwhile heuristic.
1365     // FIXME: Add a checker option to turn this uniqueing feature off.
1366     const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
1367     assert(StreamOpenNode && "Could not find place of stream opening.");
1368 
1369     PathDiagnosticLocation LocUsedForUniqueing;
1370     if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics())
1371       LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
1372           StreamStmt, C.getSourceManager(),
1373           StreamOpenNode->getLocationContext());
1374 
1375     std::unique_ptr<PathSensitiveBugReport> R =
1376         std::make_unique<PathSensitiveBugReport>(
1377             BT_ResourceLeak,
1378             "Opened stream never closed. Potential resource leak.", Err,
1379             LocUsedForUniqueing,
1380             StreamOpenNode->getLocationContext()->getDecl());
1381     R->markInteresting(LeakSym);
1382     C.emitReport(std::move(R));
1383   }
1384 
1385   return Err;
1386 }
1387 
1388 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1389                                      CheckerContext &C) const {
1390   ProgramStateRef State = C.getState();
1391 
1392   llvm::SmallVector<SymbolRef, 2> LeakedSyms;
1393 
1394   const StreamMapTy &Map = State->get<StreamMap>();
1395   for (const auto &I : Map) {
1396     SymbolRef Sym = I.first;
1397     const StreamState &SS = I.second;
1398     if (!SymReaper.isDead(Sym))
1399       continue;
1400     if (SS.isOpened())
1401       LeakedSyms.push_back(Sym);
1402     State = State->remove<StreamMap>(Sym);
1403   }
1404 
1405   ExplodedNode *N = C.getPredecessor();
1406   if (!LeakedSyms.empty())
1407     N = reportLeaks(LeakedSyms, C, N);
1408 
1409   C.addTransition(State, N);
1410 }
1411 
1412 ProgramStateRef StreamChecker::checkPointerEscape(
1413     ProgramStateRef State, const InvalidatedSymbols &Escaped,
1414     const CallEvent *Call, PointerEscapeKind Kind) const {
1415   // Check for file-handling system call that is not handled by the checker.
1416   // FIXME: The checker should be updated to handle all system calls that take
1417   // 'FILE*' argument. These are now ignored.
1418   if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader())
1419     return State;
1420 
1421   for (SymbolRef Sym : Escaped) {
1422     // The symbol escaped.
1423     // From now the stream can be manipulated in unknown way to the checker,
1424     // it is not possible to handle it any more.
1425     // Optimistically, assume that the corresponding file handle will be closed
1426     // somewhere else.
1427     // Remove symbol from state so the following stream calls on this symbol are
1428     // not handled by the checker.
1429     State = State->remove<StreamMap>(Sym);
1430   }
1431   return State;
1432 }
1433 
1434 //===----------------------------------------------------------------------===//
1435 // Checker registration.
1436 //===----------------------------------------------------------------------===//
1437 
1438 void ento::registerStreamChecker(CheckerManager &Mgr) {
1439   Mgr.registerChecker<StreamChecker>();
1440 }
1441 
1442 bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
1443   return true;
1444 }
1445 
1446 void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
1447   auto *Checker = Mgr.getChecker<StreamChecker>();
1448   Checker->TestMode = true;
1449 }
1450 
1451 bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
1452   return true;
1453 }
1454