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