xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
10b57cec5SDimitry Andric //===-- StreamChecker.cpp -----------------------------------------*- C++ -*--//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines checkers that model and check stream handling functions.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
140b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
17349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20bdd1243dSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
24480093f4SDimitry Andric #include <functional>
25bdd1243dSDimitry Andric #include <optional>
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric using namespace clang;
280b57cec5SDimitry Andric using namespace ento;
29480093f4SDimitry Andric using namespace std::placeholders;
300b57cec5SDimitry Andric 
31fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
32fe6060f1SDimitry Andric // Definition of state data structures.
33fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
34fe6060f1SDimitry Andric 
350b57cec5SDimitry Andric namespace {
360b57cec5SDimitry Andric 
375ffd83dbSDimitry Andric struct FnDescription;
380b57cec5SDimitry Andric 
395ffd83dbSDimitry Andric /// State of the stream error flags.
405ffd83dbSDimitry Andric /// Sometimes it is not known to the checker what error flags are set.
415ffd83dbSDimitry Andric /// This is indicated by setting more than one flag to true.
425ffd83dbSDimitry Andric /// This is an optimization to avoid state splits.
435ffd83dbSDimitry Andric /// A stream can either be in FEOF or FERROR but not both at the same time.
445ffd83dbSDimitry Andric /// Multiple flags are set to handle the corresponding states together.
455ffd83dbSDimitry Andric struct StreamErrorState {
465ffd83dbSDimitry Andric   /// The stream can be in state where none of the error flags set.
475ffd83dbSDimitry Andric   bool NoError = true;
485ffd83dbSDimitry Andric   /// The stream can be in state where the EOF indicator is set.
495ffd83dbSDimitry Andric   bool FEof = false;
505ffd83dbSDimitry Andric   /// The stream can be in state where the error indicator is set.
515ffd83dbSDimitry Andric   bool FError = false;
520b57cec5SDimitry Andric 
535ffd83dbSDimitry Andric   bool isNoError() const { return NoError && !FEof && !FError; }
545ffd83dbSDimitry Andric   bool isFEof() const { return !NoError && FEof && !FError; }
555ffd83dbSDimitry Andric   bool isFError() const { return !NoError && !FEof && FError; }
560b57cec5SDimitry Andric 
575ffd83dbSDimitry Andric   bool operator==(const StreamErrorState &ES) const {
585ffd83dbSDimitry Andric     return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
595ffd83dbSDimitry Andric   }
600b57cec5SDimitry Andric 
615ffd83dbSDimitry Andric   bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); }
625ffd83dbSDimitry Andric 
635ffd83dbSDimitry Andric   StreamErrorState operator|(const StreamErrorState &E) const {
645ffd83dbSDimitry Andric     return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
655ffd83dbSDimitry Andric   }
665ffd83dbSDimitry Andric 
675ffd83dbSDimitry Andric   StreamErrorState operator&(const StreamErrorState &E) const {
685ffd83dbSDimitry Andric     return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
695ffd83dbSDimitry Andric   }
705ffd83dbSDimitry Andric 
715ffd83dbSDimitry Andric   StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; }
725ffd83dbSDimitry Andric 
735ffd83dbSDimitry Andric   /// Returns if the StreamErrorState is a valid object.
745ffd83dbSDimitry Andric   operator bool() const { return NoError || FEof || FError; }
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const {
775ffd83dbSDimitry Andric     ID.AddBoolean(NoError);
785ffd83dbSDimitry Andric     ID.AddBoolean(FEof);
795ffd83dbSDimitry Andric     ID.AddBoolean(FError);
800b57cec5SDimitry Andric   }
810b57cec5SDimitry Andric };
820b57cec5SDimitry Andric 
835ffd83dbSDimitry Andric const StreamErrorState ErrorNone{true, false, false};
845ffd83dbSDimitry Andric const StreamErrorState ErrorFEof{false, true, false};
855ffd83dbSDimitry Andric const StreamErrorState ErrorFError{false, false, true};
860b57cec5SDimitry Andric 
875ffd83dbSDimitry Andric /// Full state information about a stream pointer.
885ffd83dbSDimitry Andric struct StreamState {
895ffd83dbSDimitry Andric   /// The last file operation called in the stream.
90bdd1243dSDimitry Andric   /// Can be nullptr.
915ffd83dbSDimitry Andric   const FnDescription *LastOperation;
920b57cec5SDimitry Andric 
935ffd83dbSDimitry Andric   /// State of a stream symbol.
945ffd83dbSDimitry Andric   enum KindTy {
955ffd83dbSDimitry Andric     Opened, /// Stream is opened.
965ffd83dbSDimitry Andric     Closed, /// Closed stream (an invalid stream pointer after it was closed).
975ffd83dbSDimitry Andric     OpenFailed /// The last open operation has failed.
985ffd83dbSDimitry Andric   } State;
990b57cec5SDimitry Andric 
1005ffd83dbSDimitry Andric   /// State of the error flags.
1015ffd83dbSDimitry Andric   /// Ignored in non-opened stream state but must be NoError.
1025ffd83dbSDimitry Andric   StreamErrorState const ErrorState;
1035ffd83dbSDimitry Andric 
1045ffd83dbSDimitry Andric   /// Indicate if the file has an "indeterminate file position indicator".
1055ffd83dbSDimitry Andric   /// This can be set at a failing read or write or seek operation.
1065ffd83dbSDimitry Andric   /// If it is set no more read or write is allowed.
1075ffd83dbSDimitry Andric   /// This value is not dependent on the stream error flags:
1085ffd83dbSDimitry Andric   /// The error flag may be cleared with `clearerr` but the file position
1095ffd83dbSDimitry Andric   /// remains still indeterminate.
1105ffd83dbSDimitry Andric   /// This value applies to all error states in ErrorState except FEOF.
1115ffd83dbSDimitry Andric   /// An EOF+indeterminate state is the same as EOF state.
1125ffd83dbSDimitry Andric   bool const FilePositionIndeterminate = false;
1135ffd83dbSDimitry Andric 
1145ffd83dbSDimitry Andric   StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES,
1155ffd83dbSDimitry Andric               bool IsFilePositionIndeterminate)
1165ffd83dbSDimitry Andric       : LastOperation(L), State(S), ErrorState(ES),
1175ffd83dbSDimitry Andric         FilePositionIndeterminate(IsFilePositionIndeterminate) {
1185ffd83dbSDimitry Andric     assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
1195ffd83dbSDimitry Andric            "FilePositionIndeterminate should be false in FEof case.");
1205ffd83dbSDimitry Andric     assert((State == Opened || ErrorState.isNoError()) &&
1215ffd83dbSDimitry Andric            "ErrorState should be None in non-opened stream state.");
1225ffd83dbSDimitry Andric   }
1235ffd83dbSDimitry Andric 
1245ffd83dbSDimitry Andric   bool isOpened() const { return State == Opened; }
1255ffd83dbSDimitry Andric   bool isClosed() const { return State == Closed; }
1265ffd83dbSDimitry Andric   bool isOpenFailed() const { return State == OpenFailed; }
1275ffd83dbSDimitry Andric 
1285ffd83dbSDimitry Andric   bool operator==(const StreamState &X) const {
1295ffd83dbSDimitry Andric     // In not opened state error state should always NoError, so comparison
1305ffd83dbSDimitry Andric     // here is no problem.
1315ffd83dbSDimitry Andric     return LastOperation == X.LastOperation && State == X.State &&
1325ffd83dbSDimitry Andric            ErrorState == X.ErrorState &&
1335ffd83dbSDimitry Andric            FilePositionIndeterminate == X.FilePositionIndeterminate;
1345ffd83dbSDimitry Andric   }
1355ffd83dbSDimitry Andric 
1365ffd83dbSDimitry Andric   static StreamState getOpened(const FnDescription *L,
1375ffd83dbSDimitry Andric                                const StreamErrorState &ES = ErrorNone,
1385ffd83dbSDimitry Andric                                bool IsFilePositionIndeterminate = false) {
1395ffd83dbSDimitry Andric     return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
1405ffd83dbSDimitry Andric   }
1415ffd83dbSDimitry Andric   static StreamState getClosed(const FnDescription *L) {
1425ffd83dbSDimitry Andric     return StreamState{L, Closed, {}, false};
1435ffd83dbSDimitry Andric   }
1445ffd83dbSDimitry Andric   static StreamState getOpenFailed(const FnDescription *L) {
1455ffd83dbSDimitry Andric     return StreamState{L, OpenFailed, {}, false};
1465ffd83dbSDimitry Andric   }
1475ffd83dbSDimitry Andric 
1485ffd83dbSDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const {
1495ffd83dbSDimitry Andric     ID.AddPointer(LastOperation);
1505ffd83dbSDimitry Andric     ID.AddInteger(State);
15181ad6265SDimitry Andric     ErrorState.Profile(ID);
1525ffd83dbSDimitry Andric     ID.AddBoolean(FilePositionIndeterminate);
1535ffd83dbSDimitry Andric   }
154480093f4SDimitry Andric };
1550b57cec5SDimitry Andric 
156fe6060f1SDimitry Andric } // namespace
157fe6060f1SDimitry Andric 
158fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
159fe6060f1SDimitry Andric // StreamChecker class and utility functions.
160fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
161fe6060f1SDimitry Andric 
162fe6060f1SDimitry Andric namespace {
163fe6060f1SDimitry Andric 
1645ffd83dbSDimitry Andric class StreamChecker;
1655ffd83dbSDimitry Andric using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
1665ffd83dbSDimitry Andric                                    const CallEvent &, CheckerContext &)>;
167480093f4SDimitry Andric 
1685ffd83dbSDimitry Andric using ArgNoTy = unsigned int;
1695ffd83dbSDimitry Andric static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
1705ffd83dbSDimitry Andric 
1715ffd83dbSDimitry Andric struct FnDescription {
1725ffd83dbSDimitry Andric   FnCheck PreFn;
1735ffd83dbSDimitry Andric   FnCheck EvalFn;
1745ffd83dbSDimitry Andric   ArgNoTy StreamArgNo;
1755ffd83dbSDimitry Andric };
1765ffd83dbSDimitry Andric 
1775ffd83dbSDimitry Andric /// Get the value of the stream argument out of the passed call event.
1785ffd83dbSDimitry Andric /// The call should contain a function that is described by Desc.
1795ffd83dbSDimitry Andric SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {
1805ffd83dbSDimitry Andric   assert(Desc && Desc->StreamArgNo != ArgNone &&
1815ffd83dbSDimitry Andric          "Try to get a non-existing stream argument.");
1825ffd83dbSDimitry Andric   return Call.getArgSVal(Desc->StreamArgNo);
1835ffd83dbSDimitry Andric }
1845ffd83dbSDimitry Andric 
1855ffd83dbSDimitry Andric /// Create a conjured symbol return value for a call expression.
1865ffd83dbSDimitry Andric DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) {
1875ffd83dbSDimitry Andric   assert(CE && "Expecting a call expression.");
1885ffd83dbSDimitry Andric 
1895ffd83dbSDimitry Andric   const LocationContext *LCtx = C.getLocationContext();
1905ffd83dbSDimitry Andric   return C.getSValBuilder()
1915ffd83dbSDimitry Andric       .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
1925ffd83dbSDimitry Andric       .castAs<DefinedSVal>();
1935ffd83dbSDimitry Andric }
1945ffd83dbSDimitry Andric 
1955ffd83dbSDimitry Andric ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
1965ffd83dbSDimitry Andric                                   const CallExpr *CE) {
1975ffd83dbSDimitry Andric   DefinedSVal RetVal = makeRetVal(C, CE);
1985ffd83dbSDimitry Andric   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
1995ffd83dbSDimitry Andric   State = State->assume(RetVal, true);
2005ffd83dbSDimitry Andric   assert(State && "Assumption on new value should not fail.");
2015ffd83dbSDimitry Andric   return State;
2025ffd83dbSDimitry Andric }
2035ffd83dbSDimitry Andric 
2045ffd83dbSDimitry Andric ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
2055ffd83dbSDimitry Andric                         CheckerContext &C, const CallExpr *CE) {
2065ffd83dbSDimitry Andric   State = State->BindExpr(CE, C.getLocationContext(),
207bdd1243dSDimitry Andric                           C.getSValBuilder().makeIntVal(Value, CE->getType()));
2085ffd83dbSDimitry Andric   return State;
2095ffd83dbSDimitry Andric }
2105ffd83dbSDimitry Andric 
2115ffd83dbSDimitry Andric class StreamChecker : public Checker<check::PreCall, eval::Call,
2125ffd83dbSDimitry Andric                                      check::DeadSymbols, check::PointerEscape> {
21306c3fb27SDimitry Andric   BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
2145ffd83dbSDimitry Andric   BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
2155ffd83dbSDimitry Andric   BugType BT_UseAfterOpenFailed{this, "Invalid stream",
2165ffd83dbSDimitry Andric                                 "Stream handling error"};
2175ffd83dbSDimitry Andric   BugType BT_IndeterminatePosition{this, "Invalid stream state",
2185ffd83dbSDimitry Andric                                    "Stream handling error"};
2195ffd83dbSDimitry Andric   BugType BT_IllegalWhence{this, "Illegal whence argument",
2205ffd83dbSDimitry Andric                            "Stream handling error"};
2215ffd83dbSDimitry Andric   BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};
222e8d8bef9SDimitry Andric   BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error",
223e8d8bef9SDimitry Andric                           /*SuppressOnSink =*/true};
2245ffd83dbSDimitry Andric 
2255ffd83dbSDimitry Andric public:
2265ffd83dbSDimitry Andric   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
2275ffd83dbSDimitry Andric   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
2285ffd83dbSDimitry Andric   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
2295ffd83dbSDimitry Andric   ProgramStateRef checkPointerEscape(ProgramStateRef State,
2305ffd83dbSDimitry Andric                                      const InvalidatedSymbols &Escaped,
2315ffd83dbSDimitry Andric                                      const CallEvent *Call,
2325ffd83dbSDimitry Andric                                      PointerEscapeKind Kind) const;
2335ffd83dbSDimitry Andric 
2345ffd83dbSDimitry Andric   /// If true, evaluate special testing stream functions.
2355ffd83dbSDimitry Andric   bool TestMode = false;
2365ffd83dbSDimitry Andric 
237fe6060f1SDimitry Andric   const BugType *getBT_StreamEof() const { return &BT_StreamEof; }
238fe6060f1SDimitry Andric 
2395ffd83dbSDimitry Andric private:
2405ffd83dbSDimitry Andric   CallDescriptionMap<FnDescription> FnDescriptions = {
241*5f757f3fSDimitry Andric       {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
242bdd1243dSDimitry Andric       {{{"freopen"}, 3},
2435ffd83dbSDimitry Andric        {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
244*5f757f3fSDimitry Andric       {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
245bdd1243dSDimitry Andric       {{{"fclose"}, 1},
2465ffd83dbSDimitry Andric        {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
247bdd1243dSDimitry Andric       {{{"fread"}, 4},
248*5f757f3fSDimitry Andric        {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
2495ffd83dbSDimitry Andric         std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
250bdd1243dSDimitry Andric       {{{"fwrite"}, 4},
251*5f757f3fSDimitry Andric        {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
2525ffd83dbSDimitry Andric         std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
253*5f757f3fSDimitry Andric       {{{"fgetc"}, 1},
254*5f757f3fSDimitry Andric        {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
255*5f757f3fSDimitry Andric         std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
256*5f757f3fSDimitry Andric       {{{"fgets"}, 3},
257*5f757f3fSDimitry Andric        {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
258*5f757f3fSDimitry Andric         std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}},
259*5f757f3fSDimitry Andric       {{{"fputc"}, 2},
260*5f757f3fSDimitry Andric        {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
261*5f757f3fSDimitry Andric         std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
262*5f757f3fSDimitry Andric       {{{"fputs"}, 2},
263*5f757f3fSDimitry Andric        {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
264*5f757f3fSDimitry Andric         std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}},
265bdd1243dSDimitry Andric       {{{"fseek"}, 3},
266bdd1243dSDimitry Andric        {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
267bdd1243dSDimitry Andric       {{{"ftell"}, 1},
268bdd1243dSDimitry Andric        {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
269bdd1243dSDimitry Andric       {{{"rewind"}, 1},
270bdd1243dSDimitry Andric        {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
271bdd1243dSDimitry Andric       {{{"fgetpos"}, 2},
272bdd1243dSDimitry Andric        {&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}},
273bdd1243dSDimitry Andric       {{{"fsetpos"}, 2},
274bdd1243dSDimitry Andric        {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
275bdd1243dSDimitry Andric       {{{"clearerr"}, 1},
2765ffd83dbSDimitry Andric        {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
277bdd1243dSDimitry Andric       {{{"feof"}, 1},
2785ffd83dbSDimitry Andric        {&StreamChecker::preDefault,
2795ffd83dbSDimitry Andric         std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
2805ffd83dbSDimitry Andric         0}},
281bdd1243dSDimitry Andric       {{{"ferror"}, 1},
2825ffd83dbSDimitry Andric        {&StreamChecker::preDefault,
2835ffd83dbSDimitry Andric         std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
2845ffd83dbSDimitry Andric         0}},
285bdd1243dSDimitry Andric       {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}},
2865ffd83dbSDimitry Andric   };
2875ffd83dbSDimitry Andric 
2885ffd83dbSDimitry Andric   CallDescriptionMap<FnDescription> FnTestDescriptions = {
289bdd1243dSDimitry Andric       {{{"StreamTesterChecker_make_feof_stream"}, 1},
2905ffd83dbSDimitry Andric        {nullptr,
2915ffd83dbSDimitry Andric         std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof),
2925ffd83dbSDimitry Andric         0}},
293bdd1243dSDimitry Andric       {{{"StreamTesterChecker_make_ferror_stream"}, 1},
2945ffd83dbSDimitry Andric        {nullptr,
2955ffd83dbSDimitry Andric         std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
2965ffd83dbSDimitry Andric                   ErrorFError),
2975ffd83dbSDimitry Andric         0}},
2985ffd83dbSDimitry Andric   };
2995ffd83dbSDimitry Andric 
30006c3fb27SDimitry Andric   /// Expanded value of EOF, empty before initialization.
301bdd1243dSDimitry Andric   mutable std::optional<int> EofVal;
30206c3fb27SDimitry Andric   /// Expanded value of SEEK_SET, 0 if not found.
30306c3fb27SDimitry Andric   mutable int SeekSetVal = 0;
30406c3fb27SDimitry Andric   /// Expanded value of SEEK_CUR, 1 if not found.
30506c3fb27SDimitry Andric   mutable int SeekCurVal = 1;
30606c3fb27SDimitry Andric   /// Expanded value of SEEK_END, 2 if not found.
30706c3fb27SDimitry Andric   mutable int SeekEndVal = 2;
308bdd1243dSDimitry Andric 
3095ffd83dbSDimitry Andric   void evalFopen(const FnDescription *Desc, const CallEvent &Call,
3105ffd83dbSDimitry Andric                  CheckerContext &C) const;
3115ffd83dbSDimitry Andric 
3125ffd83dbSDimitry Andric   void preFreopen(const FnDescription *Desc, const CallEvent &Call,
3135ffd83dbSDimitry Andric                   CheckerContext &C) const;
3145ffd83dbSDimitry Andric   void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
3155ffd83dbSDimitry Andric                    CheckerContext &C) const;
3165ffd83dbSDimitry Andric 
3175ffd83dbSDimitry Andric   void evalFclose(const FnDescription *Desc, const CallEvent &Call,
3185ffd83dbSDimitry Andric                   CheckerContext &C) const;
3195ffd83dbSDimitry Andric 
320*5f757f3fSDimitry Andric   void preReadWrite(const FnDescription *Desc, const CallEvent &Call,
321*5f757f3fSDimitry Andric                     CheckerContext &C, bool IsRead) const;
3225ffd83dbSDimitry Andric 
3235ffd83dbSDimitry Andric   void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
3245ffd83dbSDimitry Andric                        CheckerContext &C, bool IsFread) const;
3255ffd83dbSDimitry Andric 
326*5f757f3fSDimitry Andric   void evalFgetx(const FnDescription *Desc, const CallEvent &Call,
327*5f757f3fSDimitry Andric                  CheckerContext &C, bool SingleChar) const;
328*5f757f3fSDimitry Andric 
329*5f757f3fSDimitry Andric   void evalFputx(const FnDescription *Desc, const CallEvent &Call,
330*5f757f3fSDimitry Andric                  CheckerContext &C, bool IsSingleChar) const;
331*5f757f3fSDimitry Andric 
3325ffd83dbSDimitry Andric   void preFseek(const FnDescription *Desc, const CallEvent &Call,
3335ffd83dbSDimitry Andric                 CheckerContext &C) const;
3345ffd83dbSDimitry Andric   void evalFseek(const FnDescription *Desc, const CallEvent &Call,
3355ffd83dbSDimitry Andric                  CheckerContext &C) const;
3365ffd83dbSDimitry Andric 
337bdd1243dSDimitry Andric   void evalFgetpos(const FnDescription *Desc, const CallEvent &Call,
338bdd1243dSDimitry Andric                    CheckerContext &C) const;
339bdd1243dSDimitry Andric 
340bdd1243dSDimitry Andric   void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
341bdd1243dSDimitry Andric                    CheckerContext &C) const;
342bdd1243dSDimitry Andric 
343bdd1243dSDimitry Andric   void evalFtell(const FnDescription *Desc, const CallEvent &Call,
344bdd1243dSDimitry Andric                  CheckerContext &C) const;
345bdd1243dSDimitry Andric 
346bdd1243dSDimitry Andric   void evalRewind(const FnDescription *Desc, const CallEvent &Call,
347bdd1243dSDimitry Andric                   CheckerContext &C) const;
348bdd1243dSDimitry Andric 
3495ffd83dbSDimitry Andric   void preDefault(const FnDescription *Desc, const CallEvent &Call,
3505ffd83dbSDimitry Andric                   CheckerContext &C) const;
3515ffd83dbSDimitry Andric 
3525ffd83dbSDimitry Andric   void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
3535ffd83dbSDimitry Andric                     CheckerContext &C) const;
3545ffd83dbSDimitry Andric 
3555ffd83dbSDimitry Andric   void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
3565ffd83dbSDimitry Andric                       CheckerContext &C,
3575ffd83dbSDimitry Andric                       const StreamErrorState &ErrorKind) const;
3585ffd83dbSDimitry Andric 
3595ffd83dbSDimitry Andric   void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
3605ffd83dbSDimitry Andric                          CheckerContext &C,
3615ffd83dbSDimitry Andric                          const StreamErrorState &ErrorKind) const;
3625ffd83dbSDimitry Andric 
3635ffd83dbSDimitry Andric   /// Check that the stream (in StreamVal) is not NULL.
36406c3fb27SDimitry Andric   /// If it can only be NULL a fatal error is emitted and nullptr returned.
3655ffd83dbSDimitry Andric   /// Otherwise the return value is a new state where the stream is constrained
3665ffd83dbSDimitry Andric   /// to be non-null.
367fe6060f1SDimitry Andric   ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
368fe6060f1SDimitry Andric                                       CheckerContext &C,
3695ffd83dbSDimitry Andric                                       ProgramStateRef State) const;
3705ffd83dbSDimitry Andric 
3715ffd83dbSDimitry Andric   /// Check that the stream is the opened state.
3725ffd83dbSDimitry Andric   /// If the stream is known to be not opened an error is generated
3735ffd83dbSDimitry Andric   /// and nullptr returned, otherwise the original state is returned.
3745ffd83dbSDimitry Andric   ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,
3755ffd83dbSDimitry Andric                                      ProgramStateRef State) const;
3765ffd83dbSDimitry Andric 
3775ffd83dbSDimitry Andric   /// Check that the stream has not an invalid ("indeterminate") file position,
3785ffd83dbSDimitry Andric   /// generate warning for it.
3795ffd83dbSDimitry Andric   /// (EOF is not an invalid position.)
3805ffd83dbSDimitry Andric   /// The returned state can be nullptr if a fatal error was generated.
3815ffd83dbSDimitry Andric   /// It can return non-null state if the stream has not an invalid position or
3825ffd83dbSDimitry Andric   /// there is execution path with non-invalid position.
3835ffd83dbSDimitry Andric   ProgramStateRef
3845ffd83dbSDimitry Andric   ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,
3855ffd83dbSDimitry Andric                                     ProgramStateRef State) const;
3865ffd83dbSDimitry Andric 
3875ffd83dbSDimitry Andric   /// Check the legality of the 'whence' argument of 'fseek'.
3885ffd83dbSDimitry Andric   /// Generate error and return nullptr if it is found to be illegal.
3895ffd83dbSDimitry Andric   /// Otherwise returns the state.
3905ffd83dbSDimitry Andric   /// (State is not changed here because the "whence" value is already known.)
3915ffd83dbSDimitry Andric   ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
3925ffd83dbSDimitry Andric                                            ProgramStateRef State) const;
3935ffd83dbSDimitry Andric 
3945ffd83dbSDimitry Andric   /// Generate warning about stream in EOF state.
3955ffd83dbSDimitry Andric   /// There will be always a state transition into the passed State,
3965ffd83dbSDimitry Andric   /// by the new non-fatal error node or (if failed) a normal transition,
3975ffd83dbSDimitry Andric   /// to ensure uniform handling.
398fe6060f1SDimitry Andric   void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
399fe6060f1SDimitry Andric                          ProgramStateRef State) const;
4005ffd83dbSDimitry Andric 
401e8d8bef9SDimitry Andric   /// Emit resource leak warnings for the given symbols.
402e8d8bef9SDimitry Andric   /// Createn a non-fatal error node for these, and returns it (if any warnings
403e8d8bef9SDimitry Andric   /// were generated). Return value is non-null.
404e8d8bef9SDimitry Andric   ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
405e8d8bef9SDimitry Andric                             CheckerContext &C, ExplodedNode *Pred) const;
406e8d8bef9SDimitry Andric 
4075ffd83dbSDimitry Andric   /// Find the description data of the function called by a call event.
4085ffd83dbSDimitry Andric   /// Returns nullptr if no function is recognized.
4095ffd83dbSDimitry Andric   const FnDescription *lookupFn(const CallEvent &Call) const {
4105ffd83dbSDimitry Andric     // Recognize "global C functions" with only integral or pointer arguments
4115ffd83dbSDimitry Andric     // (and matching name) as stream functions.
4125ffd83dbSDimitry Andric     if (!Call.isGlobalCFunction())
4135ffd83dbSDimitry Andric       return nullptr;
414bdd1243dSDimitry Andric     for (auto *P : Call.parameters()) {
4155ffd83dbSDimitry Andric       QualType T = P->getType();
4165ffd83dbSDimitry Andric       if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
4175ffd83dbSDimitry Andric         return nullptr;
4185ffd83dbSDimitry Andric     }
4195ffd83dbSDimitry Andric 
4205ffd83dbSDimitry Andric     return FnDescriptions.lookup(Call);
4215ffd83dbSDimitry Andric   }
4225ffd83dbSDimitry Andric 
4235ffd83dbSDimitry Andric   /// Generate a message for BugReporterVisitor if the stored symbol is
4245ffd83dbSDimitry Andric   /// marked as interesting by the actual bug report.
4255ffd83dbSDimitry Andric   const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym,
4265ffd83dbSDimitry Andric                                   const std::string &Message) const {
427*5f757f3fSDimitry Andric     return C.getNoteTag([this, StreamSym,
428*5f757f3fSDimitry Andric                          Message](PathSensitiveBugReport &BR) -> std::string {
429*5f757f3fSDimitry Andric       if (BR.isInteresting(StreamSym) && &BR.getBugType() == &BT_ResourceLeak)
430*5f757f3fSDimitry Andric         return Message;
431*5f757f3fSDimitry Andric       return "";
432*5f757f3fSDimitry Andric     });
433fe6060f1SDimitry Andric   }
434fe6060f1SDimitry Andric 
435fe6060f1SDimitry Andric   const NoteTag *constructSetEofNoteTag(CheckerContext &C,
436fe6060f1SDimitry Andric                                         SymbolRef StreamSym) const {
437fe6060f1SDimitry Andric     return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
438fe6060f1SDimitry Andric       if (!BR.isInteresting(StreamSym) ||
439fe6060f1SDimitry Andric           &BR.getBugType() != this->getBT_StreamEof())
440fe6060f1SDimitry Andric         return "";
441fe6060f1SDimitry Andric 
442fe6060f1SDimitry Andric       BR.markNotInteresting(StreamSym);
443fe6060f1SDimitry Andric 
444fe6060f1SDimitry Andric       return "Assuming stream reaches end-of-file here";
445fe6060f1SDimitry Andric     });
4465ffd83dbSDimitry Andric   }
4475ffd83dbSDimitry Andric 
44806c3fb27SDimitry Andric   void initMacroValues(CheckerContext &C) const {
449bdd1243dSDimitry Andric     if (EofVal)
450bdd1243dSDimitry Andric       return;
451bdd1243dSDimitry Andric 
452bdd1243dSDimitry Andric     if (const std::optional<int> OptInt =
453bdd1243dSDimitry Andric             tryExpandAsInteger("EOF", C.getPreprocessor()))
454bdd1243dSDimitry Andric       EofVal = *OptInt;
455bdd1243dSDimitry Andric     else
456bdd1243dSDimitry Andric       EofVal = -1;
45706c3fb27SDimitry Andric     if (const std::optional<int> OptInt =
45806c3fb27SDimitry Andric             tryExpandAsInteger("SEEK_SET", C.getPreprocessor()))
45906c3fb27SDimitry Andric       SeekSetVal = *OptInt;
46006c3fb27SDimitry Andric     if (const std::optional<int> OptInt =
46106c3fb27SDimitry Andric             tryExpandAsInteger("SEEK_END", C.getPreprocessor()))
46206c3fb27SDimitry Andric       SeekEndVal = *OptInt;
46306c3fb27SDimitry Andric     if (const std::optional<int> OptInt =
46406c3fb27SDimitry Andric             tryExpandAsInteger("SEEK_CUR", C.getPreprocessor()))
46506c3fb27SDimitry Andric       SeekCurVal = *OptInt;
466bdd1243dSDimitry Andric   }
467bdd1243dSDimitry Andric 
4685ffd83dbSDimitry Andric   /// Searches for the ExplodedNode where the file descriptor was acquired for
4695ffd83dbSDimitry Andric   /// StreamSym.
4705ffd83dbSDimitry Andric   static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
4715ffd83dbSDimitry Andric                                                 SymbolRef StreamSym,
4725ffd83dbSDimitry Andric                                                 CheckerContext &C);
4730b57cec5SDimitry Andric };
4740b57cec5SDimitry Andric 
4750b57cec5SDimitry Andric } // end anonymous namespace
4760b57cec5SDimitry Andric 
477fe6060f1SDimitry Andric // This map holds the state of a stream.
478fe6060f1SDimitry Andric // The stream is identified with a SymbolRef that is created when a stream
479fe6060f1SDimitry Andric // opening function is modeled by the checker.
4800b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
4810b57cec5SDimitry Andric 
4825ffd83dbSDimitry Andric inline void assertStreamStateOpened(const StreamState *SS) {
483bdd1243dSDimitry Andric   assert(SS->isOpened() && "Stream is expected to be opened");
4840b57cec5SDimitry Andric }
4850b57cec5SDimitry Andric 
4865ffd83dbSDimitry Andric const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
4875ffd83dbSDimitry Andric                                                       SymbolRef StreamSym,
4885ffd83dbSDimitry Andric                                                       CheckerContext &C) {
4895ffd83dbSDimitry Andric   ProgramStateRef State = N->getState();
4905ffd83dbSDimitry Andric   // When bug type is resource leak, exploded node N may not have state info
4915ffd83dbSDimitry Andric   // for leaked file descriptor, but predecessor should have it.
4925ffd83dbSDimitry Andric   if (!State->get<StreamMap>(StreamSym))
4935ffd83dbSDimitry Andric     N = N->getFirstPred();
4945ffd83dbSDimitry Andric 
4955ffd83dbSDimitry Andric   const ExplodedNode *Pred = N;
4965ffd83dbSDimitry Andric   while (N) {
4975ffd83dbSDimitry Andric     State = N->getState();
4985ffd83dbSDimitry Andric     if (!State->get<StreamMap>(StreamSym))
4995ffd83dbSDimitry Andric       return Pred;
5005ffd83dbSDimitry Andric     Pred = N;
5015ffd83dbSDimitry Andric     N = N->getFirstPred();
5025ffd83dbSDimitry Andric   }
5035ffd83dbSDimitry Andric 
5045ffd83dbSDimitry Andric   return nullptr;
5055ffd83dbSDimitry Andric }
5065ffd83dbSDimitry Andric 
507fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
508fe6060f1SDimitry Andric // Methods of StreamChecker.
509fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
510fe6060f1SDimitry Andric 
5115ffd83dbSDimitry Andric void StreamChecker::checkPreCall(const CallEvent &Call,
5125ffd83dbSDimitry Andric                                  CheckerContext &C) const {
51306c3fb27SDimitry Andric   initMacroValues(C);
514bdd1243dSDimitry Andric 
5155ffd83dbSDimitry Andric   const FnDescription *Desc = lookupFn(Call);
5165ffd83dbSDimitry Andric   if (!Desc || !Desc->PreFn)
5175ffd83dbSDimitry Andric     return;
5185ffd83dbSDimitry Andric 
5195ffd83dbSDimitry Andric   Desc->PreFn(this, Desc, Call, C);
5205ffd83dbSDimitry Andric }
5215ffd83dbSDimitry Andric 
5225ffd83dbSDimitry Andric bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
5235ffd83dbSDimitry Andric   const FnDescription *Desc = lookupFn(Call);
5245ffd83dbSDimitry Andric   if (!Desc && TestMode)
5255ffd83dbSDimitry Andric     Desc = FnTestDescriptions.lookup(Call);
5265ffd83dbSDimitry Andric   if (!Desc || !Desc->EvalFn)
527480093f4SDimitry Andric     return false;
528480093f4SDimitry Andric 
5295ffd83dbSDimitry Andric   Desc->EvalFn(this, Desc, Call, C);
530480093f4SDimitry Andric 
531480093f4SDimitry Andric   return C.isDifferent();
5320b57cec5SDimitry Andric }
5330b57cec5SDimitry Andric 
5345ffd83dbSDimitry Andric void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
5355ffd83dbSDimitry Andric                               CheckerContext &C) const {
5365ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
5375ffd83dbSDimitry Andric   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
538480093f4SDimitry Andric   if (!CE)
539480093f4SDimitry Andric     return;
540480093f4SDimitry Andric 
5415ffd83dbSDimitry Andric   DefinedSVal RetVal = makeRetVal(C, CE);
5425ffd83dbSDimitry Andric   SymbolRef RetSym = RetVal.getAsSymbol();
5435ffd83dbSDimitry Andric   assert(RetSym && "RetVal must be a symbol here.");
5440b57cec5SDimitry Andric 
5455ffd83dbSDimitry Andric   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
5465ffd83dbSDimitry Andric 
5470b57cec5SDimitry Andric   // Bifurcate the state into two: one with a valid FILE* pointer, the other
5480b57cec5SDimitry Andric   // with a NULL.
5495ffd83dbSDimitry Andric   ProgramStateRef StateNotNull, StateNull;
5505ffd83dbSDimitry Andric   std::tie(StateNotNull, StateNull) =
5515ffd83dbSDimitry Andric       C.getConstraintManager().assumeDual(State, RetVal);
5520b57cec5SDimitry Andric 
5535ffd83dbSDimitry Andric   StateNotNull =
5545ffd83dbSDimitry Andric       StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
5555ffd83dbSDimitry Andric   StateNull =
5565ffd83dbSDimitry Andric       StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
5570b57cec5SDimitry Andric 
5585ffd83dbSDimitry Andric   C.addTransition(StateNotNull,
5595ffd83dbSDimitry Andric                   constructNoteTag(C, RetSym, "Stream opened here"));
5605ffd83dbSDimitry Andric   C.addTransition(StateNull);
5610b57cec5SDimitry Andric }
5620b57cec5SDimitry Andric 
5635ffd83dbSDimitry Andric void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
5645ffd83dbSDimitry Andric                                CheckerContext &C) const {
5655ffd83dbSDimitry Andric   // Do not allow NULL as passed stream pointer but allow a closed stream.
5665ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
567fe6060f1SDimitry Andric   State = ensureStreamNonNull(getStreamArg(Desc, Call),
568fe6060f1SDimitry Andric                               Call.getArgExpr(Desc->StreamArgNo), C, State);
5695ffd83dbSDimitry Andric   if (!State)
5705ffd83dbSDimitry Andric     return;
5715ffd83dbSDimitry Andric 
5725ffd83dbSDimitry Andric   C.addTransition(State);
5735ffd83dbSDimitry Andric }
5745ffd83dbSDimitry Andric 
5755ffd83dbSDimitry Andric void StreamChecker::evalFreopen(const FnDescription *Desc,
5765ffd83dbSDimitry Andric                                 const CallEvent &Call,
577480093f4SDimitry Andric                                 CheckerContext &C) const {
578480093f4SDimitry Andric   ProgramStateRef State = C.getState();
5790b57cec5SDimitry Andric 
580480093f4SDimitry Andric   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
581480093f4SDimitry Andric   if (!CE)
5820b57cec5SDimitry Andric     return;
583480093f4SDimitry Andric 
584bdd1243dSDimitry Andric   std::optional<DefinedSVal> StreamVal =
5855ffd83dbSDimitry Andric       getStreamArg(Desc, Call).getAs<DefinedSVal>();
586480093f4SDimitry Andric   if (!StreamVal)
587480093f4SDimitry Andric     return;
588480093f4SDimitry Andric 
589480093f4SDimitry Andric   SymbolRef StreamSym = StreamVal->getAsSymbol();
5905ffd83dbSDimitry Andric   // Do not care about concrete values for stream ("(FILE *)0x12345"?).
5915ffd83dbSDimitry Andric   // FIXME: Can be stdin, stdout, stderr such values?
592480093f4SDimitry Andric   if (!StreamSym)
593480093f4SDimitry Andric     return;
594480093f4SDimitry Andric 
5955ffd83dbSDimitry Andric   // Do not handle untracked stream. It is probably escaped.
5965ffd83dbSDimitry Andric   if (!State->get<StreamMap>(StreamSym))
5975ffd83dbSDimitry Andric     return;
5985ffd83dbSDimitry Andric 
599480093f4SDimitry Andric   // Generate state for non-failed case.
600480093f4SDimitry Andric   // Return value is the passed stream pointer.
601480093f4SDimitry Andric   // According to the documentations, the stream is closed first
602480093f4SDimitry Andric   // but any close error is ignored. The state changes to (or remains) opened.
603480093f4SDimitry Andric   ProgramStateRef StateRetNotNull =
604480093f4SDimitry Andric       State->BindExpr(CE, C.getLocationContext(), *StreamVal);
605480093f4SDimitry Andric   // Generate state for NULL return value.
606480093f4SDimitry Andric   // Stream switches to OpenFailed state.
60781ad6265SDimitry Andric   ProgramStateRef StateRetNull =
60881ad6265SDimitry Andric       State->BindExpr(CE, C.getLocationContext(),
60981ad6265SDimitry Andric                       C.getSValBuilder().makeNullWithType(CE->getType()));
610480093f4SDimitry Andric 
611480093f4SDimitry Andric   StateRetNotNull =
6125ffd83dbSDimitry Andric       StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
613480093f4SDimitry Andric   StateRetNull =
6145ffd83dbSDimitry Andric       StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
615480093f4SDimitry Andric 
6165ffd83dbSDimitry Andric   C.addTransition(StateRetNotNull,
6175ffd83dbSDimitry Andric                   constructNoteTag(C, StreamSym, "Stream reopened here"));
618480093f4SDimitry Andric   C.addTransition(StateRetNull);
6190b57cec5SDimitry Andric }
6200b57cec5SDimitry Andric 
6215ffd83dbSDimitry Andric void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
6225ffd83dbSDimitry Andric                                CheckerContext &C) const {
623480093f4SDimitry Andric   ProgramStateRef State = C.getState();
6245ffd83dbSDimitry Andric   SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
6255ffd83dbSDimitry Andric   if (!Sym)
6265ffd83dbSDimitry Andric     return;
6275ffd83dbSDimitry Andric 
6285ffd83dbSDimitry Andric   const StreamState *SS = State->get<StreamMap>(Sym);
6295ffd83dbSDimitry Andric   if (!SS)
6305ffd83dbSDimitry Andric     return;
6315ffd83dbSDimitry Andric 
632bdd1243dSDimitry Andric   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
633bdd1243dSDimitry Andric   if (!CE)
634bdd1243dSDimitry Andric     return;
635bdd1243dSDimitry Andric 
6365ffd83dbSDimitry Andric   assertStreamStateOpened(SS);
6375ffd83dbSDimitry Andric 
6385ffd83dbSDimitry Andric   // Close the File Descriptor.
6395ffd83dbSDimitry Andric   // Regardless if the close fails or not, stream becomes "closed"
6405ffd83dbSDimitry Andric   // and can not be used any more.
6415ffd83dbSDimitry Andric   State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
6425ffd83dbSDimitry Andric 
643bdd1243dSDimitry Andric   // Return 0 on success, EOF on failure.
644bdd1243dSDimitry Andric   SValBuilder &SVB = C.getSValBuilder();
645bdd1243dSDimitry Andric   ProgramStateRef StateSuccess = State->BindExpr(
646bdd1243dSDimitry Andric       CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy));
647bdd1243dSDimitry Andric   ProgramStateRef StateFailure =
648bdd1243dSDimitry Andric       State->BindExpr(CE, C.getLocationContext(),
649bdd1243dSDimitry Andric                       SVB.makeIntVal(*EofVal, C.getASTContext().IntTy));
650bdd1243dSDimitry Andric 
651bdd1243dSDimitry Andric   C.addTransition(StateSuccess);
652bdd1243dSDimitry Andric   C.addTransition(StateFailure);
6530b57cec5SDimitry Andric }
6540b57cec5SDimitry Andric 
655*5f757f3fSDimitry Andric void StreamChecker::preReadWrite(const FnDescription *Desc,
656*5f757f3fSDimitry Andric                                  const CallEvent &Call, CheckerContext &C,
657*5f757f3fSDimitry Andric                                  bool IsRead) const {
658480093f4SDimitry Andric   ProgramStateRef State = C.getState();
6595ffd83dbSDimitry Andric   SVal StreamVal = getStreamArg(Desc, Call);
660fe6060f1SDimitry Andric   State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
661fe6060f1SDimitry Andric                               State);
6625ffd83dbSDimitry Andric   if (!State)
6635ffd83dbSDimitry Andric     return;
6645ffd83dbSDimitry Andric   State = ensureStreamOpened(StreamVal, C, State);
6655ffd83dbSDimitry Andric   if (!State)
6665ffd83dbSDimitry Andric     return;
6675ffd83dbSDimitry Andric   State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
6685ffd83dbSDimitry Andric   if (!State)
669480093f4SDimitry Andric     return;
670480093f4SDimitry Andric 
671*5f757f3fSDimitry Andric   if (!IsRead) {
672*5f757f3fSDimitry Andric     C.addTransition(State);
673*5f757f3fSDimitry Andric     return;
674*5f757f3fSDimitry Andric   }
675*5f757f3fSDimitry Andric 
6765ffd83dbSDimitry Andric   SymbolRef Sym = StreamVal.getAsSymbol();
6775ffd83dbSDimitry Andric   if (Sym && State->get<StreamMap>(Sym)) {
6785ffd83dbSDimitry Andric     const StreamState *SS = State->get<StreamMap>(Sym);
6795ffd83dbSDimitry Andric     if (SS->ErrorState & ErrorFEof)
680fe6060f1SDimitry Andric       reportFEofWarning(Sym, C, State);
6815ffd83dbSDimitry Andric   } else {
682480093f4SDimitry Andric     C.addTransition(State);
6835ffd83dbSDimitry Andric   }
684480093f4SDimitry Andric }
685480093f4SDimitry Andric 
6865ffd83dbSDimitry Andric void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
6875ffd83dbSDimitry Andric                                     const CallEvent &Call, CheckerContext &C,
6885ffd83dbSDimitry Andric                                     bool IsFread) const {
6895ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
6905ffd83dbSDimitry Andric   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
6915ffd83dbSDimitry Andric   if (!StreamSym)
6925ffd83dbSDimitry Andric     return;
6935ffd83dbSDimitry Andric 
6945ffd83dbSDimitry Andric   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
6955ffd83dbSDimitry Andric   if (!CE)
6965ffd83dbSDimitry Andric     return;
6975ffd83dbSDimitry Andric 
698bdd1243dSDimitry Andric   std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
6995ffd83dbSDimitry Andric   if (!SizeVal)
7005ffd83dbSDimitry Andric     return;
701bdd1243dSDimitry Andric   std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
7025ffd83dbSDimitry Andric   if (!NMembVal)
7035ffd83dbSDimitry Andric     return;
7045ffd83dbSDimitry Andric 
705fe6060f1SDimitry Andric   const StreamState *OldSS = State->get<StreamMap>(StreamSym);
706fe6060f1SDimitry Andric   if (!OldSS)
7075ffd83dbSDimitry Andric     return;
7085ffd83dbSDimitry Andric 
709fe6060f1SDimitry Andric   assertStreamStateOpened(OldSS);
7105ffd83dbSDimitry Andric 
7115ffd83dbSDimitry Andric   // C'99 standard, §7.19.8.1.3, the return value of fread:
7125ffd83dbSDimitry Andric   // The fread function returns the number of elements successfully read, which
7135ffd83dbSDimitry Andric   // may be less than nmemb if a read error or end-of-file is encountered. If
7145ffd83dbSDimitry Andric   // size or nmemb is zero, fread returns zero and the contents of the array and
7155ffd83dbSDimitry Andric   // the state of the stream remain unchanged.
7165ffd83dbSDimitry Andric 
7175ffd83dbSDimitry Andric   if (State->isNull(*SizeVal).isConstrainedTrue() ||
7185ffd83dbSDimitry Andric       State->isNull(*NMembVal).isConstrainedTrue()) {
7195ffd83dbSDimitry Andric     // This is the "size or nmemb is zero" case.
7205ffd83dbSDimitry Andric     // Just return 0, do nothing more (not clear the error flags).
7215ffd83dbSDimitry Andric     State = bindInt(0, State, C, CE);
7225ffd83dbSDimitry Andric     C.addTransition(State);
7235ffd83dbSDimitry Andric     return;
7245ffd83dbSDimitry Andric   }
7255ffd83dbSDimitry Andric 
7265ffd83dbSDimitry Andric   // Generate a transition for the success state.
7275ffd83dbSDimitry Andric   // If we know the state to be FEOF at fread, do not add a success state.
728fe6060f1SDimitry Andric   if (!IsFread || (OldSS->ErrorState != ErrorFEof)) {
7295ffd83dbSDimitry Andric     ProgramStateRef StateNotFailed =
7305ffd83dbSDimitry Andric         State->BindExpr(CE, C.getLocationContext(), *NMembVal);
73181ad6265SDimitry Andric     StateNotFailed =
73281ad6265SDimitry Andric         StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
7335ffd83dbSDimitry Andric     C.addTransition(StateNotFailed);
7345ffd83dbSDimitry Andric   }
7355ffd83dbSDimitry Andric 
7365ffd83dbSDimitry Andric   // Add transition for the failed state.
73781ad6265SDimitry Andric   NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
7385ffd83dbSDimitry Andric   ProgramStateRef StateFailed =
73981ad6265SDimitry Andric       State->BindExpr(CE, C.getLocationContext(), RetVal);
740*5f757f3fSDimitry Andric   SValBuilder &SVB = C.getSValBuilder();
74181ad6265SDimitry Andric   auto Cond =
742*5f757f3fSDimitry Andric       SVB.evalBinOpNN(State, BO_LT, RetVal, *NMembVal, SVB.getConditionType())
7435ffd83dbSDimitry Andric           .getAs<DefinedOrUnknownSVal>();
7445ffd83dbSDimitry Andric   if (!Cond)
7455ffd83dbSDimitry Andric     return;
7465ffd83dbSDimitry Andric   StateFailed = StateFailed->assume(*Cond, true);
7475ffd83dbSDimitry Andric   if (!StateFailed)
7485ffd83dbSDimitry Andric     return;
7495ffd83dbSDimitry Andric 
7505ffd83dbSDimitry Andric   StreamErrorState NewES;
7515ffd83dbSDimitry Andric   if (IsFread)
752fe6060f1SDimitry Andric     NewES =
753fe6060f1SDimitry Andric         (OldSS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError;
7545ffd83dbSDimitry Andric   else
7555ffd83dbSDimitry Andric     NewES = ErrorFError;
7565ffd83dbSDimitry Andric   // If a (non-EOF) error occurs, the resulting value of the file position
7575ffd83dbSDimitry Andric   // indicator for the stream is indeterminate.
758fe6060f1SDimitry Andric   StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
759fe6060f1SDimitry Andric   StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
760fe6060f1SDimitry Andric   if (IsFread && OldSS->ErrorState != ErrorFEof)
761fe6060f1SDimitry Andric     C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
762fe6060f1SDimitry Andric   else
7635ffd83dbSDimitry Andric     C.addTransition(StateFailed);
7645ffd83dbSDimitry Andric }
7655ffd83dbSDimitry Andric 
766*5f757f3fSDimitry Andric void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call,
767*5f757f3fSDimitry Andric                               CheckerContext &C, bool SingleChar) const {
768*5f757f3fSDimitry Andric   ProgramStateRef State = C.getState();
769*5f757f3fSDimitry Andric   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
770*5f757f3fSDimitry Andric   if (!StreamSym)
771*5f757f3fSDimitry Andric     return;
772*5f757f3fSDimitry Andric 
773*5f757f3fSDimitry Andric   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
774*5f757f3fSDimitry Andric   if (!CE)
775*5f757f3fSDimitry Andric     return;
776*5f757f3fSDimitry Andric 
777*5f757f3fSDimitry Andric   const StreamState *OldSS = State->get<StreamMap>(StreamSym);
778*5f757f3fSDimitry Andric   if (!OldSS)
779*5f757f3fSDimitry Andric     return;
780*5f757f3fSDimitry Andric 
781*5f757f3fSDimitry Andric   assertStreamStateOpened(OldSS);
782*5f757f3fSDimitry Andric 
783*5f757f3fSDimitry Andric   // `fgetc` returns the read character on success, otherwise returns EOF.
784*5f757f3fSDimitry Andric   // `fgets` returns the read buffer address on success, otherwise returns NULL.
785*5f757f3fSDimitry Andric 
786*5f757f3fSDimitry Andric   if (OldSS->ErrorState != ErrorFEof) {
787*5f757f3fSDimitry Andric     if (SingleChar) {
788*5f757f3fSDimitry Andric       // Generate a transition for the success state of `fgetc`.
789*5f757f3fSDimitry Andric       NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
790*5f757f3fSDimitry Andric       ProgramStateRef StateNotFailed =
791*5f757f3fSDimitry Andric           State->BindExpr(CE, C.getLocationContext(), RetVal);
792*5f757f3fSDimitry Andric       SValBuilder &SVB = C.getSValBuilder();
793*5f757f3fSDimitry Andric       ASTContext &ASTC = C.getASTContext();
794*5f757f3fSDimitry Andric       // The returned 'unsigned char' of `fgetc` is converted to 'int',
795*5f757f3fSDimitry Andric       // so we need to check if it is in range [0, 255].
796*5f757f3fSDimitry Andric       auto CondLow =
797*5f757f3fSDimitry Andric           SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy),
798*5f757f3fSDimitry Andric                         SVB.getConditionType())
799*5f757f3fSDimitry Andric               .getAs<DefinedOrUnknownSVal>();
800*5f757f3fSDimitry Andric       auto CondHigh =
801*5f757f3fSDimitry Andric           SVB.evalBinOp(State, BO_LE, RetVal,
802*5f757f3fSDimitry Andric                         SVB.makeIntVal(SVB.getBasicValueFactory()
803*5f757f3fSDimitry Andric                                            .getMaxValue(ASTC.UnsignedCharTy)
804*5f757f3fSDimitry Andric                                            .getLimitedValue(),
805*5f757f3fSDimitry Andric                                        ASTC.IntTy),
806*5f757f3fSDimitry Andric                         SVB.getConditionType())
807*5f757f3fSDimitry Andric               .getAs<DefinedOrUnknownSVal>();
808*5f757f3fSDimitry Andric       if (!CondLow || !CondHigh)
809*5f757f3fSDimitry Andric         return;
810*5f757f3fSDimitry Andric       StateNotFailed = StateNotFailed->assume(*CondLow, true);
811*5f757f3fSDimitry Andric       if (!StateNotFailed)
812*5f757f3fSDimitry Andric         return;
813*5f757f3fSDimitry Andric       StateNotFailed = StateNotFailed->assume(*CondHigh, true);
814*5f757f3fSDimitry Andric       if (!StateNotFailed)
815*5f757f3fSDimitry Andric         return;
816*5f757f3fSDimitry Andric       C.addTransition(StateNotFailed);
817*5f757f3fSDimitry Andric     } else {
818*5f757f3fSDimitry Andric       // Generate a transition for the success state of `fgets`.
819*5f757f3fSDimitry Andric       std::optional<DefinedSVal> GetBuf =
820*5f757f3fSDimitry Andric           Call.getArgSVal(0).getAs<DefinedSVal>();
821*5f757f3fSDimitry Andric       if (!GetBuf)
822*5f757f3fSDimitry Andric         return;
823*5f757f3fSDimitry Andric       ProgramStateRef StateNotFailed =
824*5f757f3fSDimitry Andric           State->BindExpr(CE, C.getLocationContext(), *GetBuf);
825*5f757f3fSDimitry Andric       StateNotFailed = StateNotFailed->set<StreamMap>(
826*5f757f3fSDimitry Andric           StreamSym, StreamState::getOpened(Desc));
827*5f757f3fSDimitry Andric       C.addTransition(StateNotFailed);
828*5f757f3fSDimitry Andric     }
829*5f757f3fSDimitry Andric   }
830*5f757f3fSDimitry Andric 
831*5f757f3fSDimitry Andric   // Add transition for the failed state.
832*5f757f3fSDimitry Andric   ProgramStateRef StateFailed;
833*5f757f3fSDimitry Andric   if (SingleChar)
834*5f757f3fSDimitry Andric     StateFailed = bindInt(*EofVal, State, C, CE);
835*5f757f3fSDimitry Andric   else
836*5f757f3fSDimitry Andric     StateFailed =
837*5f757f3fSDimitry Andric         State->BindExpr(CE, C.getLocationContext(),
838*5f757f3fSDimitry Andric                         C.getSValBuilder().makeNullWithType(CE->getType()));
839*5f757f3fSDimitry Andric 
840*5f757f3fSDimitry Andric   // If a (non-EOF) error occurs, the resulting value of the file position
841*5f757f3fSDimitry Andric   // indicator for the stream is indeterminate.
842*5f757f3fSDimitry Andric   StreamErrorState NewES =
843*5f757f3fSDimitry Andric       OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError;
844*5f757f3fSDimitry Andric   StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
845*5f757f3fSDimitry Andric   StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
846*5f757f3fSDimitry Andric   if (OldSS->ErrorState != ErrorFEof)
847*5f757f3fSDimitry Andric     C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
848*5f757f3fSDimitry Andric   else
849*5f757f3fSDimitry Andric     C.addTransition(StateFailed);
850*5f757f3fSDimitry Andric }
851*5f757f3fSDimitry Andric 
852*5f757f3fSDimitry Andric void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call,
853*5f757f3fSDimitry Andric                               CheckerContext &C, bool IsSingleChar) const {
854*5f757f3fSDimitry Andric   ProgramStateRef State = C.getState();
855*5f757f3fSDimitry Andric   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
856*5f757f3fSDimitry Andric   if (!StreamSym)
857*5f757f3fSDimitry Andric     return;
858*5f757f3fSDimitry Andric 
859*5f757f3fSDimitry Andric   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
860*5f757f3fSDimitry Andric   if (!CE)
861*5f757f3fSDimitry Andric     return;
862*5f757f3fSDimitry Andric 
863*5f757f3fSDimitry Andric   const StreamState *OldSS = State->get<StreamMap>(StreamSym);
864*5f757f3fSDimitry Andric   if (!OldSS)
865*5f757f3fSDimitry Andric     return;
866*5f757f3fSDimitry Andric 
867*5f757f3fSDimitry Andric   assertStreamStateOpened(OldSS);
868*5f757f3fSDimitry Andric 
869*5f757f3fSDimitry Andric   // `fputc` returns the written character on success, otherwise returns EOF.
870*5f757f3fSDimitry Andric   // `fputs` returns a non negative value on sucecess, otherwise returns EOF.
871*5f757f3fSDimitry Andric 
872*5f757f3fSDimitry Andric   if (IsSingleChar) {
873*5f757f3fSDimitry Andric     // Generate a transition for the success state of `fputc`.
874*5f757f3fSDimitry Andric     std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
875*5f757f3fSDimitry Andric     if (!PutVal)
876*5f757f3fSDimitry Andric       return;
877*5f757f3fSDimitry Andric     ProgramStateRef StateNotFailed =
878*5f757f3fSDimitry Andric         State->BindExpr(CE, C.getLocationContext(), *PutVal);
879*5f757f3fSDimitry Andric     StateNotFailed =
880*5f757f3fSDimitry Andric         StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
881*5f757f3fSDimitry Andric     C.addTransition(StateNotFailed);
882*5f757f3fSDimitry Andric   } else {
883*5f757f3fSDimitry Andric     // Generate a transition for the success state of `fputs`.
884*5f757f3fSDimitry Andric     NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
885*5f757f3fSDimitry Andric     ProgramStateRef StateNotFailed =
886*5f757f3fSDimitry Andric         State->BindExpr(CE, C.getLocationContext(), RetVal);
887*5f757f3fSDimitry Andric     SValBuilder &SVB = C.getSValBuilder();
888*5f757f3fSDimitry Andric     auto &ASTC = C.getASTContext();
889*5f757f3fSDimitry Andric     auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy),
890*5f757f3fSDimitry Andric                               SVB.getConditionType())
891*5f757f3fSDimitry Andric                     .getAs<DefinedOrUnknownSVal>();
892*5f757f3fSDimitry Andric     if (!Cond)
893*5f757f3fSDimitry Andric       return;
894*5f757f3fSDimitry Andric     StateNotFailed = StateNotFailed->assume(*Cond, true);
895*5f757f3fSDimitry Andric     if (!StateNotFailed)
896*5f757f3fSDimitry Andric       return;
897*5f757f3fSDimitry Andric     StateNotFailed =
898*5f757f3fSDimitry Andric         StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
899*5f757f3fSDimitry Andric     C.addTransition(StateNotFailed);
900*5f757f3fSDimitry Andric   }
901*5f757f3fSDimitry Andric 
902*5f757f3fSDimitry Andric   // Add transition for the failed state. The resulting value of the file
903*5f757f3fSDimitry Andric   // position indicator for the stream is indeterminate.
904*5f757f3fSDimitry Andric   ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
905*5f757f3fSDimitry Andric   StreamState NewSS = StreamState::getOpened(Desc, ErrorFError, true);
906*5f757f3fSDimitry Andric   StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
907*5f757f3fSDimitry Andric   C.addTransition(StateFailed);
908*5f757f3fSDimitry Andric }
909*5f757f3fSDimitry Andric 
9105ffd83dbSDimitry Andric void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
9115ffd83dbSDimitry Andric                              CheckerContext &C) const {
9125ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
9135ffd83dbSDimitry Andric   SVal StreamVal = getStreamArg(Desc, Call);
914fe6060f1SDimitry Andric   State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
915fe6060f1SDimitry Andric                               State);
9165ffd83dbSDimitry Andric   if (!State)
9175ffd83dbSDimitry Andric     return;
9185ffd83dbSDimitry Andric   State = ensureStreamOpened(StreamVal, C, State);
9195ffd83dbSDimitry Andric   if (!State)
9205ffd83dbSDimitry Andric     return;
9215ffd83dbSDimitry Andric   State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
9225ffd83dbSDimitry Andric   if (!State)
9235ffd83dbSDimitry Andric     return;
9245ffd83dbSDimitry Andric 
9255ffd83dbSDimitry Andric   C.addTransition(State);
9265ffd83dbSDimitry Andric }
9275ffd83dbSDimitry Andric 
9285ffd83dbSDimitry Andric void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
9295ffd83dbSDimitry Andric                               CheckerContext &C) const {
9305ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
9315ffd83dbSDimitry Andric   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
9325ffd83dbSDimitry Andric   if (!StreamSym)
9335ffd83dbSDimitry Andric     return;
9345ffd83dbSDimitry Andric 
9355ffd83dbSDimitry Andric   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
9365ffd83dbSDimitry Andric   if (!CE)
9375ffd83dbSDimitry Andric     return;
9385ffd83dbSDimitry Andric 
9395ffd83dbSDimitry Andric   // Ignore the call if the stream is not tracked.
9405ffd83dbSDimitry Andric   if (!State->get<StreamMap>(StreamSym))
9415ffd83dbSDimitry Andric     return;
9425ffd83dbSDimitry Andric 
94306c3fb27SDimitry Andric   const llvm::APSInt *PosV =
94406c3fb27SDimitry Andric       C.getSValBuilder().getKnownValue(State, Call.getArgSVal(1));
94506c3fb27SDimitry Andric   const llvm::APSInt *WhenceV =
94606c3fb27SDimitry Andric       C.getSValBuilder().getKnownValue(State, Call.getArgSVal(2));
94706c3fb27SDimitry Andric 
9485ffd83dbSDimitry Andric   DefinedSVal RetVal = makeRetVal(C, CE);
9495ffd83dbSDimitry Andric 
9505ffd83dbSDimitry Andric   // Make expression result.
9515ffd83dbSDimitry Andric   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
9525ffd83dbSDimitry Andric 
9535ffd83dbSDimitry Andric   // Bifurcate the state into failed and non-failed.
9545ffd83dbSDimitry Andric   // Return zero on success, nonzero on error.
9555ffd83dbSDimitry Andric   ProgramStateRef StateNotFailed, StateFailed;
9565ffd83dbSDimitry Andric   std::tie(StateFailed, StateNotFailed) =
9575ffd83dbSDimitry Andric       C.getConstraintManager().assumeDual(State, RetVal);
9585ffd83dbSDimitry Andric 
9595ffd83dbSDimitry Andric   // Reset the state to opened with no error.
9605ffd83dbSDimitry Andric   StateNotFailed =
9615ffd83dbSDimitry Andric       StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
9625ffd83dbSDimitry Andric   // We get error.
9635ffd83dbSDimitry Andric   // It is possible that fseek fails but sets none of the error flags.
9645ffd83dbSDimitry Andric   // If fseek failed, assume that the file position becomes indeterminate in any
9655ffd83dbSDimitry Andric   // case.
96606c3fb27SDimitry Andric   StreamErrorState NewErrS = ErrorNone | ErrorFError;
96706c3fb27SDimitry Andric   // Setting the position to start of file never produces EOF error.
96806c3fb27SDimitry Andric   if (!(PosV && *PosV == 0 && WhenceV && *WhenceV == SeekSetVal))
96906c3fb27SDimitry Andric     NewErrS = NewErrS | ErrorFEof;
9705ffd83dbSDimitry Andric   StateFailed = StateFailed->set<StreamMap>(
97106c3fb27SDimitry Andric       StreamSym, StreamState::getOpened(Desc, NewErrS, true));
9725ffd83dbSDimitry Andric 
9735ffd83dbSDimitry Andric   C.addTransition(StateNotFailed);
974fe6060f1SDimitry Andric   C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
9755ffd83dbSDimitry Andric }
9765ffd83dbSDimitry Andric 
977bdd1243dSDimitry Andric void StreamChecker::evalFgetpos(const FnDescription *Desc,
978bdd1243dSDimitry Andric                                 const CallEvent &Call,
979bdd1243dSDimitry Andric                                 CheckerContext &C) const {
980bdd1243dSDimitry Andric   ProgramStateRef State = C.getState();
981bdd1243dSDimitry Andric   SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
982bdd1243dSDimitry Andric   if (!Sym)
983bdd1243dSDimitry Andric     return;
984bdd1243dSDimitry Andric 
985bdd1243dSDimitry Andric   // Do not evaluate if stream is not found.
986bdd1243dSDimitry Andric   if (!State->get<StreamMap>(Sym))
987bdd1243dSDimitry Andric     return;
988bdd1243dSDimitry Andric 
989bdd1243dSDimitry Andric   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
990bdd1243dSDimitry Andric   if (!CE)
991bdd1243dSDimitry Andric     return;
992bdd1243dSDimitry Andric 
993bdd1243dSDimitry Andric   DefinedSVal RetVal = makeRetVal(C, CE);
994bdd1243dSDimitry Andric   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
995bdd1243dSDimitry Andric   ProgramStateRef StateNotFailed, StateFailed;
996bdd1243dSDimitry Andric   std::tie(StateFailed, StateNotFailed) =
997bdd1243dSDimitry Andric       C.getConstraintManager().assumeDual(State, RetVal);
998bdd1243dSDimitry Andric 
999bdd1243dSDimitry Andric   // This function does not affect the stream state.
1000bdd1243dSDimitry Andric   // Still we add success and failure state with the appropriate return value.
1001bdd1243dSDimitry Andric   // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
1002bdd1243dSDimitry Andric   C.addTransition(StateNotFailed);
1003bdd1243dSDimitry Andric   C.addTransition(StateFailed);
1004bdd1243dSDimitry Andric }
1005bdd1243dSDimitry Andric 
1006bdd1243dSDimitry Andric void StreamChecker::evalFsetpos(const FnDescription *Desc,
1007bdd1243dSDimitry Andric                                 const CallEvent &Call,
1008bdd1243dSDimitry Andric                                 CheckerContext &C) const {
1009bdd1243dSDimitry Andric   ProgramStateRef State = C.getState();
1010bdd1243dSDimitry Andric   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1011bdd1243dSDimitry Andric   if (!StreamSym)
1012bdd1243dSDimitry Andric     return;
1013bdd1243dSDimitry Andric 
1014bdd1243dSDimitry Andric   const StreamState *SS = State->get<StreamMap>(StreamSym);
1015bdd1243dSDimitry Andric   if (!SS)
1016bdd1243dSDimitry Andric     return;
1017bdd1243dSDimitry Andric 
1018bdd1243dSDimitry Andric   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1019bdd1243dSDimitry Andric   if (!CE)
1020bdd1243dSDimitry Andric     return;
1021bdd1243dSDimitry Andric 
1022bdd1243dSDimitry Andric   assertStreamStateOpened(SS);
1023bdd1243dSDimitry Andric 
1024bdd1243dSDimitry Andric   DefinedSVal RetVal = makeRetVal(C, CE);
1025bdd1243dSDimitry Andric   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
1026bdd1243dSDimitry Andric   ProgramStateRef StateNotFailed, StateFailed;
1027bdd1243dSDimitry Andric   std::tie(StateFailed, StateNotFailed) =
1028bdd1243dSDimitry Andric       C.getConstraintManager().assumeDual(State, RetVal);
1029bdd1243dSDimitry Andric 
1030bdd1243dSDimitry Andric   StateNotFailed = StateNotFailed->set<StreamMap>(
1031bdd1243dSDimitry Andric       StreamSym, StreamState::getOpened(Desc, ErrorNone, false));
1032bdd1243dSDimitry Andric 
1033bdd1243dSDimitry Andric   // At failure ferror could be set.
1034bdd1243dSDimitry Andric   // The standards do not tell what happens with the file position at failure.
1035bdd1243dSDimitry Andric   // But we can assume that it is dangerous to make a next I/O operation after
1036bdd1243dSDimitry Andric   // the position was not set correctly (similar to 'fseek').
1037bdd1243dSDimitry Andric   StateFailed = StateFailed->set<StreamMap>(
1038bdd1243dSDimitry Andric       StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
1039bdd1243dSDimitry Andric 
1040bdd1243dSDimitry Andric   C.addTransition(StateNotFailed);
1041bdd1243dSDimitry Andric   C.addTransition(StateFailed);
1042bdd1243dSDimitry Andric }
1043bdd1243dSDimitry Andric 
1044bdd1243dSDimitry Andric void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
1045bdd1243dSDimitry Andric                               CheckerContext &C) const {
1046bdd1243dSDimitry Andric   ProgramStateRef State = C.getState();
1047bdd1243dSDimitry Andric   SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
1048bdd1243dSDimitry Andric   if (!Sym)
1049bdd1243dSDimitry Andric     return;
1050bdd1243dSDimitry Andric 
1051bdd1243dSDimitry Andric   if (!State->get<StreamMap>(Sym))
1052bdd1243dSDimitry Andric     return;
1053bdd1243dSDimitry Andric 
1054bdd1243dSDimitry Andric   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1055bdd1243dSDimitry Andric   if (!CE)
1056bdd1243dSDimitry Andric     return;
1057bdd1243dSDimitry Andric 
1058bdd1243dSDimitry Andric   SValBuilder &SVB = C.getSValBuilder();
1059bdd1243dSDimitry Andric   NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
1060bdd1243dSDimitry Andric   ProgramStateRef StateNotFailed =
1061bdd1243dSDimitry Andric       State->BindExpr(CE, C.getLocationContext(), RetVal);
1062bdd1243dSDimitry Andric   auto Cond = SVB.evalBinOp(State, BO_GE, RetVal,
1063bdd1243dSDimitry Andric                             SVB.makeZeroVal(C.getASTContext().LongTy),
1064bdd1243dSDimitry Andric                             SVB.getConditionType())
1065bdd1243dSDimitry Andric                   .getAs<DefinedOrUnknownSVal>();
1066bdd1243dSDimitry Andric   if (!Cond)
1067bdd1243dSDimitry Andric     return;
1068bdd1243dSDimitry Andric   StateNotFailed = StateNotFailed->assume(*Cond, true);
1069bdd1243dSDimitry Andric   if (!StateNotFailed)
1070bdd1243dSDimitry Andric     return;
1071bdd1243dSDimitry Andric 
1072bdd1243dSDimitry Andric   ProgramStateRef StateFailed = State->BindExpr(
1073bdd1243dSDimitry Andric       CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy));
1074bdd1243dSDimitry Andric 
1075*5f757f3fSDimitry Andric   // This function does not affect the stream state.
1076*5f757f3fSDimitry Andric   // Still we add success and failure state with the appropriate return value.
1077*5f757f3fSDimitry Andric   // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
1078bdd1243dSDimitry Andric   C.addTransition(StateNotFailed);
1079bdd1243dSDimitry Andric   C.addTransition(StateFailed);
1080bdd1243dSDimitry Andric }
1081bdd1243dSDimitry Andric 
1082bdd1243dSDimitry Andric void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call,
1083bdd1243dSDimitry Andric                                CheckerContext &C) const {
1084bdd1243dSDimitry Andric   ProgramStateRef State = C.getState();
1085bdd1243dSDimitry Andric   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1086bdd1243dSDimitry Andric   if (!StreamSym)
1087bdd1243dSDimitry Andric     return;
1088bdd1243dSDimitry Andric 
1089bdd1243dSDimitry Andric   const StreamState *SS = State->get<StreamMap>(StreamSym);
1090bdd1243dSDimitry Andric   if (!SS)
1091bdd1243dSDimitry Andric     return;
1092bdd1243dSDimitry Andric 
1093bdd1243dSDimitry Andric   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1094bdd1243dSDimitry Andric   if (!CE)
1095bdd1243dSDimitry Andric     return;
1096bdd1243dSDimitry Andric 
1097bdd1243dSDimitry Andric   assertStreamStateOpened(SS);
1098bdd1243dSDimitry Andric 
1099bdd1243dSDimitry Andric   State = State->set<StreamMap>(StreamSym,
1100bdd1243dSDimitry Andric                                 StreamState::getOpened(Desc, ErrorNone, false));
1101bdd1243dSDimitry Andric 
1102bdd1243dSDimitry Andric   C.addTransition(State);
1103bdd1243dSDimitry Andric }
1104bdd1243dSDimitry Andric 
11055ffd83dbSDimitry Andric void StreamChecker::evalClearerr(const FnDescription *Desc,
11065ffd83dbSDimitry Andric                                  const CallEvent &Call,
11075ffd83dbSDimitry Andric                                  CheckerContext &C) const {
11085ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
11095ffd83dbSDimitry Andric   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
11105ffd83dbSDimitry Andric   if (!StreamSym)
11115ffd83dbSDimitry Andric     return;
11125ffd83dbSDimitry Andric 
11135ffd83dbSDimitry Andric   const StreamState *SS = State->get<StreamMap>(StreamSym);
11145ffd83dbSDimitry Andric   if (!SS)
11155ffd83dbSDimitry Andric     return;
11165ffd83dbSDimitry Andric 
11175ffd83dbSDimitry Andric   assertStreamStateOpened(SS);
11185ffd83dbSDimitry Andric 
11195ffd83dbSDimitry Andric   // FilePositionIndeterminate is not cleared.
11205ffd83dbSDimitry Andric   State = State->set<StreamMap>(
11215ffd83dbSDimitry Andric       StreamSym,
11225ffd83dbSDimitry Andric       StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate));
11235ffd83dbSDimitry Andric   C.addTransition(State);
11245ffd83dbSDimitry Andric }
11255ffd83dbSDimitry Andric 
11265ffd83dbSDimitry Andric void StreamChecker::evalFeofFerror(const FnDescription *Desc,
11275ffd83dbSDimitry Andric                                    const CallEvent &Call, CheckerContext &C,
11285ffd83dbSDimitry Andric                                    const StreamErrorState &ErrorKind) const {
11295ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
11305ffd83dbSDimitry Andric   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
11315ffd83dbSDimitry Andric   if (!StreamSym)
11325ffd83dbSDimitry Andric     return;
11335ffd83dbSDimitry Andric 
11345ffd83dbSDimitry Andric   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
11355ffd83dbSDimitry Andric   if (!CE)
11365ffd83dbSDimitry Andric     return;
11375ffd83dbSDimitry Andric 
11385ffd83dbSDimitry Andric   const StreamState *SS = State->get<StreamMap>(StreamSym);
11395ffd83dbSDimitry Andric   if (!SS)
11405ffd83dbSDimitry Andric     return;
11415ffd83dbSDimitry Andric 
11425ffd83dbSDimitry Andric   assertStreamStateOpened(SS);
11435ffd83dbSDimitry Andric 
11445ffd83dbSDimitry Andric   if (SS->ErrorState & ErrorKind) {
11455ffd83dbSDimitry Andric     // Execution path with error of ErrorKind.
11465ffd83dbSDimitry Andric     // Function returns true.
11475ffd83dbSDimitry Andric     // From now on it is the only one error state.
11485ffd83dbSDimitry Andric     ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE);
11495ffd83dbSDimitry Andric     C.addTransition(TrueState->set<StreamMap>(
11505ffd83dbSDimitry Andric         StreamSym, StreamState::getOpened(Desc, ErrorKind,
11515ffd83dbSDimitry Andric                                           SS->FilePositionIndeterminate &&
11525ffd83dbSDimitry Andric                                               !ErrorKind.isFEof())));
11535ffd83dbSDimitry Andric   }
11545ffd83dbSDimitry Andric   if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) {
11555ffd83dbSDimitry Andric     // Execution path(s) with ErrorKind not set.
11565ffd83dbSDimitry Andric     // Function returns false.
11575ffd83dbSDimitry Andric     // New error state is everything before minus ErrorKind.
11585ffd83dbSDimitry Andric     ProgramStateRef FalseState = bindInt(0, State, C, CE);
11595ffd83dbSDimitry Andric     C.addTransition(FalseState->set<StreamMap>(
11605ffd83dbSDimitry Andric         StreamSym,
11615ffd83dbSDimitry Andric         StreamState::getOpened(
11625ffd83dbSDimitry Andric             Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof())));
11635ffd83dbSDimitry Andric   }
11645ffd83dbSDimitry Andric }
11655ffd83dbSDimitry Andric 
11665ffd83dbSDimitry Andric void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
11675ffd83dbSDimitry Andric                                CheckerContext &C) const {
11685ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
11695ffd83dbSDimitry Andric   SVal StreamVal = getStreamArg(Desc, Call);
1170fe6060f1SDimitry Andric   State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1171fe6060f1SDimitry Andric                               State);
11725ffd83dbSDimitry Andric   if (!State)
11735ffd83dbSDimitry Andric     return;
11745ffd83dbSDimitry Andric   State = ensureStreamOpened(StreamVal, C, State);
11755ffd83dbSDimitry Andric   if (!State)
11765ffd83dbSDimitry Andric     return;
11775ffd83dbSDimitry Andric 
11785ffd83dbSDimitry Andric   C.addTransition(State);
11795ffd83dbSDimitry Andric }
11805ffd83dbSDimitry Andric 
11815ffd83dbSDimitry Andric void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
11825ffd83dbSDimitry Andric                                       const CallEvent &Call, CheckerContext &C,
11835ffd83dbSDimitry Andric                                       const StreamErrorState &ErrorKind) const {
11845ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
11855ffd83dbSDimitry Andric   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
11865ffd83dbSDimitry Andric   assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
11875ffd83dbSDimitry Andric   const StreamState *SS = State->get<StreamMap>(StreamSym);
11885ffd83dbSDimitry Andric   assert(SS && "Stream should be tracked by the checker.");
11895ffd83dbSDimitry Andric   State = State->set<StreamMap>(
11905ffd83dbSDimitry Andric       StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind));
11915ffd83dbSDimitry Andric   C.addTransition(State);
11925ffd83dbSDimitry Andric }
11935ffd83dbSDimitry Andric 
11945ffd83dbSDimitry Andric ProgramStateRef
1195fe6060f1SDimitry Andric StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
1196fe6060f1SDimitry Andric                                    CheckerContext &C,
11975ffd83dbSDimitry Andric                                    ProgramStateRef State) const {
11985ffd83dbSDimitry Andric   auto Stream = StreamVal.getAs<DefinedSVal>();
11995ffd83dbSDimitry Andric   if (!Stream)
12005ffd83dbSDimitry Andric     return State;
1201480093f4SDimitry Andric 
1202480093f4SDimitry Andric   ConstraintManager &CM = C.getConstraintManager();
12035ffd83dbSDimitry Andric 
1204480093f4SDimitry Andric   ProgramStateRef StateNotNull, StateNull;
1205*5f757f3fSDimitry Andric   std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream);
1206480093f4SDimitry Andric 
1207480093f4SDimitry Andric   if (!StateNotNull && StateNull) {
120806c3fb27SDimitry Andric     if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
120906c3fb27SDimitry Andric       auto R = std::make_unique<PathSensitiveBugReport>(
121006c3fb27SDimitry Andric           BT_FileNull, "Stream pointer might be NULL.", N);
121106c3fb27SDimitry Andric       if (StreamE)
121206c3fb27SDimitry Andric         bugreporter::trackExpressionValue(N, StreamE, *R);
121306c3fb27SDimitry Andric       C.emitReport(std::move(R));
121406c3fb27SDimitry Andric     }
12155ffd83dbSDimitry Andric     return nullptr;
1216480093f4SDimitry Andric   }
1217480093f4SDimitry Andric 
12185ffd83dbSDimitry Andric   return StateNotNull;
1219480093f4SDimitry Andric }
1220480093f4SDimitry Andric 
12215ffd83dbSDimitry Andric ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
12225ffd83dbSDimitry Andric                                                   CheckerContext &C,
12235ffd83dbSDimitry Andric                                                   ProgramStateRef State) const {
12245ffd83dbSDimitry Andric   SymbolRef Sym = StreamVal.getAsSymbol();
12255ffd83dbSDimitry Andric   if (!Sym)
12265ffd83dbSDimitry Andric     return State;
12275ffd83dbSDimitry Andric 
12285ffd83dbSDimitry Andric   const StreamState *SS = State->get<StreamMap>(Sym);
12295ffd83dbSDimitry Andric   if (!SS)
12305ffd83dbSDimitry Andric     return State;
12315ffd83dbSDimitry Andric 
12325ffd83dbSDimitry Andric   if (SS->isClosed()) {
12335ffd83dbSDimitry Andric     // Using a stream pointer after 'fclose' causes undefined behavior
12345ffd83dbSDimitry Andric     // according to cppreference.com .
12355ffd83dbSDimitry Andric     ExplodedNode *N = C.generateErrorNode();
12365ffd83dbSDimitry Andric     if (N) {
12375ffd83dbSDimitry Andric       C.emitReport(std::make_unique<PathSensitiveBugReport>(
12385ffd83dbSDimitry Andric           BT_UseAfterClose,
12395ffd83dbSDimitry Andric           "Stream might be already closed. Causes undefined behaviour.", N));
12405ffd83dbSDimitry Andric       return nullptr;
1241480093f4SDimitry Andric     }
1242480093f4SDimitry Andric 
12435ffd83dbSDimitry Andric     return State;
12445ffd83dbSDimitry Andric   }
12455ffd83dbSDimitry Andric 
12465ffd83dbSDimitry Andric   if (SS->isOpenFailed()) {
12475ffd83dbSDimitry Andric     // Using a stream that has failed to open is likely to cause problems.
12485ffd83dbSDimitry Andric     // This should usually not occur because stream pointer is NULL.
12495ffd83dbSDimitry Andric     // But freopen can cause a state when stream pointer remains non-null but
12505ffd83dbSDimitry Andric     // failed to open.
12515ffd83dbSDimitry Andric     ExplodedNode *N = C.generateErrorNode();
12525ffd83dbSDimitry Andric     if (N) {
12535ffd83dbSDimitry Andric       C.emitReport(std::make_unique<PathSensitiveBugReport>(
12545ffd83dbSDimitry Andric           BT_UseAfterOpenFailed,
12555ffd83dbSDimitry Andric           "Stream might be invalid after "
12565ffd83dbSDimitry Andric           "(re-)opening it has failed. "
12575ffd83dbSDimitry Andric           "Can cause undefined behaviour.",
12585ffd83dbSDimitry Andric           N));
12595ffd83dbSDimitry Andric       return nullptr;
12605ffd83dbSDimitry Andric     }
12615ffd83dbSDimitry Andric   }
12625ffd83dbSDimitry Andric 
12635ffd83dbSDimitry Andric   return State;
12645ffd83dbSDimitry Andric }
12655ffd83dbSDimitry Andric 
12665ffd83dbSDimitry Andric ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
12675ffd83dbSDimitry Andric     SVal StreamVal, CheckerContext &C, ProgramStateRef State) const {
12685ffd83dbSDimitry Andric   static const char *BugMessage =
12695ffd83dbSDimitry Andric       "File position of the stream might be 'indeterminate' "
12705ffd83dbSDimitry Andric       "after a failed operation. "
12715ffd83dbSDimitry Andric       "Can cause undefined behavior.";
12725ffd83dbSDimitry Andric 
12735ffd83dbSDimitry Andric   SymbolRef Sym = StreamVal.getAsSymbol();
12745ffd83dbSDimitry Andric   if (!Sym)
12755ffd83dbSDimitry Andric     return State;
12765ffd83dbSDimitry Andric 
12775ffd83dbSDimitry Andric   const StreamState *SS = State->get<StreamMap>(Sym);
12785ffd83dbSDimitry Andric   if (!SS)
12795ffd83dbSDimitry Andric     return State;
12805ffd83dbSDimitry Andric 
12815ffd83dbSDimitry Andric   assert(SS->isOpened() && "First ensure that stream is opened.");
12825ffd83dbSDimitry Andric 
12835ffd83dbSDimitry Andric   if (SS->FilePositionIndeterminate) {
12845ffd83dbSDimitry Andric     if (SS->ErrorState & ErrorFEof) {
12855ffd83dbSDimitry Andric       // The error is unknown but may be FEOF.
12865ffd83dbSDimitry Andric       // Continue analysis with the FEOF error state.
12875ffd83dbSDimitry Andric       // Report warning because the other possible error states.
12885ffd83dbSDimitry Andric       ExplodedNode *N = C.generateNonFatalErrorNode(State);
12895ffd83dbSDimitry Andric       if (!N)
12905ffd83dbSDimitry Andric         return nullptr;
12915ffd83dbSDimitry Andric 
12925ffd83dbSDimitry Andric       C.emitReport(std::make_unique<PathSensitiveBugReport>(
12935ffd83dbSDimitry Andric           BT_IndeterminatePosition, BugMessage, N));
12945ffd83dbSDimitry Andric       return State->set<StreamMap>(
12955ffd83dbSDimitry Andric           Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));
12965ffd83dbSDimitry Andric     }
12975ffd83dbSDimitry Andric 
12985ffd83dbSDimitry Andric     // Known or unknown error state without FEOF possible.
12995ffd83dbSDimitry Andric     // Stop analysis, report error.
13005ffd83dbSDimitry Andric     ExplodedNode *N = C.generateErrorNode(State);
13015ffd83dbSDimitry Andric     if (N)
13025ffd83dbSDimitry Andric       C.emitReport(std::make_unique<PathSensitiveBugReport>(
13035ffd83dbSDimitry Andric           BT_IndeterminatePosition, BugMessage, N));
13045ffd83dbSDimitry Andric 
13055ffd83dbSDimitry Andric     return nullptr;
13065ffd83dbSDimitry Andric   }
13075ffd83dbSDimitry Andric 
13085ffd83dbSDimitry Andric   return State;
13095ffd83dbSDimitry Andric }
13105ffd83dbSDimitry Andric 
13115ffd83dbSDimitry Andric ProgramStateRef
13125ffd83dbSDimitry Andric StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
13135ffd83dbSDimitry Andric                                         ProgramStateRef State) const {
1314bdd1243dSDimitry Andric   std::optional<nonloc::ConcreteInt> CI =
1315bdd1243dSDimitry Andric       WhenceVal.getAs<nonloc::ConcreteInt>();
13160b57cec5SDimitry Andric   if (!CI)
13175ffd83dbSDimitry Andric     return State;
13180b57cec5SDimitry Andric 
1319480093f4SDimitry Andric   int64_t X = CI->getValue().getSExtValue();
132006c3fb27SDimitry Andric   if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal)
13215ffd83dbSDimitry Andric     return State;
13220b57cec5SDimitry Andric 
1323480093f4SDimitry Andric   if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
13245ffd83dbSDimitry Andric     C.emitReport(std::make_unique<PathSensitiveBugReport>(
13255ffd83dbSDimitry Andric         BT_IllegalWhence,
13260b57cec5SDimitry Andric         "The whence argument to fseek() should be "
13275ffd83dbSDimitry Andric         "SEEK_SET, SEEK_END, or SEEK_CUR.",
13285ffd83dbSDimitry Andric         N));
13295ffd83dbSDimitry Andric     return nullptr;
13305ffd83dbSDimitry Andric   }
13315ffd83dbSDimitry Andric 
13325ffd83dbSDimitry Andric   return State;
13335ffd83dbSDimitry Andric }
13345ffd83dbSDimitry Andric 
1335fe6060f1SDimitry Andric void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
13365ffd83dbSDimitry Andric                                       ProgramStateRef State) const {
13375ffd83dbSDimitry Andric   if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1338fe6060f1SDimitry Andric     auto R = std::make_unique<PathSensitiveBugReport>(
13395ffd83dbSDimitry Andric         BT_StreamEof,
13405ffd83dbSDimitry Andric         "Read function called when stream is in EOF state. "
13415ffd83dbSDimitry Andric         "Function has no effect.",
1342fe6060f1SDimitry Andric         N);
1343fe6060f1SDimitry Andric     R->markInteresting(StreamSym);
1344fe6060f1SDimitry Andric     C.emitReport(std::move(R));
13455ffd83dbSDimitry Andric     return;
13460b57cec5SDimitry Andric   }
13475ffd83dbSDimitry Andric   C.addTransition(State);
13480b57cec5SDimitry Andric }
13490b57cec5SDimitry Andric 
1350e8d8bef9SDimitry Andric ExplodedNode *
1351e8d8bef9SDimitry Andric StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
1352e8d8bef9SDimitry Andric                            CheckerContext &C, ExplodedNode *Pred) const {
1353e8d8bef9SDimitry Andric   ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);
1354e8d8bef9SDimitry Andric   if (!Err)
1355e8d8bef9SDimitry Andric     return Pred;
13560b57cec5SDimitry Andric 
1357e8d8bef9SDimitry Andric   for (SymbolRef LeakSym : LeakedSyms) {
13585ffd83dbSDimitry Andric     // Resource leaks can result in multiple warning that describe the same kind
13595ffd83dbSDimitry Andric     // of programming error:
13605ffd83dbSDimitry Andric     //  void f() {
13615ffd83dbSDimitry Andric     //    FILE *F = fopen("a.txt");
13625ffd83dbSDimitry Andric     //    if (rand()) // state split
13635ffd83dbSDimitry Andric     //      return; // warning
13645ffd83dbSDimitry Andric     //  } // warning
13655ffd83dbSDimitry Andric     // While this isn't necessarily true (leaking the same stream could result
13665ffd83dbSDimitry Andric     // from a different kinds of errors), the reduction in redundant reports
13675ffd83dbSDimitry Andric     // makes this a worthwhile heuristic.
13685ffd83dbSDimitry Andric     // FIXME: Add a checker option to turn this uniqueing feature off.
1369e8d8bef9SDimitry Andric     const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
13705ffd83dbSDimitry Andric     assert(StreamOpenNode && "Could not find place of stream opening.");
137106c3fb27SDimitry Andric 
137206c3fb27SDimitry Andric     PathDiagnosticLocation LocUsedForUniqueing;
137306c3fb27SDimitry Andric     if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics())
137406c3fb27SDimitry Andric       LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
137506c3fb27SDimitry Andric           StreamStmt, C.getSourceManager(),
13765ffd83dbSDimitry Andric           StreamOpenNode->getLocationContext());
13775ffd83dbSDimitry Andric 
13785ffd83dbSDimitry Andric     std::unique_ptr<PathSensitiveBugReport> R =
13795ffd83dbSDimitry Andric         std::make_unique<PathSensitiveBugReport>(
13805ffd83dbSDimitry Andric             BT_ResourceLeak,
1381e8d8bef9SDimitry Andric             "Opened stream never closed. Potential resource leak.", Err,
13825ffd83dbSDimitry Andric             LocUsedForUniqueing,
13835ffd83dbSDimitry Andric             StreamOpenNode->getLocationContext()->getDecl());
1384e8d8bef9SDimitry Andric     R->markInteresting(LeakSym);
13855ffd83dbSDimitry Andric     C.emitReport(std::move(R));
13860b57cec5SDimitry Andric   }
1387e8d8bef9SDimitry Andric 
1388e8d8bef9SDimitry Andric   return Err;
1389e8d8bef9SDimitry Andric }
1390e8d8bef9SDimitry Andric 
1391e8d8bef9SDimitry Andric void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1392e8d8bef9SDimitry Andric                                      CheckerContext &C) const {
1393e8d8bef9SDimitry Andric   ProgramStateRef State = C.getState();
1394e8d8bef9SDimitry Andric 
1395e8d8bef9SDimitry Andric   llvm::SmallVector<SymbolRef, 2> LeakedSyms;
1396e8d8bef9SDimitry Andric 
1397e8d8bef9SDimitry Andric   const StreamMapTy &Map = State->get<StreamMap>();
1398e8d8bef9SDimitry Andric   for (const auto &I : Map) {
1399e8d8bef9SDimitry Andric     SymbolRef Sym = I.first;
1400e8d8bef9SDimitry Andric     const StreamState &SS = I.second;
1401e8d8bef9SDimitry Andric     if (!SymReaper.isDead(Sym))
1402e8d8bef9SDimitry Andric       continue;
1403e8d8bef9SDimitry Andric     if (SS.isOpened())
1404e8d8bef9SDimitry Andric       LeakedSyms.push_back(Sym);
1405e8d8bef9SDimitry Andric     State = State->remove<StreamMap>(Sym);
1406e8d8bef9SDimitry Andric   }
1407e8d8bef9SDimitry Andric 
1408e8d8bef9SDimitry Andric   ExplodedNode *N = C.getPredecessor();
1409e8d8bef9SDimitry Andric   if (!LeakedSyms.empty())
1410e8d8bef9SDimitry Andric     N = reportLeaks(LeakedSyms, C, N);
1411e8d8bef9SDimitry Andric 
1412e8d8bef9SDimitry Andric   C.addTransition(State, N);
14130b57cec5SDimitry Andric }
14140b57cec5SDimitry Andric 
14155ffd83dbSDimitry Andric ProgramStateRef StreamChecker::checkPointerEscape(
14165ffd83dbSDimitry Andric     ProgramStateRef State, const InvalidatedSymbols &Escaped,
14175ffd83dbSDimitry Andric     const CallEvent *Call, PointerEscapeKind Kind) const {
14185ffd83dbSDimitry Andric   // Check for file-handling system call that is not handled by the checker.
14195ffd83dbSDimitry Andric   // FIXME: The checker should be updated to handle all system calls that take
14205ffd83dbSDimitry Andric   // 'FILE*' argument. These are now ignored.
14215ffd83dbSDimitry Andric   if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader())
14225ffd83dbSDimitry Andric     return State;
14235ffd83dbSDimitry Andric 
14245ffd83dbSDimitry Andric   for (SymbolRef Sym : Escaped) {
14255ffd83dbSDimitry Andric     // The symbol escaped.
14265ffd83dbSDimitry Andric     // From now the stream can be manipulated in unknown way to the checker,
14275ffd83dbSDimitry Andric     // it is not possible to handle it any more.
14285ffd83dbSDimitry Andric     // Optimistically, assume that the corresponding file handle will be closed
14295ffd83dbSDimitry Andric     // somewhere else.
14305ffd83dbSDimitry Andric     // Remove symbol from state so the following stream calls on this symbol are
14315ffd83dbSDimitry Andric     // not handled by the checker.
14325ffd83dbSDimitry Andric     State = State->remove<StreamMap>(Sym);
14335ffd83dbSDimitry Andric   }
14345ffd83dbSDimitry Andric   return State;
14350b57cec5SDimitry Andric }
14360b57cec5SDimitry Andric 
1437fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
1438fe6060f1SDimitry Andric // Checker registration.
1439fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
1440fe6060f1SDimitry Andric 
14415ffd83dbSDimitry Andric void ento::registerStreamChecker(CheckerManager &Mgr) {
14425ffd83dbSDimitry Andric   Mgr.registerChecker<StreamChecker>();
14435ffd83dbSDimitry Andric }
14445ffd83dbSDimitry Andric 
14455ffd83dbSDimitry Andric bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
14465ffd83dbSDimitry Andric   return true;
14475ffd83dbSDimitry Andric }
14485ffd83dbSDimitry Andric 
14495ffd83dbSDimitry Andric void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
14505ffd83dbSDimitry Andric   auto *Checker = Mgr.getChecker<StreamChecker>();
14515ffd83dbSDimitry Andric   Checker->TestMode = true;
14525ffd83dbSDimitry Andric }
14535ffd83dbSDimitry Andric 
14545ffd83dbSDimitry Andric bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
14550b57cec5SDimitry Andric   return true;
14560b57cec5SDimitry Andric }
1457