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