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