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/CallEvent.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
22 
23 using namespace clang;
24 using namespace ento;
25 
26 namespace {
27 
28 struct FnDescription;
29 
30 /// Full state information about a stream pointer.
31 struct StreamState {
32   /// The last file operation called in the stream.
33   const FnDescription *LastOperation;
34 
35   /// State of a stream symbol.
36   /// FIXME: We need maybe an "escaped" state later.
37   enum KindTy {
38     Opened, /// Stream is opened.
39     Closed, /// Closed stream (an invalid stream pointer after it was closed).
40     OpenFailed /// The last open operation has failed.
41   } State;
42 
43   /// The error state of a stream.
44   /// Valid only if the stream is opened.
45   /// It is assumed that feof and ferror flags are never true at the same time.
46   enum ErrorKindTy {
47     /// No error flag is set (or stream is not open).
48     NoError,
49     /// EOF condition (`feof` is true).
50     FEof,
51     /// Other generic (non-EOF) error (`ferror` is true).
52     FError,
53     /// Unknown error flag is set (or none), the meaning depends on the last
54     /// operation.
55     Unknown
56   } ErrorState = NoError;
57 
58   bool isOpened() const { return State == Opened; }
59   bool isClosed() const { return State == Closed; }
60   bool isOpenFailed() const { return State == OpenFailed; }
61 
62   bool isNoError() const {
63     assert(State == Opened && "Error undefined for closed stream.");
64     return ErrorState == NoError;
65   }
66   bool isFEof() const {
67     assert(State == Opened && "Error undefined for closed stream.");
68     return ErrorState == FEof;
69   }
70   bool isFError() const {
71     assert(State == Opened && "Error undefined for closed stream.");
72     return ErrorState == FError;
73   }
74   bool isUnknown() const {
75     assert(State == Opened && "Error undefined for closed stream.");
76     return ErrorState == Unknown;
77   }
78 
79   bool operator==(const StreamState &X) const {
80     // In not opened state error should always NoError.
81     return LastOperation == X.LastOperation && State == X.State &&
82            ErrorState == X.ErrorState;
83   }
84 
85   static StreamState getOpened(const FnDescription *L) {
86     return StreamState{L, Opened};
87   }
88   static StreamState getOpened(const FnDescription *L, ErrorKindTy E) {
89     return StreamState{L, Opened, E};
90   }
91   static StreamState getClosed(const FnDescription *L) {
92     return StreamState{L, Closed};
93   }
94   static StreamState getOpenFailed(const FnDescription *L) {
95     return StreamState{L, OpenFailed};
96   }
97 
98   /// Return if the specified error kind is possible on the stream in the
99   /// current state.
100   /// This depends on the stored `LastOperation` value.
101   /// If the error is not possible returns empty value.
102   /// If the error is possible returns the remaining possible error type
103   /// (after taking out `ErrorKind`). If a single error is possible it will
104   /// return that value, otherwise unknown error.
105   Optional<ErrorKindTy> getRemainingPossibleError(ErrorKindTy ErrorKind) const;
106 
107   void Profile(llvm::FoldingSetNodeID &ID) const {
108     ID.AddPointer(LastOperation);
109     ID.AddInteger(State);
110     ID.AddInteger(ErrorState);
111   }
112 };
113 
114 class StreamChecker;
115 using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
116                                    const CallEvent &, CheckerContext &)>;
117 
118 using ArgNoTy = unsigned int;
119 static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
120 
121 struct FnDescription {
122   FnCheck PreFn;
123   FnCheck EvalFn;
124   ArgNoTy StreamArgNo;
125   // What errors are possible after this operation.
126   // Used only if this operation resulted in Unknown state
127   // (otherwise there is a known single error).
128   // Must contain 2 or 3 elements, or zero.
129   llvm::SmallVector<StreamState::ErrorKindTy, 3> PossibleErrors = {};
130 };
131 
132 Optional<StreamState::ErrorKindTy>
133 StreamState::getRemainingPossibleError(ErrorKindTy ErrorKind) const {
134   assert(ErrorState == Unknown &&
135          "Function to be used only if error is unknown.");
136   llvm::SmallVector<StreamState::ErrorKindTy, 3> NewPossibleErrors;
137   for (StreamState::ErrorKindTy E : LastOperation->PossibleErrors)
138     if (E != ErrorKind)
139       NewPossibleErrors.push_back(E);
140   if (NewPossibleErrors.size() == LastOperation->PossibleErrors.size())
141     return {};
142   if (NewPossibleErrors.size() == 1)
143     return NewPossibleErrors.front();
144   return Unknown;
145 }
146 
147 /// Get the value of the stream argument out of the passed call event.
148 /// The call should contain a function that is described by Desc.
149 SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {
150   assert(Desc && Desc->StreamArgNo != ArgNone &&
151          "Try to get a non-existing stream argument.");
152   return Call.getArgSVal(Desc->StreamArgNo);
153 }
154 
155 /// Create a conjured symbol return value for a call expression.
156 DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) {
157   assert(CE && "Expecting a call expression.");
158 
159   const LocationContext *LCtx = C.getLocationContext();
160   return C.getSValBuilder()
161       .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
162       .castAs<DefinedSVal>();
163 }
164 
165 ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
166                                   const CallExpr *CE) {
167   DefinedSVal RetVal = makeRetVal(C, CE);
168   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
169   State = State->assume(RetVal, true);
170   assert(State && "Assumption on new value should not fail.");
171   return State;
172 }
173 
174 ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
175                         CheckerContext &C, const CallExpr *CE) {
176   State = State->BindExpr(CE, C.getLocationContext(),
177                           C.getSValBuilder().makeIntVal(Value, false));
178   return State;
179 }
180 
181 class StreamChecker
182     : public Checker<check::PreCall, eval::Call, check::DeadSymbols> {
183   mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
184       BT_UseAfterClose, BT_UseAfterOpenFailed, BT_ResourceLeak;
185 
186 public:
187   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
188   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
189   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
190 
191   /// If true, evaluate special testing stream functions.
192   bool TestMode = false;
193 
194 private:
195   CallDescriptionMap<FnDescription> FnDescriptions = {
196       {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone, {}}},
197       {{"freopen", 3},
198        {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2, {}}},
199       {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone, {}}},
200       {{"fclose", 1},
201        {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0, {}}},
202       {{"fread", 4}, {&StreamChecker::preDefault, nullptr, 3, {}}},
203       {{"fwrite", 4}, {&StreamChecker::preDefault, nullptr, 3, {}}},
204       {{"fseek", 3},
205        {&StreamChecker::preFseek,
206         &StreamChecker::evalFseek,
207         0,
208         {StreamState::FEof, StreamState::FError, StreamState::NoError}}},
209       {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0, {}}},
210       {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0, {}}},
211       {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0, {}}},
212       {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0, {}}},
213       {{"clearerr", 1},
214        {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0, {}}},
215       // Note: feof can result in Unknown if at the call there is a
216       // PossibleErrors with all 3 error states (including NoError).
217       // Then if feof is false the remaining error could be FError or NoError.
218       {{"feof", 1},
219        {&StreamChecker::preDefault,
220         &StreamChecker::evalFeofFerror<StreamState::FEof>,
221         0,
222         {StreamState::FError, StreamState::NoError}}},
223       // Note: ferror can result in Unknown if at the call there is a
224       // PossibleErrors with all 3 error states (including NoError).
225       // Then if ferror is false the remaining error could be FEof or NoError.
226       {{"ferror", 1},
227        {&StreamChecker::preDefault,
228         &StreamChecker::evalFeofFerror<StreamState::FError>,
229         0,
230         {StreamState::FEof, StreamState::NoError}}},
231       {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0, {}}},
232   };
233 
234   CallDescriptionMap<FnDescription> FnTestDescriptions = {
235       {{"StreamTesterChecker_make_feof_stream", 1},
236        {nullptr, &StreamChecker::evalSetFeofFerror<StreamState::FEof>, 0}},
237       {{"StreamTesterChecker_make_ferror_stream", 1},
238        {nullptr, &StreamChecker::evalSetFeofFerror<StreamState::FError>, 0}},
239   };
240 
241   void evalFopen(const FnDescription *Desc, const CallEvent &Call,
242                  CheckerContext &C) const;
243 
244   void preFreopen(const FnDescription *Desc, const CallEvent &Call,
245                   CheckerContext &C) const;
246   void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
247                    CheckerContext &C) const;
248 
249   void evalFclose(const FnDescription *Desc, const CallEvent &Call,
250                   CheckerContext &C) const;
251 
252   void preFseek(const FnDescription *Desc, const CallEvent &Call,
253                 CheckerContext &C) const;
254   void evalFseek(const FnDescription *Desc, const CallEvent &Call,
255                  CheckerContext &C) const;
256 
257   void preDefault(const FnDescription *Desc, const CallEvent &Call,
258                   CheckerContext &C) const;
259 
260   void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
261                     CheckerContext &C) const;
262 
263   template <StreamState::ErrorKindTy ErrorKind>
264   void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
265                       CheckerContext &C) const;
266 
267   template <StreamState::ErrorKindTy EK>
268   void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
269                          CheckerContext &C) const;
270 
271   /// Check that the stream (in StreamVal) is not NULL.
272   /// If it can only be NULL a fatal error is emitted and nullptr returned.
273   /// Otherwise the return value is a new state where the stream is constrained
274   /// to be non-null.
275   ProgramStateRef ensureStreamNonNull(SVal StreamVal, CheckerContext &C,
276                                       ProgramStateRef State) const;
277 
278   /// Check that the stream is the opened state.
279   /// If the stream is known to be not opened an error is generated
280   /// and nullptr returned, otherwise the original state is returned.
281   ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,
282                                      ProgramStateRef State) const;
283 
284   /// Check the legality of the 'whence' argument of 'fseek'.
285   /// Generate error and return nullptr if it is found to be illegal.
286   /// Otherwise returns the state.
287   /// (State is not changed here because the "whence" value is already known.)
288   ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
289                                            ProgramStateRef State) const;
290 
291   /// Find the description data of the function called by a call event.
292   /// Returns nullptr if no function is recognized.
293   const FnDescription *lookupFn(const CallEvent &Call) const {
294     // Recognize "global C functions" with only integral or pointer arguments
295     // (and matching name) as stream functions.
296     if (!Call.isGlobalCFunction())
297       return nullptr;
298     for (auto P : Call.parameters()) {
299       QualType T = P->getType();
300       if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
301         return nullptr;
302     }
303 
304     return FnDescriptions.lookup(Call);
305   }
306 };
307 
308 } // end anonymous namespace
309 
310 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
311 
312 inline void assertStreamStateOpened(const StreamState *SS) {
313   assert(SS->isOpened() &&
314          "Previous create of error node for non-opened stream failed?");
315 }
316 
317 void StreamChecker::checkPreCall(const CallEvent &Call,
318                                  CheckerContext &C) const {
319   const FnDescription *Desc = lookupFn(Call);
320   if (!Desc || !Desc->PreFn)
321     return;
322 
323   Desc->PreFn(this, Desc, Call, C);
324 }
325 
326 bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
327   const FnDescription *Desc = lookupFn(Call);
328   if (!Desc && TestMode)
329     Desc = FnTestDescriptions.lookup(Call);
330   if (!Desc || !Desc->EvalFn)
331     return false;
332 
333   Desc->EvalFn(this, Desc, Call, C);
334 
335   return C.isDifferent();
336 }
337 
338 void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
339                               CheckerContext &C) const {
340   ProgramStateRef State = C.getState();
341   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
342   if (!CE)
343     return;
344 
345   DefinedSVal RetVal = makeRetVal(C, CE);
346   SymbolRef RetSym = RetVal.getAsSymbol();
347   assert(RetSym && "RetVal must be a symbol here.");
348 
349   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
350 
351   // Bifurcate the state into two: one with a valid FILE* pointer, the other
352   // with a NULL.
353   ProgramStateRef StateNotNull, StateNull;
354   std::tie(StateNotNull, StateNull) =
355       C.getConstraintManager().assumeDual(State, RetVal);
356 
357   StateNotNull =
358       StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
359   StateNull =
360       StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
361 
362   C.addTransition(StateNotNull);
363   C.addTransition(StateNull);
364 }
365 
366 void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
367                                CheckerContext &C) const {
368   // Do not allow NULL as passed stream pointer but allow a closed stream.
369   ProgramStateRef State = C.getState();
370   State = ensureStreamNonNull(getStreamArg(Desc, Call), C, State);
371   if (!State)
372     return;
373 
374   C.addTransition(State);
375 }
376 
377 void StreamChecker::evalFreopen(const FnDescription *Desc,
378                                 const CallEvent &Call,
379                                 CheckerContext &C) const {
380   ProgramStateRef State = C.getState();
381 
382   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
383   if (!CE)
384     return;
385 
386   Optional<DefinedSVal> StreamVal =
387       getStreamArg(Desc, Call).getAs<DefinedSVal>();
388   if (!StreamVal)
389     return;
390 
391   SymbolRef StreamSym = StreamVal->getAsSymbol();
392   // Do not care about concrete values for stream ("(FILE *)0x12345"?).
393   // FIXME: Are stdin, stdout, stderr such values?
394   if (!StreamSym)
395     return;
396 
397   // Generate state for non-failed case.
398   // Return value is the passed stream pointer.
399   // According to the documentations, the stream is closed first
400   // but any close error is ignored. The state changes to (or remains) opened.
401   ProgramStateRef StateRetNotNull =
402       State->BindExpr(CE, C.getLocationContext(), *StreamVal);
403   // Generate state for NULL return value.
404   // Stream switches to OpenFailed state.
405   ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(),
406                                                  C.getSValBuilder().makeNull());
407 
408   StateRetNotNull =
409       StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
410   StateRetNull =
411       StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
412 
413   C.addTransition(StateRetNotNull);
414   C.addTransition(StateRetNull);
415 }
416 
417 void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
418                                CheckerContext &C) const {
419   ProgramStateRef State = C.getState();
420   SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
421   if (!Sym)
422     return;
423 
424   const StreamState *SS = State->get<StreamMap>(Sym);
425   if (!SS)
426     return;
427 
428   assertStreamStateOpened(SS);
429 
430   // Close the File Descriptor.
431   // Regardless if the close fails or not, stream becomes "closed"
432   // and can not be used any more.
433   State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
434 
435   C.addTransition(State);
436 }
437 
438 void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
439                              CheckerContext &C) const {
440   ProgramStateRef State = C.getState();
441   SVal StreamVal = getStreamArg(Desc, Call);
442   State = ensureStreamNonNull(StreamVal, C, State);
443   if (!State)
444     return;
445   State = ensureStreamOpened(StreamVal, C, State);
446   if (!State)
447     return;
448   State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
449   if (!State)
450     return;
451 
452   C.addTransition(State);
453 }
454 
455 void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
456                               CheckerContext &C) const {
457   ProgramStateRef State = C.getState();
458   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
459   if (!StreamSym)
460     return;
461 
462   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
463   if (!CE)
464     return;
465 
466   // Ignore the call if the stream is not tracked.
467   if (!State->get<StreamMap>(StreamSym))
468     return;
469 
470   DefinedSVal RetVal = makeRetVal(C, CE);
471 
472   // Make expression result.
473   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
474 
475   // Bifurcate the state into failed and non-failed.
476   // Return zero on success, nonzero on error.
477   ProgramStateRef StateNotFailed, StateFailed;
478   std::tie(StateFailed, StateNotFailed) =
479       C.getConstraintManager().assumeDual(State, RetVal);
480 
481   // Reset the state to opened with no error.
482   StateNotFailed =
483       StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
484   // We get error.
485   StateFailed = StateFailed->set<StreamMap>(
486       StreamSym, StreamState::getOpened(Desc, StreamState::Unknown));
487 
488   C.addTransition(StateNotFailed);
489   C.addTransition(StateFailed);
490 }
491 
492 void StreamChecker::evalClearerr(const FnDescription *Desc,
493                                  const CallEvent &Call,
494                                  CheckerContext &C) const {
495   ProgramStateRef State = C.getState();
496   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
497   if (!StreamSym)
498     return;
499 
500   const StreamState *SS = State->get<StreamMap>(StreamSym);
501   if (!SS)
502     return;
503 
504   assertStreamStateOpened(SS);
505 
506   State = State->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
507   C.addTransition(State);
508 }
509 
510 template <StreamState::ErrorKindTy ErrorKind>
511 void StreamChecker::evalFeofFerror(const FnDescription *Desc,
512                                    const CallEvent &Call,
513                                    CheckerContext &C) const {
514   ProgramStateRef State = C.getState();
515   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
516   if (!StreamSym)
517     return;
518 
519   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
520   if (!CE)
521     return;
522 
523   auto AddTransition = [&C, Desc, StreamSym](ProgramStateRef State,
524                                              StreamState::ErrorKindTy SSError) {
525     StreamState SSNew = StreamState::getOpened(Desc, SSError);
526     State = State->set<StreamMap>(StreamSym, SSNew);
527     C.addTransition(State);
528   };
529 
530   const StreamState *SS = State->get<StreamMap>(StreamSym);
531   if (!SS)
532     return;
533 
534   assertStreamStateOpened(SS);
535 
536   if (!SS->isUnknown()) {
537     // The stream is in exactly known state (error or not).
538     // Check if it is in error of kind `ErrorKind`.
539     if (SS->ErrorState == ErrorKind)
540       State = bindAndAssumeTrue(State, C, CE);
541     else
542       State = bindInt(0, State, C, CE);
543     // Error state is not changed in the new state.
544     AddTransition(State, SS->ErrorState);
545   } else {
546     // Stream is in unknown state, check if error `ErrorKind` is possible.
547     Optional<StreamState::ErrorKindTy> NewError =
548         SS->getRemainingPossibleError(ErrorKind);
549     if (!NewError) {
550       // This kind of error is not possible, function returns zero.
551       // Error state remains unknown.
552       AddTransition(bindInt(0, State, C, CE), StreamState::Unknown);
553     } else {
554       // Add state with true returned.
555       AddTransition(bindAndAssumeTrue(State, C, CE), ErrorKind);
556       // Add state with false returned and the new remaining error type.
557       AddTransition(bindInt(0, State, C, CE), *NewError);
558     }
559   }
560 }
561 
562 void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
563                                CheckerContext &C) const {
564   ProgramStateRef State = C.getState();
565   SVal StreamVal = getStreamArg(Desc, Call);
566   State = ensureStreamNonNull(StreamVal, C, State);
567   if (!State)
568     return;
569   State = ensureStreamOpened(StreamVal, C, State);
570   if (!State)
571     return;
572 
573   C.addTransition(State);
574 }
575 
576 template <StreamState::ErrorKindTy EK>
577 void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
578                                       const CallEvent &Call,
579                                       CheckerContext &C) const {
580   ProgramStateRef State = C.getState();
581   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
582   assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
583   const StreamState *SS = State->get<StreamMap>(StreamSym);
584   assert(SS && "Stream should be tracked by the checker.");
585   State = State->set<StreamMap>(StreamSym,
586                                 StreamState::getOpened(SS->LastOperation, EK));
587   C.addTransition(State);
588 }
589 
590 ProgramStateRef
591 StreamChecker::ensureStreamNonNull(SVal StreamVal, CheckerContext &C,
592                                    ProgramStateRef State) const {
593   auto Stream = StreamVal.getAs<DefinedSVal>();
594   if (!Stream)
595     return State;
596 
597   ConstraintManager &CM = C.getConstraintManager();
598 
599   ProgramStateRef StateNotNull, StateNull;
600   std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream);
601 
602   if (!StateNotNull && StateNull) {
603     if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
604       if (!BT_nullfp)
605         BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
606                                        "Stream pointer might be NULL."));
607       C.emitReport(std::make_unique<PathSensitiveBugReport>(
608           *BT_nullfp, BT_nullfp->getDescription(), N));
609     }
610     return nullptr;
611   }
612 
613   return StateNotNull;
614 }
615 
616 ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
617                                                   CheckerContext &C,
618                                                   ProgramStateRef State) const {
619   SymbolRef Sym = StreamVal.getAsSymbol();
620   if (!Sym)
621     return State;
622 
623   const StreamState *SS = State->get<StreamMap>(Sym);
624   if (!SS)
625     return State;
626 
627   if (SS->isClosed()) {
628     // Using a stream pointer after 'fclose' causes undefined behavior
629     // according to cppreference.com .
630     ExplodedNode *N = C.generateErrorNode();
631     if (N) {
632       if (!BT_UseAfterClose)
633         BT_UseAfterClose.reset(new BuiltinBug(this, "Closed stream",
634                                               "Stream might be already closed. "
635                                               "Causes undefined behaviour."));
636       C.emitReport(std::make_unique<PathSensitiveBugReport>(
637           *BT_UseAfterClose, BT_UseAfterClose->getDescription(), N));
638       return nullptr;
639     }
640 
641     return State;
642   }
643 
644   if (SS->isOpenFailed()) {
645     // Using a stream that has failed to open is likely to cause problems.
646     // This should usually not occur because stream pointer is NULL.
647     // But freopen can cause a state when stream pointer remains non-null but
648     // failed to open.
649     ExplodedNode *N = C.generateErrorNode();
650     if (N) {
651       if (!BT_UseAfterOpenFailed)
652         BT_UseAfterOpenFailed.reset(
653             new BuiltinBug(this, "Invalid stream",
654                            "Stream might be invalid after "
655                            "(re-)opening it has failed. "
656                            "Can cause undefined behaviour."));
657       C.emitReport(std::make_unique<PathSensitiveBugReport>(
658           *BT_UseAfterOpenFailed, BT_UseAfterOpenFailed->getDescription(), N));
659       return nullptr;
660     }
661     return State;
662   }
663 
664   return State;
665 }
666 
667 ProgramStateRef
668 StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
669                                         ProgramStateRef State) const {
670   Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>();
671   if (!CI)
672     return State;
673 
674   int64_t X = CI->getValue().getSExtValue();
675   if (X >= 0 && X <= 2)
676     return State;
677 
678   if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
679     if (!BT_illegalwhence)
680       BT_illegalwhence.reset(
681           new BuiltinBug(this, "Illegal whence argument",
682                          "The whence argument to fseek() should be "
683                          "SEEK_SET, SEEK_END, or SEEK_CUR."));
684     C.emitReport(std::make_unique<PathSensitiveBugReport>(
685         *BT_illegalwhence, BT_illegalwhence->getDescription(), N));
686     return nullptr;
687   }
688 
689   return State;
690 }
691 
692 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
693                                      CheckerContext &C) const {
694   ProgramStateRef State = C.getState();
695 
696   // TODO: Clean up the state.
697   const StreamMapTy &Map = State->get<StreamMap>();
698   for (const auto &I : Map) {
699     SymbolRef Sym = I.first;
700     const StreamState &SS = I.second;
701     if (!SymReaper.isDead(Sym) || !SS.isOpened())
702       continue;
703 
704     ExplodedNode *N = C.generateErrorNode();
705     if (!N)
706       continue;
707 
708     if (!BT_ResourceLeak)
709       BT_ResourceLeak.reset(
710           new BuiltinBug(this, "Resource Leak",
711                          "Opened File never closed. Potential Resource leak."));
712     C.emitReport(std::make_unique<PathSensitiveBugReport>(
713         *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N));
714   }
715 }
716 
717 void ento::registerStreamChecker(CheckerManager &Mgr) {
718   Mgr.registerChecker<StreamChecker>();
719 }
720 
721 bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
722   return true;
723 }
724 
725 void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
726   auto *Checker = Mgr.getChecker<StreamChecker>();
727   Checker->TestMode = true;
728 }
729 
730 bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
731   return true;
732 }
733