xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
13*0fca6ea1SDimitry Andric #include "NoOwnershipChangeVisitor.h"
14*0fca6ea1SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
15*0fca6ea1SDimitry Andric #include "clang/ASTMatchers/ASTMatchers.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
20349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
23bdd1243dSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
250b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
260b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
27*0fca6ea1SDimitry Andric #include "llvm/ADT/Sequence.h"
28480093f4SDimitry Andric #include <functional>
29bdd1243dSDimitry Andric #include <optional>
300b57cec5SDimitry Andric 
310b57cec5SDimitry Andric using namespace clang;
320b57cec5SDimitry Andric using namespace ento;
33480093f4SDimitry Andric using namespace std::placeholders;
340b57cec5SDimitry Andric 
35fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
36fe6060f1SDimitry Andric // Definition of state data structures.
37fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
38fe6060f1SDimitry Andric 
390b57cec5SDimitry Andric namespace {
400b57cec5SDimitry Andric 
415ffd83dbSDimitry Andric struct FnDescription;
420b57cec5SDimitry Andric 
435ffd83dbSDimitry Andric /// State of the stream error flags.
445ffd83dbSDimitry Andric /// Sometimes it is not known to the checker what error flags are set.
455ffd83dbSDimitry Andric /// This is indicated by setting more than one flag to true.
465ffd83dbSDimitry Andric /// This is an optimization to avoid state splits.
475ffd83dbSDimitry Andric /// A stream can either be in FEOF or FERROR but not both at the same time.
485ffd83dbSDimitry Andric /// Multiple flags are set to handle the corresponding states together.
495ffd83dbSDimitry Andric struct StreamErrorState {
505ffd83dbSDimitry Andric   /// The stream can be in state where none of the error flags set.
515ffd83dbSDimitry Andric   bool NoError = true;
525ffd83dbSDimitry Andric   /// The stream can be in state where the EOF indicator is set.
535ffd83dbSDimitry Andric   bool FEof = false;
545ffd83dbSDimitry Andric   /// The stream can be in state where the error indicator is set.
555ffd83dbSDimitry Andric   bool FError = false;
560b57cec5SDimitry Andric 
isNoError__anon299e83ef0111::StreamErrorState575ffd83dbSDimitry Andric   bool isNoError() const { return NoError && !FEof && !FError; }
isFEof__anon299e83ef0111::StreamErrorState585ffd83dbSDimitry Andric   bool isFEof() const { return !NoError && FEof && !FError; }
isFError__anon299e83ef0111::StreamErrorState595ffd83dbSDimitry Andric   bool isFError() const { return !NoError && !FEof && FError; }
600b57cec5SDimitry Andric 
operator ==__anon299e83ef0111::StreamErrorState615ffd83dbSDimitry Andric   bool operator==(const StreamErrorState &ES) const {
625ffd83dbSDimitry Andric     return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
635ffd83dbSDimitry Andric   }
640b57cec5SDimitry Andric 
operator !=__anon299e83ef0111::StreamErrorState655ffd83dbSDimitry Andric   bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); }
665ffd83dbSDimitry Andric 
operator |__anon299e83ef0111::StreamErrorState675ffd83dbSDimitry Andric   StreamErrorState operator|(const StreamErrorState &E) const {
685ffd83dbSDimitry Andric     return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
695ffd83dbSDimitry Andric   }
705ffd83dbSDimitry Andric 
operator &__anon299e83ef0111::StreamErrorState715ffd83dbSDimitry Andric   StreamErrorState operator&(const StreamErrorState &E) const {
725ffd83dbSDimitry Andric     return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
735ffd83dbSDimitry Andric   }
745ffd83dbSDimitry Andric 
operator ~__anon299e83ef0111::StreamErrorState755ffd83dbSDimitry Andric   StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; }
765ffd83dbSDimitry Andric 
775ffd83dbSDimitry Andric   /// Returns if the StreamErrorState is a valid object.
operator bool__anon299e83ef0111::StreamErrorState785ffd83dbSDimitry Andric   operator bool() const { return NoError || FEof || FError; }
790b57cec5SDimitry Andric 
dump__anon299e83ef0111::StreamErrorState80*0fca6ea1SDimitry Andric   LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
dumpToStream__anon299e83ef0111::StreamErrorState81*0fca6ea1SDimitry Andric   LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &os) const {
82*0fca6ea1SDimitry Andric     os << "NoError: " << NoError << ", FEof: " << FEof
83*0fca6ea1SDimitry Andric        << ", FError: " << FError;
84*0fca6ea1SDimitry Andric   }
85*0fca6ea1SDimitry Andric 
Profile__anon299e83ef0111::StreamErrorState860b57cec5SDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const {
875ffd83dbSDimitry Andric     ID.AddBoolean(NoError);
885ffd83dbSDimitry Andric     ID.AddBoolean(FEof);
895ffd83dbSDimitry Andric     ID.AddBoolean(FError);
900b57cec5SDimitry Andric   }
910b57cec5SDimitry Andric };
920b57cec5SDimitry Andric 
935ffd83dbSDimitry Andric const StreamErrorState ErrorNone{true, false, false};
945ffd83dbSDimitry Andric const StreamErrorState ErrorFEof{false, true, false};
955ffd83dbSDimitry Andric const StreamErrorState ErrorFError{false, false, true};
960b57cec5SDimitry Andric 
975ffd83dbSDimitry Andric /// Full state information about a stream pointer.
985ffd83dbSDimitry Andric struct StreamState {
995ffd83dbSDimitry Andric   /// The last file operation called in the stream.
100bdd1243dSDimitry Andric   /// Can be nullptr.
1015ffd83dbSDimitry Andric   const FnDescription *LastOperation;
1020b57cec5SDimitry Andric 
1035ffd83dbSDimitry Andric   /// State of a stream symbol.
1045ffd83dbSDimitry Andric   enum KindTy {
1055ffd83dbSDimitry Andric     Opened, /// Stream is opened.
1065ffd83dbSDimitry Andric     Closed, /// Closed stream (an invalid stream pointer after it was closed).
1075ffd83dbSDimitry Andric     OpenFailed /// The last open operation has failed.
1085ffd83dbSDimitry Andric   } State;
1090b57cec5SDimitry Andric 
getKindStr__anon299e83ef0111::StreamState110*0fca6ea1SDimitry Andric   StringRef getKindStr() const {
111*0fca6ea1SDimitry Andric     switch (State) {
112*0fca6ea1SDimitry Andric     case Opened:
113*0fca6ea1SDimitry Andric       return "Opened";
114*0fca6ea1SDimitry Andric     case Closed:
115*0fca6ea1SDimitry Andric       return "Closed";
116*0fca6ea1SDimitry Andric     case OpenFailed:
117*0fca6ea1SDimitry Andric       return "OpenFailed";
118*0fca6ea1SDimitry Andric     }
119*0fca6ea1SDimitry Andric     llvm_unreachable("Unknown StreamState!");
120*0fca6ea1SDimitry Andric   }
121*0fca6ea1SDimitry Andric 
1225ffd83dbSDimitry Andric   /// State of the error flags.
1235ffd83dbSDimitry Andric   /// Ignored in non-opened stream state but must be NoError.
1245ffd83dbSDimitry Andric   StreamErrorState const ErrorState;
1255ffd83dbSDimitry Andric 
1265ffd83dbSDimitry Andric   /// Indicate if the file has an "indeterminate file position indicator".
1275ffd83dbSDimitry Andric   /// This can be set at a failing read or write or seek operation.
1285ffd83dbSDimitry Andric   /// If it is set no more read or write is allowed.
1295ffd83dbSDimitry Andric   /// This value is not dependent on the stream error flags:
1305ffd83dbSDimitry Andric   /// The error flag may be cleared with `clearerr` but the file position
1315ffd83dbSDimitry Andric   /// remains still indeterminate.
1325ffd83dbSDimitry Andric   /// This value applies to all error states in ErrorState except FEOF.
1335ffd83dbSDimitry Andric   /// An EOF+indeterminate state is the same as EOF state.
1345ffd83dbSDimitry Andric   bool const FilePositionIndeterminate = false;
1355ffd83dbSDimitry Andric 
StreamState__anon299e83ef0111::StreamState1365ffd83dbSDimitry Andric   StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES,
1375ffd83dbSDimitry Andric               bool IsFilePositionIndeterminate)
1385ffd83dbSDimitry Andric       : LastOperation(L), State(S), ErrorState(ES),
1395ffd83dbSDimitry Andric         FilePositionIndeterminate(IsFilePositionIndeterminate) {
1405ffd83dbSDimitry Andric     assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
1415ffd83dbSDimitry Andric            "FilePositionIndeterminate should be false in FEof case.");
1425ffd83dbSDimitry Andric     assert((State == Opened || ErrorState.isNoError()) &&
1435ffd83dbSDimitry Andric            "ErrorState should be None in non-opened stream state.");
1445ffd83dbSDimitry Andric   }
1455ffd83dbSDimitry Andric 
isOpened__anon299e83ef0111::StreamState1465ffd83dbSDimitry Andric   bool isOpened() const { return State == Opened; }
isClosed__anon299e83ef0111::StreamState1475ffd83dbSDimitry Andric   bool isClosed() const { return State == Closed; }
isOpenFailed__anon299e83ef0111::StreamState1485ffd83dbSDimitry Andric   bool isOpenFailed() const { return State == OpenFailed; }
1495ffd83dbSDimitry Andric 
operator ==__anon299e83ef0111::StreamState1505ffd83dbSDimitry Andric   bool operator==(const StreamState &X) const {
1515ffd83dbSDimitry Andric     // In not opened state error state should always NoError, so comparison
1525ffd83dbSDimitry Andric     // here is no problem.
1535ffd83dbSDimitry Andric     return LastOperation == X.LastOperation && State == X.State &&
1545ffd83dbSDimitry Andric            ErrorState == X.ErrorState &&
1555ffd83dbSDimitry Andric            FilePositionIndeterminate == X.FilePositionIndeterminate;
1565ffd83dbSDimitry Andric   }
1575ffd83dbSDimitry Andric 
getOpened__anon299e83ef0111::StreamState1585ffd83dbSDimitry Andric   static StreamState getOpened(const FnDescription *L,
1595ffd83dbSDimitry Andric                                const StreamErrorState &ES = ErrorNone,
1605ffd83dbSDimitry Andric                                bool IsFilePositionIndeterminate = false) {
1615ffd83dbSDimitry Andric     return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
1625ffd83dbSDimitry Andric   }
getClosed__anon299e83ef0111::StreamState1635ffd83dbSDimitry Andric   static StreamState getClosed(const FnDescription *L) {
1645ffd83dbSDimitry Andric     return StreamState{L, Closed, {}, false};
1655ffd83dbSDimitry Andric   }
getOpenFailed__anon299e83ef0111::StreamState1665ffd83dbSDimitry Andric   static StreamState getOpenFailed(const FnDescription *L) {
1675ffd83dbSDimitry Andric     return StreamState{L, OpenFailed, {}, false};
1685ffd83dbSDimitry Andric   }
1695ffd83dbSDimitry Andric 
dump__anon299e83ef0111::StreamState170*0fca6ea1SDimitry Andric   LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
171*0fca6ea1SDimitry Andric   LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &os) const;
172*0fca6ea1SDimitry Andric 
Profile__anon299e83ef0111::StreamState1735ffd83dbSDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const {
1745ffd83dbSDimitry Andric     ID.AddPointer(LastOperation);
1755ffd83dbSDimitry Andric     ID.AddInteger(State);
17681ad6265SDimitry Andric     ErrorState.Profile(ID);
1775ffd83dbSDimitry Andric     ID.AddBoolean(FilePositionIndeterminate);
1785ffd83dbSDimitry Andric   }
179480093f4SDimitry Andric };
1800b57cec5SDimitry Andric 
181fe6060f1SDimitry Andric } // namespace
182fe6060f1SDimitry Andric 
183*0fca6ea1SDimitry Andric // This map holds the state of a stream.
184*0fca6ea1SDimitry Andric // The stream is identified with a SymbolRef that is created when a stream
185*0fca6ea1SDimitry Andric // opening function is modeled by the checker.
186*0fca6ea1SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
187*0fca6ea1SDimitry Andric 
188fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
189fe6060f1SDimitry Andric // StreamChecker class and utility functions.
190fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
191fe6060f1SDimitry Andric 
192fe6060f1SDimitry Andric namespace {
193fe6060f1SDimitry Andric 
1945ffd83dbSDimitry Andric class StreamChecker;
1955ffd83dbSDimitry Andric using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
1965ffd83dbSDimitry Andric                                    const CallEvent &, CheckerContext &)>;
197480093f4SDimitry Andric 
1985ffd83dbSDimitry Andric using ArgNoTy = unsigned int;
1995ffd83dbSDimitry Andric static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
2005ffd83dbSDimitry Andric 
201*0fca6ea1SDimitry Andric const char *FeofNote = "Assuming stream reaches end-of-file here";
202*0fca6ea1SDimitry Andric const char *FerrorNote = "Assuming this stream operation fails";
203*0fca6ea1SDimitry Andric 
2045ffd83dbSDimitry Andric struct FnDescription {
2055ffd83dbSDimitry Andric   FnCheck PreFn;
2065ffd83dbSDimitry Andric   FnCheck EvalFn;
2075ffd83dbSDimitry Andric   ArgNoTy StreamArgNo;
2085ffd83dbSDimitry Andric };
2095ffd83dbSDimitry Andric 
dumpToStream(llvm::raw_ostream & os) const210*0fca6ea1SDimitry Andric LLVM_DUMP_METHOD void StreamState::dumpToStream(llvm::raw_ostream &os) const {
211*0fca6ea1SDimitry Andric   os << "{Kind: " << getKindStr() << ", Last operation: " << LastOperation
212*0fca6ea1SDimitry Andric      << ", ErrorState: ";
213*0fca6ea1SDimitry Andric   ErrorState.dumpToStream(os);
214*0fca6ea1SDimitry Andric   os << ", FilePos: " << (FilePositionIndeterminate ? "Indeterminate" : "OK")
215*0fca6ea1SDimitry Andric      << '}';
216*0fca6ea1SDimitry Andric }
217*0fca6ea1SDimitry Andric 
2185ffd83dbSDimitry Andric /// Get the value of the stream argument out of the passed call event.
2195ffd83dbSDimitry Andric /// The call should contain a function that is described by Desc.
getStreamArg(const FnDescription * Desc,const CallEvent & Call)2205ffd83dbSDimitry Andric SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {
2215ffd83dbSDimitry Andric   assert(Desc && Desc->StreamArgNo != ArgNone &&
2225ffd83dbSDimitry Andric          "Try to get a non-existing stream argument.");
2235ffd83dbSDimitry Andric   return Call.getArgSVal(Desc->StreamArgNo);
2245ffd83dbSDimitry Andric }
2255ffd83dbSDimitry Andric 
2265ffd83dbSDimitry Andric /// Create a conjured symbol return value for a call expression.
makeRetVal(CheckerContext & C,const CallExpr * CE)2275ffd83dbSDimitry Andric DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) {
2285ffd83dbSDimitry Andric   assert(CE && "Expecting a call expression.");
2295ffd83dbSDimitry Andric 
2305ffd83dbSDimitry Andric   const LocationContext *LCtx = C.getLocationContext();
2315ffd83dbSDimitry Andric   return C.getSValBuilder()
2325ffd83dbSDimitry Andric       .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
2335ffd83dbSDimitry Andric       .castAs<DefinedSVal>();
2345ffd83dbSDimitry Andric }
2355ffd83dbSDimitry Andric 
bindAndAssumeTrue(ProgramStateRef State,CheckerContext & C,const CallExpr * CE)2365ffd83dbSDimitry Andric ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
2375ffd83dbSDimitry Andric                                   const CallExpr *CE) {
2385ffd83dbSDimitry Andric   DefinedSVal RetVal = makeRetVal(C, CE);
2395ffd83dbSDimitry Andric   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
2405ffd83dbSDimitry Andric   State = State->assume(RetVal, true);
2415ffd83dbSDimitry Andric   assert(State && "Assumption on new value should not fail.");
2425ffd83dbSDimitry Andric   return State;
2435ffd83dbSDimitry Andric }
2445ffd83dbSDimitry Andric 
bindInt(uint64_t Value,ProgramStateRef State,CheckerContext & C,const CallExpr * CE)2455ffd83dbSDimitry Andric ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
2465ffd83dbSDimitry Andric                         CheckerContext &C, const CallExpr *CE) {
2475ffd83dbSDimitry Andric   State = State->BindExpr(CE, C.getLocationContext(),
248bdd1243dSDimitry Andric                           C.getSValBuilder().makeIntVal(Value, CE->getType()));
2495ffd83dbSDimitry Andric   return State;
2505ffd83dbSDimitry Andric }
2515ffd83dbSDimitry Andric 
assertStreamStateOpened(const StreamState * SS)252*0fca6ea1SDimitry Andric inline void assertStreamStateOpened(const StreamState *SS) {
253*0fca6ea1SDimitry Andric   assert(SS->isOpened() && "Stream is expected to be opened");
254*0fca6ea1SDimitry Andric }
255*0fca6ea1SDimitry Andric 
2565ffd83dbSDimitry Andric class StreamChecker : public Checker<check::PreCall, eval::Call,
2575ffd83dbSDimitry Andric                                      check::DeadSymbols, check::PointerEscape> {
25806c3fb27SDimitry Andric   BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
2595ffd83dbSDimitry Andric   BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
2605ffd83dbSDimitry Andric   BugType BT_UseAfterOpenFailed{this, "Invalid stream",
2615ffd83dbSDimitry Andric                                 "Stream handling error"};
2625ffd83dbSDimitry Andric   BugType BT_IndeterminatePosition{this, "Invalid stream state",
2635ffd83dbSDimitry Andric                                    "Stream handling error"};
2645ffd83dbSDimitry Andric   BugType BT_IllegalWhence{this, "Illegal whence argument",
2655ffd83dbSDimitry Andric                            "Stream handling error"};
2665ffd83dbSDimitry Andric   BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};
267e8d8bef9SDimitry Andric   BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error",
268e8d8bef9SDimitry Andric                           /*SuppressOnSink =*/true};
2695ffd83dbSDimitry Andric 
2705ffd83dbSDimitry Andric public:
2715ffd83dbSDimitry Andric   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
2725ffd83dbSDimitry Andric   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
2735ffd83dbSDimitry Andric   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
2745ffd83dbSDimitry Andric   ProgramStateRef checkPointerEscape(ProgramStateRef State,
2755ffd83dbSDimitry Andric                                      const InvalidatedSymbols &Escaped,
2765ffd83dbSDimitry Andric                                      const CallEvent *Call,
2775ffd83dbSDimitry Andric                                      PointerEscapeKind Kind) const;
2785ffd83dbSDimitry Andric 
getBT_StreamEof() const279*0fca6ea1SDimitry Andric   const BugType *getBT_StreamEof() const { return &BT_StreamEof; }
getBT_IndeterminatePosition() const280*0fca6ea1SDimitry Andric   const BugType *getBT_IndeterminatePosition() const {
281*0fca6ea1SDimitry Andric     return &BT_IndeterminatePosition;
282*0fca6ea1SDimitry Andric   }
283*0fca6ea1SDimitry Andric 
constructSetEofNoteTag(CheckerContext & C,SymbolRef StreamSym) const284*0fca6ea1SDimitry Andric   const NoteTag *constructSetEofNoteTag(CheckerContext &C,
285*0fca6ea1SDimitry Andric                                         SymbolRef StreamSym) const {
286*0fca6ea1SDimitry Andric     return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
287*0fca6ea1SDimitry Andric       if (!BR.isInteresting(StreamSym) ||
288*0fca6ea1SDimitry Andric           &BR.getBugType() != this->getBT_StreamEof())
289*0fca6ea1SDimitry Andric         return "";
290*0fca6ea1SDimitry Andric 
291*0fca6ea1SDimitry Andric       BR.markNotInteresting(StreamSym);
292*0fca6ea1SDimitry Andric 
293*0fca6ea1SDimitry Andric       return FeofNote;
294*0fca6ea1SDimitry Andric     });
295*0fca6ea1SDimitry Andric   }
296*0fca6ea1SDimitry Andric 
constructSetErrorNoteTag(CheckerContext & C,SymbolRef StreamSym) const297*0fca6ea1SDimitry Andric   const NoteTag *constructSetErrorNoteTag(CheckerContext &C,
298*0fca6ea1SDimitry Andric                                           SymbolRef StreamSym) const {
299*0fca6ea1SDimitry Andric     return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
300*0fca6ea1SDimitry Andric       if (!BR.isInteresting(StreamSym) ||
301*0fca6ea1SDimitry Andric           &BR.getBugType() != this->getBT_IndeterminatePosition())
302*0fca6ea1SDimitry Andric         return "";
303*0fca6ea1SDimitry Andric 
304*0fca6ea1SDimitry Andric       BR.markNotInteresting(StreamSym);
305*0fca6ea1SDimitry Andric 
306*0fca6ea1SDimitry Andric       return FerrorNote;
307*0fca6ea1SDimitry Andric     });
308*0fca6ea1SDimitry Andric   }
309*0fca6ea1SDimitry Andric 
constructSetEofOrErrorNoteTag(CheckerContext & C,SymbolRef StreamSym) const310*0fca6ea1SDimitry Andric   const NoteTag *constructSetEofOrErrorNoteTag(CheckerContext &C,
311*0fca6ea1SDimitry Andric                                                SymbolRef StreamSym) const {
312*0fca6ea1SDimitry Andric     return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
313*0fca6ea1SDimitry Andric       if (!BR.isInteresting(StreamSym))
314*0fca6ea1SDimitry Andric         return "";
315*0fca6ea1SDimitry Andric 
316*0fca6ea1SDimitry Andric       if (&BR.getBugType() == this->getBT_StreamEof()) {
317*0fca6ea1SDimitry Andric         BR.markNotInteresting(StreamSym);
318*0fca6ea1SDimitry Andric         return FeofNote;
319*0fca6ea1SDimitry Andric       }
320*0fca6ea1SDimitry Andric       if (&BR.getBugType() == this->getBT_IndeterminatePosition()) {
321*0fca6ea1SDimitry Andric         BR.markNotInteresting(StreamSym);
322*0fca6ea1SDimitry Andric         return FerrorNote;
323*0fca6ea1SDimitry Andric       }
324*0fca6ea1SDimitry Andric 
325*0fca6ea1SDimitry Andric       return "";
326*0fca6ea1SDimitry Andric     });
327*0fca6ea1SDimitry Andric   }
328*0fca6ea1SDimitry Andric 
3295ffd83dbSDimitry Andric   /// If true, evaluate special testing stream functions.
3305ffd83dbSDimitry Andric   bool TestMode = false;
3315ffd83dbSDimitry Andric 
332*0fca6ea1SDimitry Andric   /// If true, generate failure branches for cases that are often not checked.
333*0fca6ea1SDimitry Andric   bool PedanticMode = false;
334*0fca6ea1SDimitry Andric 
335*0fca6ea1SDimitry Andric   const CallDescription FCloseDesc = {CDM::CLibrary, {"fclose"}, 1};
336fe6060f1SDimitry Andric 
3375ffd83dbSDimitry Andric private:
3385ffd83dbSDimitry Andric   CallDescriptionMap<FnDescription> FnDescriptions = {
339*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fopen"}, 2},
340*0fca6ea1SDimitry Andric        {nullptr, &StreamChecker::evalFopen, ArgNone}},
341*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fdopen"}, 2},
342*0fca6ea1SDimitry Andric        {nullptr, &StreamChecker::evalFopen, ArgNone}},
343*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"freopen"}, 3},
3445ffd83dbSDimitry Andric        {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
345*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"tmpfile"}, 0},
346*0fca6ea1SDimitry Andric        {nullptr, &StreamChecker::evalFopen, ArgNone}},
347*0fca6ea1SDimitry Andric       {FCloseDesc, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
348*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fread"}, 4},
349*0fca6ea1SDimitry Andric        {&StreamChecker::preRead,
3505ffd83dbSDimitry Andric         std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
351*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fwrite"}, 4},
352*0fca6ea1SDimitry Andric        {&StreamChecker::preWrite,
3535ffd83dbSDimitry Andric         std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
354*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fgetc"}, 1},
355*0fca6ea1SDimitry Andric        {&StreamChecker::preRead,
3565f757f3fSDimitry Andric         std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
357*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fgets"}, 3},
358*0fca6ea1SDimitry Andric        {&StreamChecker::preRead,
3595f757f3fSDimitry Andric         std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}},
360*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"getc"}, 1},
361*0fca6ea1SDimitry Andric        {&StreamChecker::preRead,
362*0fca6ea1SDimitry Andric         std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
363*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fputc"}, 2},
364*0fca6ea1SDimitry Andric        {&StreamChecker::preWrite,
3655f757f3fSDimitry Andric         std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
366*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fputs"}, 2},
367*0fca6ea1SDimitry Andric        {&StreamChecker::preWrite,
3685f757f3fSDimitry Andric         std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}},
369*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"putc"}, 2},
370*0fca6ea1SDimitry Andric        {&StreamChecker::preWrite,
371*0fca6ea1SDimitry Andric         std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
372*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fprintf"}},
373*0fca6ea1SDimitry Andric        {&StreamChecker::preWrite,
3747a6dacacSDimitry Andric         std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
375*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"vfprintf"}, 3},
376*0fca6ea1SDimitry Andric        {&StreamChecker::preWrite,
377*0fca6ea1SDimitry Andric         std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
378*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fscanf"}},
379*0fca6ea1SDimitry Andric        {&StreamChecker::preRead,
3807a6dacacSDimitry Andric         std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
381*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"vfscanf"}, 3},
382*0fca6ea1SDimitry Andric        {&StreamChecker::preRead,
383*0fca6ea1SDimitry Andric         std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
384*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"ungetc"}, 2},
385*0fca6ea1SDimitry Andric        {&StreamChecker::preWrite,
386297eecfbSDimitry Andric         std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}},
387*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"getdelim"}, 4},
388*0fca6ea1SDimitry Andric        {&StreamChecker::preRead,
3897a6dacacSDimitry Andric         std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 3}},
390*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"getline"}, 3},
391*0fca6ea1SDimitry Andric        {&StreamChecker::preRead,
3927a6dacacSDimitry Andric         std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 2}},
393*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fseek"}, 3},
394bdd1243dSDimitry Andric        {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
395*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fseeko"}, 3},
396297eecfbSDimitry Andric        {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
397*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"ftell"}, 1},
398*0fca6ea1SDimitry Andric        {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
399*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"ftello"}, 1},
400*0fca6ea1SDimitry Andric        {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
401*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fflush"}, 1},
402cb14a3feSDimitry Andric        {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
403*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"rewind"}, 1},
404bdd1243dSDimitry Andric        {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
405*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fgetpos"}, 2},
406*0fca6ea1SDimitry Andric        {&StreamChecker::preWrite, &StreamChecker::evalFgetpos, 0}},
407*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fsetpos"}, 2},
408bdd1243dSDimitry Andric        {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
409*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"clearerr"}, 1},
4105ffd83dbSDimitry Andric        {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
411*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"feof"}, 1},
4125ffd83dbSDimitry Andric        {&StreamChecker::preDefault,
4135ffd83dbSDimitry Andric         std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
4145ffd83dbSDimitry Andric         0}},
415*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"ferror"}, 1},
4165ffd83dbSDimitry Andric        {&StreamChecker::preDefault,
4175ffd83dbSDimitry Andric         std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
4185ffd83dbSDimitry Andric         0}},
419*0fca6ea1SDimitry Andric       {{CDM::CLibrary, {"fileno"}, 1},
420*0fca6ea1SDimitry Andric        {&StreamChecker::preDefault, &StreamChecker::evalFileno, 0}},
4215ffd83dbSDimitry Andric   };
4225ffd83dbSDimitry Andric 
4235ffd83dbSDimitry Andric   CallDescriptionMap<FnDescription> FnTestDescriptions = {
424*0fca6ea1SDimitry Andric       {{CDM::SimpleFunc, {"StreamTesterChecker_make_feof_stream"}, 1},
4255ffd83dbSDimitry Andric        {nullptr,
426*0fca6ea1SDimitry Andric         std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof,
427*0fca6ea1SDimitry Andric                   false),
4285ffd83dbSDimitry Andric         0}},
429*0fca6ea1SDimitry Andric       {{CDM::SimpleFunc, {"StreamTesterChecker_make_ferror_stream"}, 1},
4305ffd83dbSDimitry Andric        {nullptr,
4315ffd83dbSDimitry Andric         std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
432*0fca6ea1SDimitry Andric                   ErrorFError, false),
433*0fca6ea1SDimitry Andric         0}},
434*0fca6ea1SDimitry Andric       {{CDM::SimpleFunc,
435*0fca6ea1SDimitry Andric         {"StreamTesterChecker_make_ferror_indeterminate_stream"},
436*0fca6ea1SDimitry Andric         1},
437*0fca6ea1SDimitry Andric        {nullptr,
438*0fca6ea1SDimitry Andric         std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
439*0fca6ea1SDimitry Andric                   ErrorFError, true),
4405ffd83dbSDimitry Andric         0}},
4415ffd83dbSDimitry Andric   };
4425ffd83dbSDimitry Andric 
44306c3fb27SDimitry Andric   /// Expanded value of EOF, empty before initialization.
444bdd1243dSDimitry Andric   mutable std::optional<int> EofVal;
44506c3fb27SDimitry Andric   /// Expanded value of SEEK_SET, 0 if not found.
44606c3fb27SDimitry Andric   mutable int SeekSetVal = 0;
44706c3fb27SDimitry Andric   /// Expanded value of SEEK_CUR, 1 if not found.
44806c3fb27SDimitry Andric   mutable int SeekCurVal = 1;
44906c3fb27SDimitry Andric   /// Expanded value of SEEK_END, 2 if not found.
45006c3fb27SDimitry Andric   mutable int SeekEndVal = 2;
451*0fca6ea1SDimitry Andric   /// The built-in va_list type is platform-specific
452*0fca6ea1SDimitry Andric   mutable QualType VaListType;
453bdd1243dSDimitry Andric 
4545ffd83dbSDimitry Andric   void evalFopen(const FnDescription *Desc, const CallEvent &Call,
4555ffd83dbSDimitry Andric                  CheckerContext &C) const;
4565ffd83dbSDimitry Andric 
4575ffd83dbSDimitry Andric   void preFreopen(const FnDescription *Desc, const CallEvent &Call,
4585ffd83dbSDimitry Andric                   CheckerContext &C) const;
4595ffd83dbSDimitry Andric   void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
4605ffd83dbSDimitry Andric                    CheckerContext &C) const;
4615ffd83dbSDimitry Andric 
4625ffd83dbSDimitry Andric   void evalFclose(const FnDescription *Desc, const CallEvent &Call,
4635ffd83dbSDimitry Andric                   CheckerContext &C) const;
4645ffd83dbSDimitry Andric 
465*0fca6ea1SDimitry Andric   void preRead(const FnDescription *Desc, const CallEvent &Call,
466*0fca6ea1SDimitry Andric                CheckerContext &C) const;
467*0fca6ea1SDimitry Andric 
468*0fca6ea1SDimitry Andric   void preWrite(const FnDescription *Desc, const CallEvent &Call,
469*0fca6ea1SDimitry Andric                 CheckerContext &C) const;
4705ffd83dbSDimitry Andric 
4715ffd83dbSDimitry Andric   void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
4725ffd83dbSDimitry Andric                        CheckerContext &C, bool IsFread) const;
4735ffd83dbSDimitry Andric 
4745f757f3fSDimitry Andric   void evalFgetx(const FnDescription *Desc, const CallEvent &Call,
4755f757f3fSDimitry Andric                  CheckerContext &C, bool SingleChar) const;
4765f757f3fSDimitry Andric 
4775f757f3fSDimitry Andric   void evalFputx(const FnDescription *Desc, const CallEvent &Call,
4785f757f3fSDimitry Andric                  CheckerContext &C, bool IsSingleChar) const;
4795f757f3fSDimitry Andric 
4807a6dacacSDimitry Andric   void evalFprintf(const FnDescription *Desc, const CallEvent &Call,
4817a6dacacSDimitry Andric                    CheckerContext &C) const;
4827a6dacacSDimitry Andric 
4837a6dacacSDimitry Andric   void evalFscanf(const FnDescription *Desc, const CallEvent &Call,
4847a6dacacSDimitry Andric                   CheckerContext &C) const;
4857a6dacacSDimitry Andric 
486297eecfbSDimitry Andric   void evalUngetc(const FnDescription *Desc, const CallEvent &Call,
487297eecfbSDimitry Andric                   CheckerContext &C) const;
488297eecfbSDimitry Andric 
4897a6dacacSDimitry Andric   void evalGetdelim(const FnDescription *Desc, const CallEvent &Call,
4907a6dacacSDimitry Andric                     CheckerContext &C) const;
4917a6dacacSDimitry Andric 
4925ffd83dbSDimitry Andric   void preFseek(const FnDescription *Desc, const CallEvent &Call,
4935ffd83dbSDimitry Andric                 CheckerContext &C) const;
4945ffd83dbSDimitry Andric   void evalFseek(const FnDescription *Desc, const CallEvent &Call,
4955ffd83dbSDimitry Andric                  CheckerContext &C) const;
4965ffd83dbSDimitry Andric 
497bdd1243dSDimitry Andric   void evalFgetpos(const FnDescription *Desc, const CallEvent &Call,
498bdd1243dSDimitry Andric                    CheckerContext &C) const;
499bdd1243dSDimitry Andric 
500bdd1243dSDimitry Andric   void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
501bdd1243dSDimitry Andric                    CheckerContext &C) const;
502bdd1243dSDimitry Andric 
503bdd1243dSDimitry Andric   void evalFtell(const FnDescription *Desc, const CallEvent &Call,
504bdd1243dSDimitry Andric                  CheckerContext &C) const;
505bdd1243dSDimitry Andric 
506bdd1243dSDimitry Andric   void evalRewind(const FnDescription *Desc, const CallEvent &Call,
507bdd1243dSDimitry Andric                   CheckerContext &C) const;
508bdd1243dSDimitry Andric 
5095ffd83dbSDimitry Andric   void preDefault(const FnDescription *Desc, const CallEvent &Call,
5105ffd83dbSDimitry Andric                   CheckerContext &C) const;
5115ffd83dbSDimitry Andric 
5125ffd83dbSDimitry Andric   void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
5135ffd83dbSDimitry Andric                     CheckerContext &C) const;
5145ffd83dbSDimitry Andric 
5155ffd83dbSDimitry Andric   void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
5165ffd83dbSDimitry Andric                       CheckerContext &C,
5175ffd83dbSDimitry Andric                       const StreamErrorState &ErrorKind) const;
5185ffd83dbSDimitry Andric 
5195ffd83dbSDimitry Andric   void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
520*0fca6ea1SDimitry Andric                          CheckerContext &C, const StreamErrorState &ErrorKind,
521*0fca6ea1SDimitry Andric                          bool Indeterminate) const;
5225ffd83dbSDimitry Andric 
523cb14a3feSDimitry Andric   void preFflush(const FnDescription *Desc, const CallEvent &Call,
524cb14a3feSDimitry Andric                  CheckerContext &C) const;
525cb14a3feSDimitry Andric 
526cb14a3feSDimitry Andric   void evalFflush(const FnDescription *Desc, const CallEvent &Call,
527cb14a3feSDimitry Andric                   CheckerContext &C) const;
528cb14a3feSDimitry Andric 
529*0fca6ea1SDimitry Andric   void evalFileno(const FnDescription *Desc, const CallEvent &Call,
530*0fca6ea1SDimitry Andric                   CheckerContext &C) const;
531*0fca6ea1SDimitry Andric 
5325ffd83dbSDimitry Andric   /// Check that the stream (in StreamVal) is not NULL.
53306c3fb27SDimitry Andric   /// If it can only be NULL a fatal error is emitted and nullptr returned.
5345ffd83dbSDimitry Andric   /// Otherwise the return value is a new state where the stream is constrained
5355ffd83dbSDimitry Andric   /// to be non-null.
536fe6060f1SDimitry Andric   ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
537fe6060f1SDimitry Andric                                       CheckerContext &C,
5385ffd83dbSDimitry Andric                                       ProgramStateRef State) const;
5395ffd83dbSDimitry Andric 
5405ffd83dbSDimitry Andric   /// Check that the stream is the opened state.
5415ffd83dbSDimitry Andric   /// If the stream is known to be not opened an error is generated
5425ffd83dbSDimitry Andric   /// and nullptr returned, otherwise the original state is returned.
5435ffd83dbSDimitry Andric   ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,
5445ffd83dbSDimitry Andric                                      ProgramStateRef State) const;
5455ffd83dbSDimitry Andric 
5465ffd83dbSDimitry Andric   /// Check that the stream has not an invalid ("indeterminate") file position,
5475ffd83dbSDimitry Andric   /// generate warning for it.
5485ffd83dbSDimitry Andric   /// (EOF is not an invalid position.)
5495ffd83dbSDimitry Andric   /// The returned state can be nullptr if a fatal error was generated.
5505ffd83dbSDimitry Andric   /// It can return non-null state if the stream has not an invalid position or
5515ffd83dbSDimitry Andric   /// there is execution path with non-invalid position.
5525ffd83dbSDimitry Andric   ProgramStateRef
5535ffd83dbSDimitry Andric   ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,
5545ffd83dbSDimitry Andric                                     ProgramStateRef State) const;
5555ffd83dbSDimitry Andric 
5565ffd83dbSDimitry Andric   /// Check the legality of the 'whence' argument of 'fseek'.
5575ffd83dbSDimitry Andric   /// Generate error and return nullptr if it is found to be illegal.
5585ffd83dbSDimitry Andric   /// Otherwise returns the state.
5595ffd83dbSDimitry Andric   /// (State is not changed here because the "whence" value is already known.)
5605ffd83dbSDimitry Andric   ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
5615ffd83dbSDimitry Andric                                            ProgramStateRef State) const;
5625ffd83dbSDimitry Andric 
5635ffd83dbSDimitry Andric   /// Generate warning about stream in EOF state.
5645ffd83dbSDimitry Andric   /// There will be always a state transition into the passed State,
5655ffd83dbSDimitry Andric   /// by the new non-fatal error node or (if failed) a normal transition,
5665ffd83dbSDimitry Andric   /// to ensure uniform handling.
567fe6060f1SDimitry Andric   void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
568fe6060f1SDimitry Andric                          ProgramStateRef State) const;
5695ffd83dbSDimitry Andric 
570e8d8bef9SDimitry Andric   /// Emit resource leak warnings for the given symbols.
571e8d8bef9SDimitry Andric   /// Createn a non-fatal error node for these, and returns it (if any warnings
572e8d8bef9SDimitry Andric   /// were generated). Return value is non-null.
573e8d8bef9SDimitry Andric   ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
574e8d8bef9SDimitry Andric                             CheckerContext &C, ExplodedNode *Pred) const;
575e8d8bef9SDimitry Andric 
5765ffd83dbSDimitry Andric   /// Find the description data of the function called by a call event.
5775ffd83dbSDimitry Andric   /// Returns nullptr if no function is recognized.
lookupFn(const CallEvent & Call) const5785ffd83dbSDimitry Andric   const FnDescription *lookupFn(const CallEvent &Call) const {
5795ffd83dbSDimitry Andric     // Recognize "global C functions" with only integral or pointer arguments
5805ffd83dbSDimitry Andric     // (and matching name) as stream functions.
581bdd1243dSDimitry Andric     for (auto *P : Call.parameters()) {
5825ffd83dbSDimitry Andric       QualType T = P->getType();
583*0fca6ea1SDimitry Andric       if (!T->isIntegralOrEnumerationType() && !T->isPointerType() &&
584*0fca6ea1SDimitry Andric           T.getCanonicalType() != VaListType)
5855ffd83dbSDimitry Andric         return nullptr;
5865ffd83dbSDimitry Andric     }
5875ffd83dbSDimitry Andric 
5885ffd83dbSDimitry Andric     return FnDescriptions.lookup(Call);
5895ffd83dbSDimitry Andric   }
5905ffd83dbSDimitry Andric 
5915ffd83dbSDimitry Andric   /// Generate a message for BugReporterVisitor if the stored symbol is
5925ffd83dbSDimitry Andric   /// marked as interesting by the actual bug report.
constructLeakNoteTag(CheckerContext & C,SymbolRef StreamSym,const std::string & Message) const593*0fca6ea1SDimitry Andric   const NoteTag *constructLeakNoteTag(CheckerContext &C, SymbolRef StreamSym,
5945ffd83dbSDimitry Andric                                       const std::string &Message) const {
5955f757f3fSDimitry Andric     return C.getNoteTag([this, StreamSym,
5965f757f3fSDimitry Andric                          Message](PathSensitiveBugReport &BR) -> std::string {
5975f757f3fSDimitry Andric       if (BR.isInteresting(StreamSym) && &BR.getBugType() == &BT_ResourceLeak)
5985f757f3fSDimitry Andric         return Message;
5995f757f3fSDimitry Andric       return "";
6005f757f3fSDimitry Andric     });
601fe6060f1SDimitry Andric   }
602fe6060f1SDimitry Andric 
initMacroValues(CheckerContext & C) const60306c3fb27SDimitry Andric   void initMacroValues(CheckerContext &C) const {
604bdd1243dSDimitry Andric     if (EofVal)
605bdd1243dSDimitry Andric       return;
606bdd1243dSDimitry Andric 
607bdd1243dSDimitry Andric     if (const std::optional<int> OptInt =
608bdd1243dSDimitry Andric             tryExpandAsInteger("EOF", C.getPreprocessor()))
609bdd1243dSDimitry Andric       EofVal = *OptInt;
610bdd1243dSDimitry Andric     else
611bdd1243dSDimitry Andric       EofVal = -1;
61206c3fb27SDimitry Andric     if (const std::optional<int> OptInt =
61306c3fb27SDimitry Andric             tryExpandAsInteger("SEEK_SET", C.getPreprocessor()))
61406c3fb27SDimitry Andric       SeekSetVal = *OptInt;
61506c3fb27SDimitry Andric     if (const std::optional<int> OptInt =
61606c3fb27SDimitry Andric             tryExpandAsInteger("SEEK_END", C.getPreprocessor()))
61706c3fb27SDimitry Andric       SeekEndVal = *OptInt;
61806c3fb27SDimitry Andric     if (const std::optional<int> OptInt =
61906c3fb27SDimitry Andric             tryExpandAsInteger("SEEK_CUR", C.getPreprocessor()))
62006c3fb27SDimitry Andric       SeekCurVal = *OptInt;
621bdd1243dSDimitry Andric   }
622bdd1243dSDimitry Andric 
initVaListType(CheckerContext & C) const623*0fca6ea1SDimitry Andric   void initVaListType(CheckerContext &C) const {
624*0fca6ea1SDimitry Andric     VaListType = C.getASTContext().getBuiltinVaListType().getCanonicalType();
625*0fca6ea1SDimitry Andric   }
626*0fca6ea1SDimitry Andric 
6275ffd83dbSDimitry Andric   /// Searches for the ExplodedNode where the file descriptor was acquired for
6285ffd83dbSDimitry Andric   /// StreamSym.
6295ffd83dbSDimitry Andric   static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
6305ffd83dbSDimitry Andric                                                 SymbolRef StreamSym,
6315ffd83dbSDimitry Andric                                                 CheckerContext &C);
6320b57cec5SDimitry Andric };
6330b57cec5SDimitry Andric 
634*0fca6ea1SDimitry Andric struct StreamOperationEvaluator {
635*0fca6ea1SDimitry Andric   SValBuilder &SVB;
636*0fca6ea1SDimitry Andric   const ASTContext &ACtx;
637*0fca6ea1SDimitry Andric 
638*0fca6ea1SDimitry Andric   SymbolRef StreamSym = nullptr;
639*0fca6ea1SDimitry Andric   const StreamState *SS = nullptr;
640*0fca6ea1SDimitry Andric   const CallExpr *CE = nullptr;
641*0fca6ea1SDimitry Andric   StreamErrorState NewES;
642*0fca6ea1SDimitry Andric 
StreamOperationEvaluator__anon299e83ef0211::StreamOperationEvaluator643*0fca6ea1SDimitry Andric   StreamOperationEvaluator(CheckerContext &C)
644*0fca6ea1SDimitry Andric       : SVB(C.getSValBuilder()), ACtx(C.getASTContext()) {
645*0fca6ea1SDimitry Andric     ;
646*0fca6ea1SDimitry Andric   }
647*0fca6ea1SDimitry Andric 
Init__anon299e83ef0211::StreamOperationEvaluator648*0fca6ea1SDimitry Andric   bool Init(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C,
649*0fca6ea1SDimitry Andric             ProgramStateRef State) {
650*0fca6ea1SDimitry Andric     StreamSym = getStreamArg(Desc, Call).getAsSymbol();
651*0fca6ea1SDimitry Andric     if (!StreamSym)
652*0fca6ea1SDimitry Andric       return false;
653*0fca6ea1SDimitry Andric     SS = State->get<StreamMap>(StreamSym);
654*0fca6ea1SDimitry Andric     if (!SS)
655*0fca6ea1SDimitry Andric       return false;
656*0fca6ea1SDimitry Andric     NewES = SS->ErrorState;
657*0fca6ea1SDimitry Andric     CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
658*0fca6ea1SDimitry Andric     if (!CE)
659*0fca6ea1SDimitry Andric       return false;
660*0fca6ea1SDimitry Andric 
661*0fca6ea1SDimitry Andric     assertStreamStateOpened(SS);
662*0fca6ea1SDimitry Andric 
663*0fca6ea1SDimitry Andric     return true;
664*0fca6ea1SDimitry Andric   }
665*0fca6ea1SDimitry Andric 
isStreamEof__anon299e83ef0211::StreamOperationEvaluator666*0fca6ea1SDimitry Andric   bool isStreamEof() const { return SS->ErrorState == ErrorFEof; }
667*0fca6ea1SDimitry Andric 
getZeroVal__anon299e83ef0211::StreamOperationEvaluator668*0fca6ea1SDimitry Andric   NonLoc getZeroVal(const CallEvent &Call) {
669*0fca6ea1SDimitry Andric     return *SVB.makeZeroVal(Call.getResultType()).getAs<NonLoc>();
670*0fca6ea1SDimitry Andric   }
671*0fca6ea1SDimitry Andric 
setStreamState__anon299e83ef0211::StreamOperationEvaluator672*0fca6ea1SDimitry Andric   ProgramStateRef setStreamState(ProgramStateRef State,
673*0fca6ea1SDimitry Andric                                  const StreamState &NewSS) {
674*0fca6ea1SDimitry Andric     NewES = NewSS.ErrorState;
675*0fca6ea1SDimitry Andric     return State->set<StreamMap>(StreamSym, NewSS);
676*0fca6ea1SDimitry Andric   }
677*0fca6ea1SDimitry Andric 
makeAndBindRetVal__anon299e83ef0211::StreamOperationEvaluator678*0fca6ea1SDimitry Andric   ProgramStateRef makeAndBindRetVal(ProgramStateRef State, CheckerContext &C) {
679*0fca6ea1SDimitry Andric     NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
680*0fca6ea1SDimitry Andric     return State->BindExpr(CE, C.getLocationContext(), RetVal);
681*0fca6ea1SDimitry Andric   }
682*0fca6ea1SDimitry Andric 
bindReturnValue__anon299e83ef0211::StreamOperationEvaluator683*0fca6ea1SDimitry Andric   ProgramStateRef bindReturnValue(ProgramStateRef State, CheckerContext &C,
684*0fca6ea1SDimitry Andric                                   uint64_t Val) {
685*0fca6ea1SDimitry Andric     return State->BindExpr(CE, C.getLocationContext(),
686*0fca6ea1SDimitry Andric                            SVB.makeIntVal(Val, CE->getCallReturnType(ACtx)));
687*0fca6ea1SDimitry Andric   }
688*0fca6ea1SDimitry Andric 
bindReturnValue__anon299e83ef0211::StreamOperationEvaluator689*0fca6ea1SDimitry Andric   ProgramStateRef bindReturnValue(ProgramStateRef State, CheckerContext &C,
690*0fca6ea1SDimitry Andric                                   SVal Val) {
691*0fca6ea1SDimitry Andric     return State->BindExpr(CE, C.getLocationContext(), Val);
692*0fca6ea1SDimitry Andric   }
693*0fca6ea1SDimitry Andric 
bindNullReturnValue__anon299e83ef0211::StreamOperationEvaluator694*0fca6ea1SDimitry Andric   ProgramStateRef bindNullReturnValue(ProgramStateRef State,
695*0fca6ea1SDimitry Andric                                       CheckerContext &C) {
696*0fca6ea1SDimitry Andric     return State->BindExpr(CE, C.getLocationContext(),
697*0fca6ea1SDimitry Andric                            C.getSValBuilder().makeNullWithType(CE->getType()));
698*0fca6ea1SDimitry Andric   }
699*0fca6ea1SDimitry Andric 
assumeBinOpNN__anon299e83ef0211::StreamOperationEvaluator700*0fca6ea1SDimitry Andric   ProgramStateRef assumeBinOpNN(ProgramStateRef State,
701*0fca6ea1SDimitry Andric                                 BinaryOperator::Opcode Op, NonLoc LHS,
702*0fca6ea1SDimitry Andric                                 NonLoc RHS) {
703*0fca6ea1SDimitry Andric     auto Cond = SVB.evalBinOpNN(State, Op, LHS, RHS, SVB.getConditionType())
704*0fca6ea1SDimitry Andric                     .getAs<DefinedOrUnknownSVal>();
705*0fca6ea1SDimitry Andric     if (!Cond)
706*0fca6ea1SDimitry Andric       return nullptr;
707*0fca6ea1SDimitry Andric     return State->assume(*Cond, true);
708*0fca6ea1SDimitry Andric   }
709*0fca6ea1SDimitry Andric 
710*0fca6ea1SDimitry Andric   ConstraintManager::ProgramStatePair
makeRetValAndAssumeDual__anon299e83ef0211::StreamOperationEvaluator711*0fca6ea1SDimitry Andric   makeRetValAndAssumeDual(ProgramStateRef State, CheckerContext &C) {
712*0fca6ea1SDimitry Andric     DefinedSVal RetVal = makeRetVal(C, CE);
713*0fca6ea1SDimitry Andric     State = State->BindExpr(CE, C.getLocationContext(), RetVal);
714*0fca6ea1SDimitry Andric     return C.getConstraintManager().assumeDual(State, RetVal);
715*0fca6ea1SDimitry Andric   }
716*0fca6ea1SDimitry Andric 
getFailureNoteTag__anon299e83ef0211::StreamOperationEvaluator717*0fca6ea1SDimitry Andric   const NoteTag *getFailureNoteTag(const StreamChecker *Ch, CheckerContext &C) {
718*0fca6ea1SDimitry Andric     bool SetFeof = NewES.FEof && !SS->ErrorState.FEof;
719*0fca6ea1SDimitry Andric     bool SetFerror = NewES.FError && !SS->ErrorState.FError;
720*0fca6ea1SDimitry Andric     if (SetFeof && !SetFerror)
721*0fca6ea1SDimitry Andric       return Ch->constructSetEofNoteTag(C, StreamSym);
722*0fca6ea1SDimitry Andric     if (!SetFeof && SetFerror)
723*0fca6ea1SDimitry Andric       return Ch->constructSetErrorNoteTag(C, StreamSym);
724*0fca6ea1SDimitry Andric     if (SetFeof && SetFerror)
725*0fca6ea1SDimitry Andric       return Ch->constructSetEofOrErrorNoteTag(C, StreamSym);
726*0fca6ea1SDimitry Andric     return nullptr;
727*0fca6ea1SDimitry Andric   }
728*0fca6ea1SDimitry Andric };
729*0fca6ea1SDimitry Andric 
7300b57cec5SDimitry Andric } // end anonymous namespace
7310b57cec5SDimitry Andric 
732*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===//
733*0fca6ea1SDimitry Andric // Definition of NoStreamStateChangeVisitor.
734*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===//
7350b57cec5SDimitry Andric 
736*0fca6ea1SDimitry Andric namespace {
737*0fca6ea1SDimitry Andric class NoStreamStateChangeVisitor final : public NoOwnershipChangeVisitor {
738*0fca6ea1SDimitry Andric protected:
739*0fca6ea1SDimitry Andric   /// Syntactically checks whether the callee is a closing function. Since
740*0fca6ea1SDimitry Andric   /// we have no path-sensitive information on this call (we would need a
741*0fca6ea1SDimitry Andric   /// CallEvent instead of a CallExpr for that), its possible that a
742*0fca6ea1SDimitry Andric   /// closing function was called indirectly through a function pointer,
743*0fca6ea1SDimitry Andric   /// but we are not able to tell, so this is a best effort analysis.
isClosingCallAsWritten(const CallExpr & Call) const744*0fca6ea1SDimitry Andric   bool isClosingCallAsWritten(const CallExpr &Call) const {
745*0fca6ea1SDimitry Andric     const auto *StreamChk = static_cast<const StreamChecker *>(&Checker);
746*0fca6ea1SDimitry Andric     return StreamChk->FCloseDesc.matchesAsWritten(Call);
7470b57cec5SDimitry Andric   }
7480b57cec5SDimitry Andric 
doesFnIntendToHandleOwnership(const Decl * Callee,ASTContext & ACtx)749*0fca6ea1SDimitry Andric   bool doesFnIntendToHandleOwnership(const Decl *Callee,
750*0fca6ea1SDimitry Andric                                      ASTContext &ACtx) final {
751*0fca6ea1SDimitry Andric     using namespace clang::ast_matchers;
752*0fca6ea1SDimitry Andric     const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee);
753*0fca6ea1SDimitry Andric 
754*0fca6ea1SDimitry Andric     auto Matches =
755*0fca6ea1SDimitry Andric         match(findAll(callExpr().bind("call")), *FD->getBody(), ACtx);
756*0fca6ea1SDimitry Andric     for (BoundNodes Match : Matches) {
757*0fca6ea1SDimitry Andric       if (const auto *Call = Match.getNodeAs<CallExpr>("call"))
758*0fca6ea1SDimitry Andric         if (isClosingCallAsWritten(*Call))
759*0fca6ea1SDimitry Andric           return true;
760*0fca6ea1SDimitry Andric     }
761*0fca6ea1SDimitry Andric     // TODO: Ownership might change with an attempt to store stream object, not
762*0fca6ea1SDimitry Andric     // only through closing it. Check for attempted stores as well.
763*0fca6ea1SDimitry Andric     return false;
764*0fca6ea1SDimitry Andric   }
765*0fca6ea1SDimitry Andric 
hasResourceStateChanged(ProgramStateRef CallEnterState,ProgramStateRef CallExitEndState)766*0fca6ea1SDimitry Andric   bool hasResourceStateChanged(ProgramStateRef CallEnterState,
767*0fca6ea1SDimitry Andric                                ProgramStateRef CallExitEndState) final {
768*0fca6ea1SDimitry Andric     return CallEnterState->get<StreamMap>(Sym) !=
769*0fca6ea1SDimitry Andric            CallExitEndState->get<StreamMap>(Sym);
770*0fca6ea1SDimitry Andric   }
771*0fca6ea1SDimitry Andric 
emitNote(const ExplodedNode * N)772*0fca6ea1SDimitry Andric   PathDiagnosticPieceRef emitNote(const ExplodedNode *N) override {
773*0fca6ea1SDimitry Andric     PathDiagnosticLocation L = PathDiagnosticLocation::create(
774*0fca6ea1SDimitry Andric         N->getLocation(),
775*0fca6ea1SDimitry Andric         N->getState()->getStateManager().getContext().getSourceManager());
776*0fca6ea1SDimitry Andric     return std::make_shared<PathDiagnosticEventPiece>(
777*0fca6ea1SDimitry Andric         L, "Returning without closing stream object or storing it for later "
778*0fca6ea1SDimitry Andric            "release");
779*0fca6ea1SDimitry Andric   }
780*0fca6ea1SDimitry Andric 
781*0fca6ea1SDimitry Andric public:
NoStreamStateChangeVisitor(SymbolRef Sym,const StreamChecker * Checker)782*0fca6ea1SDimitry Andric   NoStreamStateChangeVisitor(SymbolRef Sym, const StreamChecker *Checker)
783*0fca6ea1SDimitry Andric       : NoOwnershipChangeVisitor(Sym, Checker) {}
784*0fca6ea1SDimitry Andric };
785*0fca6ea1SDimitry Andric 
786*0fca6ea1SDimitry Andric } // end anonymous namespace
787*0fca6ea1SDimitry Andric 
getAcquisitionSite(const ExplodedNode * N,SymbolRef StreamSym,CheckerContext & C)7885ffd83dbSDimitry Andric const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
7895ffd83dbSDimitry Andric                                                       SymbolRef StreamSym,
7905ffd83dbSDimitry Andric                                                       CheckerContext &C) {
7915ffd83dbSDimitry Andric   ProgramStateRef State = N->getState();
7925ffd83dbSDimitry Andric   // When bug type is resource leak, exploded node N may not have state info
7935ffd83dbSDimitry Andric   // for leaked file descriptor, but predecessor should have it.
7945ffd83dbSDimitry Andric   if (!State->get<StreamMap>(StreamSym))
7955ffd83dbSDimitry Andric     N = N->getFirstPred();
7965ffd83dbSDimitry Andric 
7975ffd83dbSDimitry Andric   const ExplodedNode *Pred = N;
7985ffd83dbSDimitry Andric   while (N) {
7995ffd83dbSDimitry Andric     State = N->getState();
8005ffd83dbSDimitry Andric     if (!State->get<StreamMap>(StreamSym))
8015ffd83dbSDimitry Andric       return Pred;
8025ffd83dbSDimitry Andric     Pred = N;
8035ffd83dbSDimitry Andric     N = N->getFirstPred();
8045ffd83dbSDimitry Andric   }
8055ffd83dbSDimitry Andric 
8065ffd83dbSDimitry Andric   return nullptr;
8075ffd83dbSDimitry Andric }
8085ffd83dbSDimitry Andric 
getKnownValue(ProgramStateRef State,SVal V)809*0fca6ea1SDimitry Andric static std::optional<int64_t> getKnownValue(ProgramStateRef State, SVal V) {
810*0fca6ea1SDimitry Andric   SValBuilder &SVB = State->getStateManager().getSValBuilder();
811*0fca6ea1SDimitry Andric   if (const llvm::APSInt *Int = SVB.getKnownValue(State, V))
812*0fca6ea1SDimitry Andric     return Int->tryExtValue();
813*0fca6ea1SDimitry Andric   return std::nullopt;
814*0fca6ea1SDimitry Andric }
815*0fca6ea1SDimitry Andric 
816*0fca6ea1SDimitry Andric /// Invalidate only the requested elements instead of the whole buffer.
817*0fca6ea1SDimitry Andric /// This is basically a refinement of the more generic 'escapeArgs' or
818*0fca6ea1SDimitry Andric /// the plain old 'invalidateRegions'.
819*0fca6ea1SDimitry Andric static ProgramStateRef
escapeByStartIndexAndCount(ProgramStateRef State,const CallEvent & Call,unsigned BlockCount,const SubRegion * Buffer,QualType ElemType,int64_t StartIndex,int64_t ElementCount)820*0fca6ea1SDimitry Andric escapeByStartIndexAndCount(ProgramStateRef State, const CallEvent &Call,
821*0fca6ea1SDimitry Andric                            unsigned BlockCount, const SubRegion *Buffer,
822*0fca6ea1SDimitry Andric                            QualType ElemType, int64_t StartIndex,
823*0fca6ea1SDimitry Andric                            int64_t ElementCount) {
824*0fca6ea1SDimitry Andric   constexpr auto DoNotInvalidateSuperRegion =
825*0fca6ea1SDimitry Andric       RegionAndSymbolInvalidationTraits::InvalidationKinds::
826*0fca6ea1SDimitry Andric           TK_DoNotInvalidateSuperRegion;
827*0fca6ea1SDimitry Andric 
828*0fca6ea1SDimitry Andric   const LocationContext *LCtx = Call.getLocationContext();
829*0fca6ea1SDimitry Andric   const ASTContext &Ctx = State->getStateManager().getContext();
830*0fca6ea1SDimitry Andric   SValBuilder &SVB = State->getStateManager().getSValBuilder();
831*0fca6ea1SDimitry Andric   auto &RegionManager = Buffer->getMemRegionManager();
832*0fca6ea1SDimitry Andric 
833*0fca6ea1SDimitry Andric   SmallVector<SVal> EscapingVals;
834*0fca6ea1SDimitry Andric   EscapingVals.reserve(ElementCount);
835*0fca6ea1SDimitry Andric 
836*0fca6ea1SDimitry Andric   RegionAndSymbolInvalidationTraits ITraits;
837*0fca6ea1SDimitry Andric   for (auto Idx : llvm::seq(StartIndex, StartIndex + ElementCount)) {
838*0fca6ea1SDimitry Andric     NonLoc Index = SVB.makeArrayIndex(Idx);
839*0fca6ea1SDimitry Andric     const auto *Element =
840*0fca6ea1SDimitry Andric         RegionManager.getElementRegion(ElemType, Index, Buffer, Ctx);
841*0fca6ea1SDimitry Andric     EscapingVals.push_back(loc::MemRegionVal(Element));
842*0fca6ea1SDimitry Andric     ITraits.setTrait(Element, DoNotInvalidateSuperRegion);
843*0fca6ea1SDimitry Andric   }
844*0fca6ea1SDimitry Andric   return State->invalidateRegions(
845*0fca6ea1SDimitry Andric       EscapingVals, Call.getOriginExpr(), BlockCount, LCtx,
846*0fca6ea1SDimitry Andric       /*CausesPointerEscape=*/false,
847*0fca6ea1SDimitry Andric       /*InvalidatedSymbols=*/nullptr, &Call, &ITraits);
848*0fca6ea1SDimitry Andric }
849*0fca6ea1SDimitry Andric 
escapeArgs(ProgramStateRef State,CheckerContext & C,const CallEvent & Call,ArrayRef<unsigned int> EscapingArgs)850*0fca6ea1SDimitry Andric static ProgramStateRef escapeArgs(ProgramStateRef State, CheckerContext &C,
851*0fca6ea1SDimitry Andric                                   const CallEvent &Call,
852*0fca6ea1SDimitry Andric                                   ArrayRef<unsigned int> EscapingArgs) {
853*0fca6ea1SDimitry Andric   auto GetArgSVal = [&Call](int Idx) { return Call.getArgSVal(Idx); };
854*0fca6ea1SDimitry Andric   auto EscapingVals = to_vector(map_range(EscapingArgs, GetArgSVal));
855*0fca6ea1SDimitry Andric   State = State->invalidateRegions(EscapingVals, Call.getOriginExpr(),
856*0fca6ea1SDimitry Andric                                    C.blockCount(), C.getLocationContext(),
857*0fca6ea1SDimitry Andric                                    /*CausesPointerEscape=*/false,
858*0fca6ea1SDimitry Andric                                    /*InvalidatedSymbols=*/nullptr);
859*0fca6ea1SDimitry Andric   return State;
860*0fca6ea1SDimitry Andric }
861*0fca6ea1SDimitry Andric 
862fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
863fe6060f1SDimitry Andric // Methods of StreamChecker.
864fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
865fe6060f1SDimitry Andric 
checkPreCall(const CallEvent & Call,CheckerContext & C) const8665ffd83dbSDimitry Andric void StreamChecker::checkPreCall(const CallEvent &Call,
8675ffd83dbSDimitry Andric                                  CheckerContext &C) const {
86806c3fb27SDimitry Andric   initMacroValues(C);
869*0fca6ea1SDimitry Andric   initVaListType(C);
870bdd1243dSDimitry Andric 
8715ffd83dbSDimitry Andric   const FnDescription *Desc = lookupFn(Call);
8725ffd83dbSDimitry Andric   if (!Desc || !Desc->PreFn)
8735ffd83dbSDimitry Andric     return;
8745ffd83dbSDimitry Andric 
8755ffd83dbSDimitry Andric   Desc->PreFn(this, Desc, Call, C);
8765ffd83dbSDimitry Andric }
8775ffd83dbSDimitry Andric 
evalCall(const CallEvent & Call,CheckerContext & C) const8785ffd83dbSDimitry Andric bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
8795ffd83dbSDimitry Andric   const FnDescription *Desc = lookupFn(Call);
8805ffd83dbSDimitry Andric   if (!Desc && TestMode)
8815ffd83dbSDimitry Andric     Desc = FnTestDescriptions.lookup(Call);
8825ffd83dbSDimitry Andric   if (!Desc || !Desc->EvalFn)
883480093f4SDimitry Andric     return false;
884480093f4SDimitry Andric 
8855ffd83dbSDimitry Andric   Desc->EvalFn(this, Desc, Call, C);
886480093f4SDimitry Andric 
887480093f4SDimitry Andric   return C.isDifferent();
8880b57cec5SDimitry Andric }
8890b57cec5SDimitry Andric 
evalFopen(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const8905ffd83dbSDimitry Andric void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
8915ffd83dbSDimitry Andric                               CheckerContext &C) const {
8925ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
8935ffd83dbSDimitry Andric   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
894480093f4SDimitry Andric   if (!CE)
895480093f4SDimitry Andric     return;
896480093f4SDimitry Andric 
8975ffd83dbSDimitry Andric   DefinedSVal RetVal = makeRetVal(C, CE);
8985ffd83dbSDimitry Andric   SymbolRef RetSym = RetVal.getAsSymbol();
8995ffd83dbSDimitry Andric   assert(RetSym && "RetVal must be a symbol here.");
9000b57cec5SDimitry Andric 
9015ffd83dbSDimitry Andric   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
9025ffd83dbSDimitry Andric 
9030b57cec5SDimitry Andric   // Bifurcate the state into two: one with a valid FILE* pointer, the other
9040b57cec5SDimitry Andric   // with a NULL.
9055ffd83dbSDimitry Andric   ProgramStateRef StateNotNull, StateNull;
9065ffd83dbSDimitry Andric   std::tie(StateNotNull, StateNull) =
9075ffd83dbSDimitry Andric       C.getConstraintManager().assumeDual(State, RetVal);
9080b57cec5SDimitry Andric 
9095ffd83dbSDimitry Andric   StateNotNull =
9105ffd83dbSDimitry Andric       StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
9115ffd83dbSDimitry Andric   StateNull =
9125ffd83dbSDimitry Andric       StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
9130b57cec5SDimitry Andric 
9145ffd83dbSDimitry Andric   C.addTransition(StateNotNull,
915*0fca6ea1SDimitry Andric                   constructLeakNoteTag(C, RetSym, "Stream opened here"));
9165ffd83dbSDimitry Andric   C.addTransition(StateNull);
9170b57cec5SDimitry Andric }
9180b57cec5SDimitry Andric 
preFreopen(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const9195ffd83dbSDimitry Andric void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
9205ffd83dbSDimitry Andric                                CheckerContext &C) const {
9215ffd83dbSDimitry Andric   // Do not allow NULL as passed stream pointer but allow a closed stream.
9225ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
923fe6060f1SDimitry Andric   State = ensureStreamNonNull(getStreamArg(Desc, Call),
924fe6060f1SDimitry Andric                               Call.getArgExpr(Desc->StreamArgNo), C, State);
9255ffd83dbSDimitry Andric   if (!State)
9265ffd83dbSDimitry Andric     return;
9275ffd83dbSDimitry Andric 
9285ffd83dbSDimitry Andric   C.addTransition(State);
9295ffd83dbSDimitry Andric }
9305ffd83dbSDimitry Andric 
evalFreopen(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const9315ffd83dbSDimitry Andric void StreamChecker::evalFreopen(const FnDescription *Desc,
9325ffd83dbSDimitry Andric                                 const CallEvent &Call,
933480093f4SDimitry Andric                                 CheckerContext &C) const {
934480093f4SDimitry Andric   ProgramStateRef State = C.getState();
9350b57cec5SDimitry Andric 
936480093f4SDimitry Andric   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
937480093f4SDimitry Andric   if (!CE)
9380b57cec5SDimitry Andric     return;
939480093f4SDimitry Andric 
940bdd1243dSDimitry Andric   std::optional<DefinedSVal> StreamVal =
9415ffd83dbSDimitry Andric       getStreamArg(Desc, Call).getAs<DefinedSVal>();
942480093f4SDimitry Andric   if (!StreamVal)
943480093f4SDimitry Andric     return;
944480093f4SDimitry Andric 
945480093f4SDimitry Andric   SymbolRef StreamSym = StreamVal->getAsSymbol();
9465ffd83dbSDimitry Andric   // Do not care about concrete values for stream ("(FILE *)0x12345"?).
9475ffd83dbSDimitry Andric   // FIXME: Can be stdin, stdout, stderr such values?
948480093f4SDimitry Andric   if (!StreamSym)
949480093f4SDimitry Andric     return;
950480093f4SDimitry Andric 
9515ffd83dbSDimitry Andric   // Do not handle untracked stream. It is probably escaped.
9525ffd83dbSDimitry Andric   if (!State->get<StreamMap>(StreamSym))
9535ffd83dbSDimitry Andric     return;
9545ffd83dbSDimitry Andric 
955480093f4SDimitry Andric   // Generate state for non-failed case.
956480093f4SDimitry Andric   // Return value is the passed stream pointer.
957480093f4SDimitry Andric   // According to the documentations, the stream is closed first
958480093f4SDimitry Andric   // but any close error is ignored. The state changes to (or remains) opened.
959480093f4SDimitry Andric   ProgramStateRef StateRetNotNull =
960480093f4SDimitry Andric       State->BindExpr(CE, C.getLocationContext(), *StreamVal);
961480093f4SDimitry Andric   // Generate state for NULL return value.
962480093f4SDimitry Andric   // Stream switches to OpenFailed state.
96381ad6265SDimitry Andric   ProgramStateRef StateRetNull =
96481ad6265SDimitry Andric       State->BindExpr(CE, C.getLocationContext(),
96581ad6265SDimitry Andric                       C.getSValBuilder().makeNullWithType(CE->getType()));
966480093f4SDimitry Andric 
967480093f4SDimitry Andric   StateRetNotNull =
9685ffd83dbSDimitry Andric       StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
969480093f4SDimitry Andric   StateRetNull =
9705ffd83dbSDimitry Andric       StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
971480093f4SDimitry Andric 
9725ffd83dbSDimitry Andric   C.addTransition(StateRetNotNull,
973*0fca6ea1SDimitry Andric                   constructLeakNoteTag(C, StreamSym, "Stream reopened here"));
974480093f4SDimitry Andric   C.addTransition(StateRetNull);
9750b57cec5SDimitry Andric }
9760b57cec5SDimitry Andric 
evalFclose(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const9775ffd83dbSDimitry Andric void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
9785ffd83dbSDimitry Andric                                CheckerContext &C) const {
979480093f4SDimitry Andric   ProgramStateRef State = C.getState();
980*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
981*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
9825ffd83dbSDimitry Andric     return;
9835ffd83dbSDimitry Andric 
9845ffd83dbSDimitry Andric   // Close the File Descriptor.
9855ffd83dbSDimitry Andric   // Regardless if the close fails or not, stream becomes "closed"
9865ffd83dbSDimitry Andric   // and can not be used any more.
987*0fca6ea1SDimitry Andric   State = E.setStreamState(State, StreamState::getClosed(Desc));
9885ffd83dbSDimitry Andric 
989bdd1243dSDimitry Andric   // Return 0 on success, EOF on failure.
990*0fca6ea1SDimitry Andric   C.addTransition(E.bindReturnValue(State, C, 0));
991*0fca6ea1SDimitry Andric   C.addTransition(E.bindReturnValue(State, C, *EofVal));
9920b57cec5SDimitry Andric }
9930b57cec5SDimitry Andric 
preRead(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const994*0fca6ea1SDimitry Andric void StreamChecker::preRead(const FnDescription *Desc, const CallEvent &Call,
995*0fca6ea1SDimitry Andric                             CheckerContext &C) const {
996480093f4SDimitry Andric   ProgramStateRef State = C.getState();
9975ffd83dbSDimitry Andric   SVal StreamVal = getStreamArg(Desc, Call);
998fe6060f1SDimitry Andric   State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
999fe6060f1SDimitry Andric                               State);
10005ffd83dbSDimitry Andric   if (!State)
10015ffd83dbSDimitry Andric     return;
10025ffd83dbSDimitry Andric   State = ensureStreamOpened(StreamVal, C, State);
10035ffd83dbSDimitry Andric   if (!State)
10045ffd83dbSDimitry Andric     return;
10055ffd83dbSDimitry Andric   State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
10065ffd83dbSDimitry Andric   if (!State)
1007480093f4SDimitry Andric     return;
1008480093f4SDimitry Andric 
10095ffd83dbSDimitry Andric   SymbolRef Sym = StreamVal.getAsSymbol();
10105ffd83dbSDimitry Andric   if (Sym && State->get<StreamMap>(Sym)) {
10115ffd83dbSDimitry Andric     const StreamState *SS = State->get<StreamMap>(Sym);
10125ffd83dbSDimitry Andric     if (SS->ErrorState & ErrorFEof)
1013fe6060f1SDimitry Andric       reportFEofWarning(Sym, C, State);
10145ffd83dbSDimitry Andric   } else {
1015480093f4SDimitry Andric     C.addTransition(State);
10165ffd83dbSDimitry Andric   }
1017480093f4SDimitry Andric }
1018480093f4SDimitry Andric 
preWrite(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1019*0fca6ea1SDimitry Andric void StreamChecker::preWrite(const FnDescription *Desc, const CallEvent &Call,
1020*0fca6ea1SDimitry Andric                              CheckerContext &C) const {
1021*0fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
1022*0fca6ea1SDimitry Andric   SVal StreamVal = getStreamArg(Desc, Call);
1023*0fca6ea1SDimitry Andric   State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1024*0fca6ea1SDimitry Andric                               State);
1025*0fca6ea1SDimitry Andric   if (!State)
1026*0fca6ea1SDimitry Andric     return;
1027*0fca6ea1SDimitry Andric   State = ensureStreamOpened(StreamVal, C, State);
1028*0fca6ea1SDimitry Andric   if (!State)
1029*0fca6ea1SDimitry Andric     return;
1030*0fca6ea1SDimitry Andric   State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
1031*0fca6ea1SDimitry Andric   if (!State)
1032*0fca6ea1SDimitry Andric     return;
1033*0fca6ea1SDimitry Andric 
1034*0fca6ea1SDimitry Andric   C.addTransition(State);
1035*0fca6ea1SDimitry Andric }
1036*0fca6ea1SDimitry Andric 
getPointeeType(const MemRegion * R)1037*0fca6ea1SDimitry Andric static QualType getPointeeType(const MemRegion *R) {
1038*0fca6ea1SDimitry Andric   if (!R)
1039*0fca6ea1SDimitry Andric     return {};
1040*0fca6ea1SDimitry Andric   if (const auto *ER = dyn_cast<ElementRegion>(R))
1041*0fca6ea1SDimitry Andric     return ER->getElementType();
1042*0fca6ea1SDimitry Andric   if (const auto *TR = dyn_cast<TypedValueRegion>(R))
1043*0fca6ea1SDimitry Andric     return TR->getValueType();
1044*0fca6ea1SDimitry Andric   if (const auto *SR = dyn_cast<SymbolicRegion>(R))
1045*0fca6ea1SDimitry Andric     return SR->getPointeeStaticType();
1046*0fca6ea1SDimitry Andric   return {};
1047*0fca6ea1SDimitry Andric }
1048*0fca6ea1SDimitry Andric 
getStartIndex(SValBuilder & SVB,const MemRegion * R)1049*0fca6ea1SDimitry Andric static std::optional<NonLoc> getStartIndex(SValBuilder &SVB,
1050*0fca6ea1SDimitry Andric                                            const MemRegion *R) {
1051*0fca6ea1SDimitry Andric   if (!R)
1052*0fca6ea1SDimitry Andric     return std::nullopt;
1053*0fca6ea1SDimitry Andric 
1054*0fca6ea1SDimitry Andric   auto Zero = [&SVB] {
1055*0fca6ea1SDimitry Andric     BasicValueFactory &BVF = SVB.getBasicValueFactory();
1056*0fca6ea1SDimitry Andric     return nonloc::ConcreteInt(BVF.getIntValue(0, /*isUnsigned=*/false));
1057*0fca6ea1SDimitry Andric   };
1058*0fca6ea1SDimitry Andric 
1059*0fca6ea1SDimitry Andric   if (const auto *ER = dyn_cast<ElementRegion>(R))
1060*0fca6ea1SDimitry Andric     return ER->getIndex();
1061*0fca6ea1SDimitry Andric   if (isa<TypedValueRegion>(R))
1062*0fca6ea1SDimitry Andric     return Zero();
1063*0fca6ea1SDimitry Andric   if (isa<SymbolicRegion>(R))
1064*0fca6ea1SDimitry Andric     return Zero();
1065*0fca6ea1SDimitry Andric   return std::nullopt;
1066*0fca6ea1SDimitry Andric }
1067*0fca6ea1SDimitry Andric 
1068*0fca6ea1SDimitry Andric static ProgramStateRef
tryToInvalidateFReadBufferByElements(ProgramStateRef State,CheckerContext & C,const CallEvent & Call,NonLoc SizeVal,NonLoc NMembVal)1069*0fca6ea1SDimitry Andric tryToInvalidateFReadBufferByElements(ProgramStateRef State, CheckerContext &C,
1070*0fca6ea1SDimitry Andric                                      const CallEvent &Call, NonLoc SizeVal,
1071*0fca6ea1SDimitry Andric                                      NonLoc NMembVal) {
1072*0fca6ea1SDimitry Andric   // Try to invalidate the individual elements.
1073*0fca6ea1SDimitry Andric   const auto *Buffer =
1074*0fca6ea1SDimitry Andric       dyn_cast_or_null<SubRegion>(Call.getArgSVal(0).getAsRegion());
1075*0fca6ea1SDimitry Andric 
1076*0fca6ea1SDimitry Andric   const ASTContext &Ctx = C.getASTContext();
1077*0fca6ea1SDimitry Andric   QualType ElemTy = getPointeeType(Buffer);
1078*0fca6ea1SDimitry Andric   std::optional<SVal> StartElementIndex =
1079*0fca6ea1SDimitry Andric       getStartIndex(C.getSValBuilder(), Buffer);
1080*0fca6ea1SDimitry Andric 
1081*0fca6ea1SDimitry Andric   // Drop the outermost ElementRegion to get the buffer.
1082*0fca6ea1SDimitry Andric   if (const auto *ER = dyn_cast_or_null<ElementRegion>(Buffer))
1083*0fca6ea1SDimitry Andric     Buffer = dyn_cast<SubRegion>(ER->getSuperRegion());
1084*0fca6ea1SDimitry Andric 
1085*0fca6ea1SDimitry Andric   std::optional<int64_t> CountVal = getKnownValue(State, NMembVal);
1086*0fca6ea1SDimitry Andric   std::optional<int64_t> Size = getKnownValue(State, SizeVal);
1087*0fca6ea1SDimitry Andric   std::optional<int64_t> StartIndexVal =
1088*0fca6ea1SDimitry Andric       getKnownValue(State, StartElementIndex.value_or(UnknownVal()));
1089*0fca6ea1SDimitry Andric 
1090*0fca6ea1SDimitry Andric   if (!ElemTy.isNull() && CountVal && Size && StartIndexVal) {
1091*0fca6ea1SDimitry Andric     int64_t NumBytesRead = Size.value() * CountVal.value();
1092*0fca6ea1SDimitry Andric     int64_t ElemSizeInChars = Ctx.getTypeSizeInChars(ElemTy).getQuantity();
1093*0fca6ea1SDimitry Andric     if (ElemSizeInChars == 0)
1094*0fca6ea1SDimitry Andric       return nullptr;
1095*0fca6ea1SDimitry Andric 
1096*0fca6ea1SDimitry Andric     bool IncompleteLastElement = (NumBytesRead % ElemSizeInChars) != 0;
1097*0fca6ea1SDimitry Andric     int64_t NumCompleteOrIncompleteElementsRead =
1098*0fca6ea1SDimitry Andric         NumBytesRead / ElemSizeInChars + IncompleteLastElement;
1099*0fca6ea1SDimitry Andric 
1100*0fca6ea1SDimitry Andric     constexpr int MaxInvalidatedElementsLimit = 64;
1101*0fca6ea1SDimitry Andric     if (NumCompleteOrIncompleteElementsRead <= MaxInvalidatedElementsLimit) {
1102*0fca6ea1SDimitry Andric       return escapeByStartIndexAndCount(State, Call, C.blockCount(), Buffer,
1103*0fca6ea1SDimitry Andric                                         ElemTy, *StartIndexVal,
1104*0fca6ea1SDimitry Andric                                         NumCompleteOrIncompleteElementsRead);
1105*0fca6ea1SDimitry Andric     }
1106*0fca6ea1SDimitry Andric   }
1107*0fca6ea1SDimitry Andric   return nullptr;
1108*0fca6ea1SDimitry Andric }
1109*0fca6ea1SDimitry Andric 
evalFreadFwrite(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C,bool IsFread) const11105ffd83dbSDimitry Andric void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
11115ffd83dbSDimitry Andric                                     const CallEvent &Call, CheckerContext &C,
11125ffd83dbSDimitry Andric                                     bool IsFread) const {
11135ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
1114*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1115*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
11165ffd83dbSDimitry Andric     return;
11175ffd83dbSDimitry Andric 
1118bdd1243dSDimitry Andric   std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
11195ffd83dbSDimitry Andric   if (!SizeVal)
11205ffd83dbSDimitry Andric     return;
1121bdd1243dSDimitry Andric   std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
11225ffd83dbSDimitry Andric   if (!NMembVal)
11235ffd83dbSDimitry Andric     return;
11245ffd83dbSDimitry Andric 
11255ffd83dbSDimitry Andric   // C'99 standard, §7.19.8.1.3, the return value of fread:
11265ffd83dbSDimitry Andric   // The fread function returns the number of elements successfully read, which
11275ffd83dbSDimitry Andric   // may be less than nmemb if a read error or end-of-file is encountered. If
11285ffd83dbSDimitry Andric   // size or nmemb is zero, fread returns zero and the contents of the array and
11295ffd83dbSDimitry Andric   // the state of the stream remain unchanged.
11305ffd83dbSDimitry Andric   if (State->isNull(*SizeVal).isConstrainedTrue() ||
11315ffd83dbSDimitry Andric       State->isNull(*NMembVal).isConstrainedTrue()) {
11325ffd83dbSDimitry Andric     // This is the "size or nmemb is zero" case.
11335ffd83dbSDimitry Andric     // Just return 0, do nothing more (not clear the error flags).
1134*0fca6ea1SDimitry Andric     C.addTransition(E.bindReturnValue(State, C, 0));
11355ffd83dbSDimitry Andric     return;
11365ffd83dbSDimitry Andric   }
11375ffd83dbSDimitry Andric 
1138*0fca6ea1SDimitry Andric   // At read, invalidate the buffer in any case of error or success,
1139*0fca6ea1SDimitry Andric   // except if EOF was already present.
1140*0fca6ea1SDimitry Andric   if (IsFread && !E.isStreamEof()) {
1141*0fca6ea1SDimitry Andric     // Try to invalidate the individual elements.
1142*0fca6ea1SDimitry Andric     // Otherwise just fall back to invalidating the whole buffer.
1143*0fca6ea1SDimitry Andric     ProgramStateRef InvalidatedState = tryToInvalidateFReadBufferByElements(
1144*0fca6ea1SDimitry Andric         State, C, Call, *SizeVal, *NMembVal);
1145*0fca6ea1SDimitry Andric     State =
1146*0fca6ea1SDimitry Andric         InvalidatedState ? InvalidatedState : escapeArgs(State, C, Call, {0});
1147*0fca6ea1SDimitry Andric   }
1148*0fca6ea1SDimitry Andric 
11495ffd83dbSDimitry Andric   // Generate a transition for the success state.
11505ffd83dbSDimitry Andric   // If we know the state to be FEOF at fread, do not add a success state.
1151*0fca6ea1SDimitry Andric   if (!IsFread || !E.isStreamEof()) {
11525ffd83dbSDimitry Andric     ProgramStateRef StateNotFailed =
1153*0fca6ea1SDimitry Andric         State->BindExpr(E.CE, C.getLocationContext(), *NMembVal);
115481ad6265SDimitry Andric     StateNotFailed =
1155*0fca6ea1SDimitry Andric         E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
11565ffd83dbSDimitry Andric     C.addTransition(StateNotFailed);
11575ffd83dbSDimitry Andric   }
11585ffd83dbSDimitry Andric 
11595ffd83dbSDimitry Andric   // Add transition for the failed state.
1160*0fca6ea1SDimitry Andric   // At write, add failure case only if "pedantic mode" is on.
1161*0fca6ea1SDimitry Andric   if (!IsFread && !PedanticMode)
11625ffd83dbSDimitry Andric     return;
1163*0fca6ea1SDimitry Andric 
1164*0fca6ea1SDimitry Andric   NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1165*0fca6ea1SDimitry Andric   ProgramStateRef StateFailed =
1166*0fca6ea1SDimitry Andric       State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1167*0fca6ea1SDimitry Andric   StateFailed = E.assumeBinOpNN(StateFailed, BO_LT, RetVal, *NMembVal);
11685ffd83dbSDimitry Andric   if (!StateFailed)
11695ffd83dbSDimitry Andric     return;
11705ffd83dbSDimitry Andric 
11715ffd83dbSDimitry Andric   StreamErrorState NewES;
11725ffd83dbSDimitry Andric   if (IsFread)
1173*0fca6ea1SDimitry Andric     NewES = E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
11745ffd83dbSDimitry Andric   else
11755ffd83dbSDimitry Andric     NewES = ErrorFError;
11765ffd83dbSDimitry Andric   // If a (non-EOF) error occurs, the resulting value of the file position
11775ffd83dbSDimitry Andric   // indicator for the stream is indeterminate.
1178*0fca6ea1SDimitry Andric   StateFailed = E.setStreamState(
1179*0fca6ea1SDimitry Andric       StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1180*0fca6ea1SDimitry Andric   C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
11815ffd83dbSDimitry Andric }
11825ffd83dbSDimitry Andric 
evalFgetx(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C,bool SingleChar) const11835f757f3fSDimitry Andric void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call,
11845f757f3fSDimitry Andric                               CheckerContext &C, bool SingleChar) const {
11855f757f3fSDimitry Andric   // `fgetc` returns the read character on success, otherwise returns EOF.
11865f757f3fSDimitry Andric   // `fgets` returns the read buffer address on success, otherwise returns NULL.
11875f757f3fSDimitry Andric 
1188*0fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
1189*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1190*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
1191*0fca6ea1SDimitry Andric     return;
1192*0fca6ea1SDimitry Andric 
1193*0fca6ea1SDimitry Andric   if (!E.isStreamEof()) {
1194*0fca6ea1SDimitry Andric     // If there was already EOF, assume that read buffer is not changed.
1195*0fca6ea1SDimitry Andric     // Otherwise it may change at success or failure.
1196*0fca6ea1SDimitry Andric     State = escapeArgs(State, C, Call, {0});
11975f757f3fSDimitry Andric     if (SingleChar) {
11985f757f3fSDimitry Andric       // Generate a transition for the success state of `fgetc`.
1199*0fca6ea1SDimitry Andric       NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
12005f757f3fSDimitry Andric       ProgramStateRef StateNotFailed =
1201*0fca6ea1SDimitry Andric           State->BindExpr(E.CE, C.getLocationContext(), RetVal);
12025f757f3fSDimitry Andric       // The returned 'unsigned char' of `fgetc` is converted to 'int',
12035f757f3fSDimitry Andric       // so we need to check if it is in range [0, 255].
1204*0fca6ea1SDimitry Andric       StateNotFailed = StateNotFailed->assumeInclusiveRange(
1205*0fca6ea1SDimitry Andric           RetVal,
1206*0fca6ea1SDimitry Andric           E.SVB.getBasicValueFactory().getValue(0, E.ACtx.UnsignedCharTy),
1207*0fca6ea1SDimitry Andric           E.SVB.getBasicValueFactory().getMaxValue(E.ACtx.UnsignedCharTy),
1208*0fca6ea1SDimitry Andric           true);
12095f757f3fSDimitry Andric       if (!StateNotFailed)
12105f757f3fSDimitry Andric         return;
12115f757f3fSDimitry Andric       C.addTransition(StateNotFailed);
12125f757f3fSDimitry Andric     } else {
12135f757f3fSDimitry Andric       // Generate a transition for the success state of `fgets`.
12145f757f3fSDimitry Andric       std::optional<DefinedSVal> GetBuf =
12155f757f3fSDimitry Andric           Call.getArgSVal(0).getAs<DefinedSVal>();
12165f757f3fSDimitry Andric       if (!GetBuf)
12175f757f3fSDimitry Andric         return;
12185f757f3fSDimitry Andric       ProgramStateRef StateNotFailed =
1219*0fca6ea1SDimitry Andric           State->BindExpr(E.CE, C.getLocationContext(), *GetBuf);
1220*0fca6ea1SDimitry Andric       StateNotFailed =
1221*0fca6ea1SDimitry Andric           E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
12225f757f3fSDimitry Andric       C.addTransition(StateNotFailed);
12235f757f3fSDimitry Andric     }
12245f757f3fSDimitry Andric   }
12255f757f3fSDimitry Andric 
12265f757f3fSDimitry Andric   // Add transition for the failed state.
12275f757f3fSDimitry Andric   ProgramStateRef StateFailed;
12285f757f3fSDimitry Andric   if (SingleChar)
1229*0fca6ea1SDimitry Andric     StateFailed = E.bindReturnValue(State, C, *EofVal);
12305f757f3fSDimitry Andric   else
1231*0fca6ea1SDimitry Andric     StateFailed = E.bindNullReturnValue(State, C);
12325f757f3fSDimitry Andric 
12335f757f3fSDimitry Andric   // If a (non-EOF) error occurs, the resulting value of the file position
12345f757f3fSDimitry Andric   // indicator for the stream is indeterminate.
12355f757f3fSDimitry Andric   StreamErrorState NewES =
1236*0fca6ea1SDimitry Andric       E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1237*0fca6ea1SDimitry Andric   StateFailed = E.setStreamState(
1238*0fca6ea1SDimitry Andric       StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1239*0fca6ea1SDimitry Andric   C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
12405f757f3fSDimitry Andric }
12415f757f3fSDimitry Andric 
evalFputx(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C,bool IsSingleChar) const12425f757f3fSDimitry Andric void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call,
12435f757f3fSDimitry Andric                               CheckerContext &C, bool IsSingleChar) const {
12445f757f3fSDimitry Andric   // `fputc` returns the written character on success, otherwise returns EOF.
1245*0fca6ea1SDimitry Andric   // `fputs` returns a nonnegative value on success, otherwise returns EOF.
1246*0fca6ea1SDimitry Andric 
1247*0fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
1248*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1249*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
1250*0fca6ea1SDimitry Andric     return;
12515f757f3fSDimitry Andric 
12525f757f3fSDimitry Andric   if (IsSingleChar) {
12535f757f3fSDimitry Andric     // Generate a transition for the success state of `fputc`.
12545f757f3fSDimitry Andric     std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
12555f757f3fSDimitry Andric     if (!PutVal)
12565f757f3fSDimitry Andric       return;
12575f757f3fSDimitry Andric     ProgramStateRef StateNotFailed =
1258*0fca6ea1SDimitry Andric         State->BindExpr(E.CE, C.getLocationContext(), *PutVal);
12595f757f3fSDimitry Andric     StateNotFailed =
1260*0fca6ea1SDimitry Andric         E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
12615f757f3fSDimitry Andric     C.addTransition(StateNotFailed);
12625f757f3fSDimitry Andric   } else {
12635f757f3fSDimitry Andric     // Generate a transition for the success state of `fputs`.
1264*0fca6ea1SDimitry Andric     NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
12655f757f3fSDimitry Andric     ProgramStateRef StateNotFailed =
1266*0fca6ea1SDimitry Andric         State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1267*0fca6ea1SDimitry Andric     StateNotFailed =
1268*0fca6ea1SDimitry Andric         E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
12695f757f3fSDimitry Andric     if (!StateNotFailed)
12705f757f3fSDimitry Andric       return;
12715f757f3fSDimitry Andric     StateNotFailed =
1272*0fca6ea1SDimitry Andric         E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
12735f757f3fSDimitry Andric     C.addTransition(StateNotFailed);
12745f757f3fSDimitry Andric   }
12755f757f3fSDimitry Andric 
1276*0fca6ea1SDimitry Andric   if (!PedanticMode)
1277*0fca6ea1SDimitry Andric     return;
1278*0fca6ea1SDimitry Andric 
12795f757f3fSDimitry Andric   // Add transition for the failed state. The resulting value of the file
12805f757f3fSDimitry Andric   // position indicator for the stream is indeterminate.
1281*0fca6ea1SDimitry Andric   ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);
1282*0fca6ea1SDimitry Andric   StateFailed = E.setStreamState(
1283*0fca6ea1SDimitry Andric       StateFailed, StreamState::getOpened(Desc, ErrorFError, true));
1284*0fca6ea1SDimitry Andric   C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
12855f757f3fSDimitry Andric }
12865f757f3fSDimitry Andric 
evalFprintf(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const12877a6dacacSDimitry Andric void StreamChecker::evalFprintf(const FnDescription *Desc,
12887a6dacacSDimitry Andric                                 const CallEvent &Call,
12897a6dacacSDimitry Andric                                 CheckerContext &C) const {
12907a6dacacSDimitry Andric   if (Call.getNumArgs() < 2)
12917a6dacacSDimitry Andric     return;
1292*0fca6ea1SDimitry Andric 
1293*0fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
1294*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1295*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
12967a6dacacSDimitry Andric     return;
12977a6dacacSDimitry Andric 
1298*0fca6ea1SDimitry Andric   NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1299*0fca6ea1SDimitry Andric   State = State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1300*0fca6ea1SDimitry Andric   auto Cond =
1301*0fca6ea1SDimitry Andric       E.SVB
1302*0fca6ea1SDimitry Andric           .evalBinOp(State, BO_GE, RetVal, E.SVB.makeZeroVal(E.ACtx.IntTy),
1303*0fca6ea1SDimitry Andric                      E.SVB.getConditionType())
13047a6dacacSDimitry Andric           .getAs<DefinedOrUnknownSVal>();
13057a6dacacSDimitry Andric   if (!Cond)
13067a6dacacSDimitry Andric     return;
13077a6dacacSDimitry Andric   ProgramStateRef StateNotFailed, StateFailed;
13087a6dacacSDimitry Andric   std::tie(StateNotFailed, StateFailed) = State->assume(*Cond);
13097a6dacacSDimitry Andric 
13107a6dacacSDimitry Andric   StateNotFailed =
1311*0fca6ea1SDimitry Andric       E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
13127a6dacacSDimitry Andric   C.addTransition(StateNotFailed);
13137a6dacacSDimitry Andric 
1314*0fca6ea1SDimitry Andric   if (!PedanticMode)
1315*0fca6ea1SDimitry Andric     return;
1316*0fca6ea1SDimitry Andric 
13177a6dacacSDimitry Andric   // Add transition for the failed state. The resulting value of the file
13187a6dacacSDimitry Andric   // position indicator for the stream is indeterminate.
1319*0fca6ea1SDimitry Andric   StateFailed = E.setStreamState(
1320*0fca6ea1SDimitry Andric       StateFailed, StreamState::getOpened(Desc, ErrorFError, true));
1321*0fca6ea1SDimitry Andric   C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
13227a6dacacSDimitry Andric }
13237a6dacacSDimitry Andric 
evalFscanf(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const13247a6dacacSDimitry Andric void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call,
13257a6dacacSDimitry Andric                                CheckerContext &C) const {
13267a6dacacSDimitry Andric   if (Call.getNumArgs() < 2)
13277a6dacacSDimitry Andric     return;
1328*0fca6ea1SDimitry Andric 
1329*0fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
1330*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1331*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
13327a6dacacSDimitry Andric     return;
13337a6dacacSDimitry Andric 
13347a6dacacSDimitry Andric   // Add the success state.
13357a6dacacSDimitry Andric   // In this context "success" means there is not an EOF or other read error
13367a6dacacSDimitry Andric   // before any item is matched in 'fscanf'. But there may be match failure,
13377a6dacacSDimitry Andric   // therefore return value can be 0 or greater.
13387a6dacacSDimitry Andric   // It is not specified what happens if some items (not all) are matched and
13397a6dacacSDimitry Andric   // then EOF or read error happens. Now this case is handled like a "success"
13407a6dacacSDimitry Andric   // case, and no error flags are set on the stream. This is probably not
13417a6dacacSDimitry Andric   // accurate, and the POSIX documentation does not tell more.
1342*0fca6ea1SDimitry Andric   if (!E.isStreamEof()) {
1343*0fca6ea1SDimitry Andric     NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
13447a6dacacSDimitry Andric     ProgramStateRef StateNotFailed =
1345*0fca6ea1SDimitry Andric         State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1346*0fca6ea1SDimitry Andric     StateNotFailed =
1347*0fca6ea1SDimitry Andric         E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1348*0fca6ea1SDimitry Andric     if (!StateNotFailed)
13497a6dacacSDimitry Andric       return;
13507a6dacacSDimitry Andric 
1351*0fca6ea1SDimitry Andric     if (auto const *Callee = Call.getCalleeIdentifier();
1352*0fca6ea1SDimitry Andric         !Callee || Callee->getName() != "vfscanf") {
1353*0fca6ea1SDimitry Andric       SmallVector<unsigned int> EscArgs;
1354*0fca6ea1SDimitry Andric       for (auto EscArg : llvm::seq(2u, Call.getNumArgs()))
1355*0fca6ea1SDimitry Andric         EscArgs.push_back(EscArg);
1356*0fca6ea1SDimitry Andric       StateNotFailed = escapeArgs(StateNotFailed, C, Call, EscArgs);
1357*0fca6ea1SDimitry Andric     }
1358*0fca6ea1SDimitry Andric 
1359*0fca6ea1SDimitry Andric     if (StateNotFailed)
13607a6dacacSDimitry Andric       C.addTransition(StateNotFailed);
13617a6dacacSDimitry Andric   }
13627a6dacacSDimitry Andric 
13637a6dacacSDimitry Andric   // Add transition for the failed state.
13647a6dacacSDimitry Andric   // Error occurs if nothing is matched yet and reading the input fails.
13657a6dacacSDimitry Andric   // Error can be EOF, or other error. At "other error" FERROR or 'errno' can
13667a6dacacSDimitry Andric   // be set but it is not further specified if all are required to be set.
13677a6dacacSDimitry Andric   // Documentation does not mention, but file position will be set to
13687a6dacacSDimitry Andric   // indeterminate similarly as at 'fread'.
1369*0fca6ea1SDimitry Andric   ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);
1370*0fca6ea1SDimitry Andric   StreamErrorState NewES =
1371*0fca6ea1SDimitry Andric       E.isStreamEof() ? ErrorFEof : ErrorNone | ErrorFEof | ErrorFError;
1372*0fca6ea1SDimitry Andric   StateFailed = E.setStreamState(
1373*0fca6ea1SDimitry Andric       StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1374*0fca6ea1SDimitry Andric   C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
13757a6dacacSDimitry Andric }
13767a6dacacSDimitry Andric 
evalUngetc(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1377297eecfbSDimitry Andric void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent &Call,
1378297eecfbSDimitry Andric                                CheckerContext &C) const {
1379297eecfbSDimitry Andric   ProgramStateRef State = C.getState();
1380*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1381*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
1382297eecfbSDimitry Andric     return;
1383297eecfbSDimitry Andric 
1384297eecfbSDimitry Andric   // Generate a transition for the success state.
1385297eecfbSDimitry Andric   std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
1386297eecfbSDimitry Andric   if (!PutVal)
1387297eecfbSDimitry Andric     return;
1388*0fca6ea1SDimitry Andric   ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, *PutVal);
1389297eecfbSDimitry Andric   StateNotFailed =
1390*0fca6ea1SDimitry Andric       E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1391297eecfbSDimitry Andric   C.addTransition(StateNotFailed);
1392297eecfbSDimitry Andric 
1393297eecfbSDimitry Andric   // Add transition for the failed state.
1394297eecfbSDimitry Andric   // Failure of 'ungetc' does not result in feof or ferror state.
1395297eecfbSDimitry Andric   // If the PutVal has value of EofVal the function should "fail", but this is
1396297eecfbSDimitry Andric   // the same transition as the success state.
1397297eecfbSDimitry Andric   // In this case only one state transition is added by the analyzer (the two
1398297eecfbSDimitry Andric   // new states may be similar).
1399*0fca6ea1SDimitry Andric   ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);
1400*0fca6ea1SDimitry Andric   StateFailed = E.setStreamState(StateFailed, StreamState::getOpened(Desc));
1401297eecfbSDimitry Andric   C.addTransition(StateFailed);
1402297eecfbSDimitry Andric }
1403297eecfbSDimitry Andric 
evalGetdelim(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const14047a6dacacSDimitry Andric void StreamChecker::evalGetdelim(const FnDescription *Desc,
14057a6dacacSDimitry Andric                                  const CallEvent &Call,
14067a6dacacSDimitry Andric                                  CheckerContext &C) const {
14077a6dacacSDimitry Andric   ProgramStateRef State = C.getState();
1408*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1409*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
14107a6dacacSDimitry Andric     return;
14117a6dacacSDimitry Andric 
14127a6dacacSDimitry Andric   // Upon successful completion, the getline() and getdelim() functions shall
14137a6dacacSDimitry Andric   // return the number of bytes written into the buffer.
14147a6dacacSDimitry Andric   // If the end-of-file indicator for the stream is set, the function shall
14157a6dacacSDimitry Andric   // return -1.
14167a6dacacSDimitry Andric   // If an error occurs, the function shall return -1 and set 'errno'.
14177a6dacacSDimitry Andric 
1418*0fca6ea1SDimitry Andric   if (!E.isStreamEof()) {
1419*0fca6ea1SDimitry Andric     // Escape buffer and size (may change by the call).
1420*0fca6ea1SDimitry Andric     // May happen even at error (partial read?).
1421*0fca6ea1SDimitry Andric     State = escapeArgs(State, C, Call, {0, 1});
1422*0fca6ea1SDimitry Andric 
14237a6dacacSDimitry Andric     // Add transition for the successful state.
1424*0fca6ea1SDimitry Andric     NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1425*0fca6ea1SDimitry Andric     ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, RetVal);
1426*0fca6ea1SDimitry Andric     StateNotFailed =
1427*0fca6ea1SDimitry Andric         E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1428*0fca6ea1SDimitry Andric 
1429*0fca6ea1SDimitry Andric     // On success, a buffer is allocated.
1430*0fca6ea1SDimitry Andric     auto NewLinePtr = getPointeeVal(Call.getArgSVal(0), State);
1431*0fca6ea1SDimitry Andric     if (NewLinePtr && isa<DefinedOrUnknownSVal>(*NewLinePtr))
1432*0fca6ea1SDimitry Andric       StateNotFailed = StateNotFailed->assume(
1433*0fca6ea1SDimitry Andric           NewLinePtr->castAs<DefinedOrUnknownSVal>(), true);
1434*0fca6ea1SDimitry Andric 
1435*0fca6ea1SDimitry Andric     // The buffer size `*n` must be enough to hold the whole line, and
1436*0fca6ea1SDimitry Andric     // greater than the return value, since it has to account for '\0'.
1437*0fca6ea1SDimitry Andric     SVal SizePtrSval = Call.getArgSVal(1);
1438*0fca6ea1SDimitry Andric     auto NVal = getPointeeVal(SizePtrSval, State);
1439*0fca6ea1SDimitry Andric     if (NVal && isa<NonLoc>(*NVal)) {
1440*0fca6ea1SDimitry Andric       StateNotFailed = E.assumeBinOpNN(StateNotFailed, BO_GT,
1441*0fca6ea1SDimitry Andric                                        NVal->castAs<NonLoc>(), RetVal);
1442*0fca6ea1SDimitry Andric       StateNotFailed = E.bindReturnValue(StateNotFailed, C, RetVal);
1443*0fca6ea1SDimitry Andric     }
14447a6dacacSDimitry Andric     if (!StateNotFailed)
14457a6dacacSDimitry Andric       return;
14467a6dacacSDimitry Andric     C.addTransition(StateNotFailed);
14477a6dacacSDimitry Andric   }
14487a6dacacSDimitry Andric 
14497a6dacacSDimitry Andric   // Add transition for the failed state.
14507a6dacacSDimitry Andric   // If a (non-EOF) error occurs, the resulting value of the file position
14517a6dacacSDimitry Andric   // indicator for the stream is indeterminate.
1452*0fca6ea1SDimitry Andric   ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1);
14537a6dacacSDimitry Andric   StreamErrorState NewES =
1454*0fca6ea1SDimitry Andric       E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1455*0fca6ea1SDimitry Andric   StateFailed = E.setStreamState(
1456*0fca6ea1SDimitry Andric       StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1457*0fca6ea1SDimitry Andric   // On failure, the content of the buffer is undefined.
1458*0fca6ea1SDimitry Andric   if (auto NewLinePtr = getPointeeVal(Call.getArgSVal(0), State))
1459*0fca6ea1SDimitry Andric     StateFailed = StateFailed->bindLoc(*NewLinePtr, UndefinedVal(),
1460*0fca6ea1SDimitry Andric                                        C.getLocationContext());
1461*0fca6ea1SDimitry Andric   C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
14627a6dacacSDimitry Andric }
14637a6dacacSDimitry Andric 
preFseek(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const14645ffd83dbSDimitry Andric void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
14655ffd83dbSDimitry Andric                              CheckerContext &C) const {
14665ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
14675ffd83dbSDimitry Andric   SVal StreamVal = getStreamArg(Desc, Call);
1468fe6060f1SDimitry Andric   State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1469fe6060f1SDimitry Andric                               State);
14705ffd83dbSDimitry Andric   if (!State)
14715ffd83dbSDimitry Andric     return;
14725ffd83dbSDimitry Andric   State = ensureStreamOpened(StreamVal, C, State);
14735ffd83dbSDimitry Andric   if (!State)
14745ffd83dbSDimitry Andric     return;
14755ffd83dbSDimitry Andric   State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
14765ffd83dbSDimitry Andric   if (!State)
14775ffd83dbSDimitry Andric     return;
14785ffd83dbSDimitry Andric 
14795ffd83dbSDimitry Andric   C.addTransition(State);
14805ffd83dbSDimitry Andric }
14815ffd83dbSDimitry Andric 
evalFseek(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const14825ffd83dbSDimitry Andric void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
14835ffd83dbSDimitry Andric                               CheckerContext &C) const {
14845ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
1485*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1486*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
14875ffd83dbSDimitry Andric     return;
14885ffd83dbSDimitry Andric 
1489*0fca6ea1SDimitry Andric   // Add success state.
1490*0fca6ea1SDimitry Andric   ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, 0);
1491*0fca6ea1SDimitry Andric   // No failure: Reset the state to opened with no error.
14925ffd83dbSDimitry Andric   StateNotFailed =
1493*0fca6ea1SDimitry Andric       E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1494*0fca6ea1SDimitry Andric   C.addTransition(StateNotFailed);
1495*0fca6ea1SDimitry Andric 
1496*0fca6ea1SDimitry Andric   if (!PedanticMode)
1497*0fca6ea1SDimitry Andric     return;
1498*0fca6ea1SDimitry Andric 
1499*0fca6ea1SDimitry Andric   // Add failure state.
1500*0fca6ea1SDimitry Andric   // At error it is possible that fseek fails but sets none of the error flags.
15015ffd83dbSDimitry Andric   // If fseek failed, assume that the file position becomes indeterminate in any
15025ffd83dbSDimitry Andric   // case.
1503*0fca6ea1SDimitry Andric   // It is allowed to set the position beyond the end of the file. EOF error
1504*0fca6ea1SDimitry Andric   // should not occur.
1505*0fca6ea1SDimitry Andric   ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1);
1506*0fca6ea1SDimitry Andric   StateFailed = E.setStreamState(
1507*0fca6ea1SDimitry Andric       StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
1508*0fca6ea1SDimitry Andric   C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
15095ffd83dbSDimitry Andric }
15105ffd83dbSDimitry Andric 
evalFgetpos(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1511bdd1243dSDimitry Andric void StreamChecker::evalFgetpos(const FnDescription *Desc,
1512bdd1243dSDimitry Andric                                 const CallEvent &Call,
1513bdd1243dSDimitry Andric                                 CheckerContext &C) const {
1514bdd1243dSDimitry Andric   ProgramStateRef State = C.getState();
1515*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1516*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
1517bdd1243dSDimitry Andric     return;
1518bdd1243dSDimitry Andric 
1519bdd1243dSDimitry Andric   ProgramStateRef StateNotFailed, StateFailed;
1520*0fca6ea1SDimitry Andric   std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C);
1521*0fca6ea1SDimitry Andric   StateNotFailed = escapeArgs(StateNotFailed, C, Call, {1});
1522bdd1243dSDimitry Andric 
1523bdd1243dSDimitry Andric   // This function does not affect the stream state.
1524bdd1243dSDimitry Andric   // Still we add success and failure state with the appropriate return value.
1525bdd1243dSDimitry Andric   // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
1526bdd1243dSDimitry Andric   C.addTransition(StateNotFailed);
1527bdd1243dSDimitry Andric   C.addTransition(StateFailed);
1528bdd1243dSDimitry Andric }
1529bdd1243dSDimitry Andric 
evalFsetpos(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1530bdd1243dSDimitry Andric void StreamChecker::evalFsetpos(const FnDescription *Desc,
1531bdd1243dSDimitry Andric                                 const CallEvent &Call,
1532bdd1243dSDimitry Andric                                 CheckerContext &C) const {
1533bdd1243dSDimitry Andric   ProgramStateRef State = C.getState();
1534*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1535*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
1536bdd1243dSDimitry Andric     return;
1537bdd1243dSDimitry Andric 
1538bdd1243dSDimitry Andric   ProgramStateRef StateNotFailed, StateFailed;
1539*0fca6ea1SDimitry Andric   std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C);
1540bdd1243dSDimitry Andric 
1541*0fca6ea1SDimitry Andric   StateNotFailed = E.setStreamState(
1542*0fca6ea1SDimitry Andric       StateNotFailed, StreamState::getOpened(Desc, ErrorNone, false));
1543*0fca6ea1SDimitry Andric   C.addTransition(StateNotFailed);
1544*0fca6ea1SDimitry Andric 
1545*0fca6ea1SDimitry Andric   if (!PedanticMode)
1546*0fca6ea1SDimitry Andric     return;
1547bdd1243dSDimitry Andric 
1548bdd1243dSDimitry Andric   // At failure ferror could be set.
1549bdd1243dSDimitry Andric   // The standards do not tell what happens with the file position at failure.
1550bdd1243dSDimitry Andric   // But we can assume that it is dangerous to make a next I/O operation after
1551bdd1243dSDimitry Andric   // the position was not set correctly (similar to 'fseek').
1552*0fca6ea1SDimitry Andric   StateFailed = E.setStreamState(
1553*0fca6ea1SDimitry Andric       StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
1554bdd1243dSDimitry Andric 
1555*0fca6ea1SDimitry Andric   C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1556bdd1243dSDimitry Andric }
1557bdd1243dSDimitry Andric 
evalFtell(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1558bdd1243dSDimitry Andric void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
1559bdd1243dSDimitry Andric                               CheckerContext &C) const {
1560bdd1243dSDimitry Andric   ProgramStateRef State = C.getState();
1561*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1562*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
1563bdd1243dSDimitry Andric     return;
1564bdd1243dSDimitry Andric 
1565*0fca6ea1SDimitry Andric   NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1566bdd1243dSDimitry Andric   ProgramStateRef StateNotFailed =
1567*0fca6ea1SDimitry Andric       State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1568*0fca6ea1SDimitry Andric   StateNotFailed =
1569*0fca6ea1SDimitry Andric       E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1570bdd1243dSDimitry Andric   if (!StateNotFailed)
1571bdd1243dSDimitry Andric     return;
1572bdd1243dSDimitry Andric 
1573*0fca6ea1SDimitry Andric   ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1);
1574bdd1243dSDimitry Andric 
15755f757f3fSDimitry Andric   // This function does not affect the stream state.
15765f757f3fSDimitry Andric   // Still we add success and failure state with the appropriate return value.
15775f757f3fSDimitry Andric   // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
1578bdd1243dSDimitry Andric   C.addTransition(StateNotFailed);
1579bdd1243dSDimitry Andric   C.addTransition(StateFailed);
1580bdd1243dSDimitry Andric }
1581bdd1243dSDimitry Andric 
evalRewind(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1582bdd1243dSDimitry Andric void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call,
1583bdd1243dSDimitry Andric                                CheckerContext &C) const {
1584bdd1243dSDimitry Andric   ProgramStateRef State = C.getState();
1585*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1586*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
1587bdd1243dSDimitry Andric     return;
1588bdd1243dSDimitry Andric 
1589*0fca6ea1SDimitry Andric   State =
1590*0fca6ea1SDimitry Andric       E.setStreamState(State, StreamState::getOpened(Desc, ErrorNone, false));
15915ffd83dbSDimitry Andric   C.addTransition(State);
15925ffd83dbSDimitry Andric }
15935ffd83dbSDimitry Andric 
preFflush(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1594cb14a3feSDimitry Andric void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call,
1595cb14a3feSDimitry Andric                               CheckerContext &C) const {
1596cb14a3feSDimitry Andric   ProgramStateRef State = C.getState();
1597cb14a3feSDimitry Andric   SVal StreamVal = getStreamArg(Desc, Call);
1598cb14a3feSDimitry Andric   std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>();
1599cb14a3feSDimitry Andric   if (!Stream)
1600cb14a3feSDimitry Andric     return;
1601cb14a3feSDimitry Andric 
1602cb14a3feSDimitry Andric   ProgramStateRef StateNotNull, StateNull;
1603cb14a3feSDimitry Andric   std::tie(StateNotNull, StateNull) =
1604cb14a3feSDimitry Andric       C.getConstraintManager().assumeDual(State, *Stream);
1605cb14a3feSDimitry Andric   if (StateNotNull && !StateNull)
1606cb14a3feSDimitry Andric     ensureStreamOpened(StreamVal, C, StateNotNull);
1607cb14a3feSDimitry Andric }
1608cb14a3feSDimitry Andric 
evalFflush(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1609cb14a3feSDimitry Andric void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call,
1610cb14a3feSDimitry Andric                                CheckerContext &C) const {
1611cb14a3feSDimitry Andric   ProgramStateRef State = C.getState();
1612cb14a3feSDimitry Andric   SVal StreamVal = getStreamArg(Desc, Call);
1613cb14a3feSDimitry Andric   std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>();
1614cb14a3feSDimitry Andric   if (!Stream)
1615cb14a3feSDimitry Andric     return;
1616cb14a3feSDimitry Andric 
1617cb14a3feSDimitry Andric   // Skip if the stream can be both NULL and non-NULL.
1618cb14a3feSDimitry Andric   ProgramStateRef StateNotNull, StateNull;
1619cb14a3feSDimitry Andric   std::tie(StateNotNull, StateNull) =
1620cb14a3feSDimitry Andric       C.getConstraintManager().assumeDual(State, *Stream);
1621cb14a3feSDimitry Andric   if (StateNotNull && StateNull)
1622cb14a3feSDimitry Andric     return;
1623cb14a3feSDimitry Andric   if (StateNotNull && !StateNull)
1624cb14a3feSDimitry Andric     State = StateNotNull;
1625cb14a3feSDimitry Andric   else
1626cb14a3feSDimitry Andric     State = StateNull;
1627cb14a3feSDimitry Andric 
1628cb14a3feSDimitry Andric   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1629cb14a3feSDimitry Andric   if (!CE)
1630cb14a3feSDimitry Andric     return;
1631cb14a3feSDimitry Andric 
1632cb14a3feSDimitry Andric   // `fflush` returns EOF on failure, otherwise returns 0.
1633cb14a3feSDimitry Andric   ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
1634cb14a3feSDimitry Andric   ProgramStateRef StateNotFailed = bindInt(0, State, C, CE);
1635cb14a3feSDimitry Andric 
1636cb14a3feSDimitry Andric   // Clear error states if `fflush` returns 0, but retain their EOF flags.
1637cb14a3feSDimitry Andric   auto ClearErrorInNotFailed = [&StateNotFailed, Desc](SymbolRef Sym,
1638cb14a3feSDimitry Andric                                                        const StreamState *SS) {
1639cb14a3feSDimitry Andric     if (SS->ErrorState & ErrorFError) {
1640cb14a3feSDimitry Andric       StreamErrorState NewES =
1641cb14a3feSDimitry Andric           (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone;
1642cb14a3feSDimitry Andric       StreamState NewSS = StreamState::getOpened(Desc, NewES, false);
1643cb14a3feSDimitry Andric       StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS);
1644cb14a3feSDimitry Andric     }
1645cb14a3feSDimitry Andric   };
1646cb14a3feSDimitry Andric 
1647cb14a3feSDimitry Andric   if (StateNotNull && !StateNull) {
1648cb14a3feSDimitry Andric     // Skip if the input stream's state is unknown, open-failed or closed.
1649cb14a3feSDimitry Andric     if (SymbolRef StreamSym = StreamVal.getAsSymbol()) {
1650cb14a3feSDimitry Andric       const StreamState *SS = State->get<StreamMap>(StreamSym);
1651cb14a3feSDimitry Andric       if (SS) {
1652cb14a3feSDimitry Andric         assert(SS->isOpened() && "Stream is expected to be opened");
1653cb14a3feSDimitry Andric         ClearErrorInNotFailed(StreamSym, SS);
1654cb14a3feSDimitry Andric       } else
1655cb14a3feSDimitry Andric         return;
1656cb14a3feSDimitry Andric     }
1657cb14a3feSDimitry Andric   } else {
1658cb14a3feSDimitry Andric     // Clear error states for all streams.
1659cb14a3feSDimitry Andric     const StreamMapTy &Map = StateNotFailed->get<StreamMap>();
1660cb14a3feSDimitry Andric     for (const auto &I : Map) {
1661cb14a3feSDimitry Andric       SymbolRef Sym = I.first;
1662cb14a3feSDimitry Andric       const StreamState &SS = I.second;
1663cb14a3feSDimitry Andric       if (SS.isOpened())
1664cb14a3feSDimitry Andric         ClearErrorInNotFailed(Sym, &SS);
1665cb14a3feSDimitry Andric     }
1666cb14a3feSDimitry Andric   }
1667cb14a3feSDimitry Andric 
1668cb14a3feSDimitry Andric   C.addTransition(StateNotFailed);
1669cb14a3feSDimitry Andric   C.addTransition(StateFailed);
1670cb14a3feSDimitry Andric }
1671cb14a3feSDimitry Andric 
evalClearerr(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1672*0fca6ea1SDimitry Andric void StreamChecker::evalClearerr(const FnDescription *Desc,
1673*0fca6ea1SDimitry Andric                                  const CallEvent &Call,
1674*0fca6ea1SDimitry Andric                                  CheckerContext &C) const {
1675*0fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
1676*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1677*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
1678*0fca6ea1SDimitry Andric     return;
1679*0fca6ea1SDimitry Andric 
1680*0fca6ea1SDimitry Andric   // FilePositionIndeterminate is not cleared.
1681*0fca6ea1SDimitry Andric   State = E.setStreamState(
1682*0fca6ea1SDimitry Andric       State,
1683*0fca6ea1SDimitry Andric       StreamState::getOpened(Desc, ErrorNone, E.SS->FilePositionIndeterminate));
1684*0fca6ea1SDimitry Andric   C.addTransition(State);
1685*0fca6ea1SDimitry Andric }
1686*0fca6ea1SDimitry Andric 
evalFeofFerror(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C,const StreamErrorState & ErrorKind) const1687*0fca6ea1SDimitry Andric void StreamChecker::evalFeofFerror(const FnDescription *Desc,
1688*0fca6ea1SDimitry Andric                                    const CallEvent &Call, CheckerContext &C,
1689*0fca6ea1SDimitry Andric                                    const StreamErrorState &ErrorKind) const {
1690*0fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
1691*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1692*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
1693*0fca6ea1SDimitry Andric     return;
1694*0fca6ea1SDimitry Andric 
1695*0fca6ea1SDimitry Andric   if (E.SS->ErrorState & ErrorKind) {
1696*0fca6ea1SDimitry Andric     // Execution path with error of ErrorKind.
1697*0fca6ea1SDimitry Andric     // Function returns true.
1698*0fca6ea1SDimitry Andric     // From now on it is the only one error state.
1699*0fca6ea1SDimitry Andric     ProgramStateRef TrueState = bindAndAssumeTrue(State, C, E.CE);
1700*0fca6ea1SDimitry Andric     C.addTransition(E.setStreamState(
1701*0fca6ea1SDimitry Andric         TrueState, StreamState::getOpened(Desc, ErrorKind,
1702*0fca6ea1SDimitry Andric                                           E.SS->FilePositionIndeterminate &&
1703*0fca6ea1SDimitry Andric                                               !ErrorKind.isFEof())));
1704*0fca6ea1SDimitry Andric   }
1705*0fca6ea1SDimitry Andric   if (StreamErrorState NewES = E.SS->ErrorState & (~ErrorKind)) {
1706*0fca6ea1SDimitry Andric     // Execution path(s) with ErrorKind not set.
1707*0fca6ea1SDimitry Andric     // Function returns false.
1708*0fca6ea1SDimitry Andric     // New error state is everything before minus ErrorKind.
1709*0fca6ea1SDimitry Andric     ProgramStateRef FalseState = E.bindReturnValue(State, C, 0);
1710*0fca6ea1SDimitry Andric     C.addTransition(E.setStreamState(
1711*0fca6ea1SDimitry Andric         FalseState,
1712*0fca6ea1SDimitry Andric         StreamState::getOpened(
1713*0fca6ea1SDimitry Andric             Desc, NewES, E.SS->FilePositionIndeterminate && !NewES.isFEof())));
1714*0fca6ea1SDimitry Andric   }
1715*0fca6ea1SDimitry Andric }
1716*0fca6ea1SDimitry Andric 
evalFileno(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1717*0fca6ea1SDimitry Andric void StreamChecker::evalFileno(const FnDescription *Desc, const CallEvent &Call,
1718*0fca6ea1SDimitry Andric                                CheckerContext &C) const {
1719*0fca6ea1SDimitry Andric   // Fileno should fail only if the passed pointer is invalid.
1720*0fca6ea1SDimitry Andric   // Some of the preconditions are checked already in preDefault.
1721*0fca6ea1SDimitry Andric   // Here we can assume that the operation does not fail, because if we
1722*0fca6ea1SDimitry Andric   // introduced a separate branch where fileno() returns -1, then it would cause
1723*0fca6ea1SDimitry Andric   // many unexpected and unwanted warnings in situations where fileno() is
1724*0fca6ea1SDimitry Andric   // called on valid streams.
1725*0fca6ea1SDimitry Andric   // The stream error states are not modified by 'fileno', and 'errno' is also
1726*0fca6ea1SDimitry Andric   // left unchanged (so this evalCall does not invalidate it, but we have a
1727*0fca6ea1SDimitry Andric   // custom evalCall instead of the default that would invalidate it).
1728*0fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
1729*0fca6ea1SDimitry Andric   StreamOperationEvaluator E(C);
1730*0fca6ea1SDimitry Andric   if (!E.Init(Desc, Call, C, State))
1731*0fca6ea1SDimitry Andric     return;
1732*0fca6ea1SDimitry Andric 
1733*0fca6ea1SDimitry Andric   NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1734*0fca6ea1SDimitry Andric   State = State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1735*0fca6ea1SDimitry Andric   State = E.assumeBinOpNN(State, BO_GE, RetVal, E.getZeroVal(Call));
1736*0fca6ea1SDimitry Andric   if (!State)
1737*0fca6ea1SDimitry Andric     return;
1738*0fca6ea1SDimitry Andric 
1739*0fca6ea1SDimitry Andric   C.addTransition(State);
1740*0fca6ea1SDimitry Andric }
1741*0fca6ea1SDimitry Andric 
preDefault(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1742*0fca6ea1SDimitry Andric void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
1743*0fca6ea1SDimitry Andric                                CheckerContext &C) const {
1744*0fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
1745*0fca6ea1SDimitry Andric   SVal StreamVal = getStreamArg(Desc, Call);
1746*0fca6ea1SDimitry Andric   State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1747*0fca6ea1SDimitry Andric                               State);
1748*0fca6ea1SDimitry Andric   if (!State)
1749*0fca6ea1SDimitry Andric     return;
1750*0fca6ea1SDimitry Andric   State = ensureStreamOpened(StreamVal, C, State);
1751*0fca6ea1SDimitry Andric   if (!State)
1752*0fca6ea1SDimitry Andric     return;
1753*0fca6ea1SDimitry Andric 
1754*0fca6ea1SDimitry Andric   C.addTransition(State);
1755*0fca6ea1SDimitry Andric }
1756*0fca6ea1SDimitry Andric 
evalSetFeofFerror(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C,const StreamErrorState & ErrorKind,bool Indeterminate) const1757*0fca6ea1SDimitry Andric void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
1758*0fca6ea1SDimitry Andric                                       const CallEvent &Call, CheckerContext &C,
1759*0fca6ea1SDimitry Andric                                       const StreamErrorState &ErrorKind,
1760*0fca6ea1SDimitry Andric                                       bool Indeterminate) const {
1761*0fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
1762*0fca6ea1SDimitry Andric   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1763*0fca6ea1SDimitry Andric   assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
1764*0fca6ea1SDimitry Andric   const StreamState *SS = State->get<StreamMap>(StreamSym);
1765*0fca6ea1SDimitry Andric   assert(SS && "Stream should be tracked by the checker.");
1766*0fca6ea1SDimitry Andric   State = State->set<StreamMap>(
1767*0fca6ea1SDimitry Andric       StreamSym,
1768*0fca6ea1SDimitry Andric       StreamState::getOpened(SS->LastOperation, ErrorKind, Indeterminate));
1769*0fca6ea1SDimitry Andric   C.addTransition(State);
1770*0fca6ea1SDimitry Andric }
1771*0fca6ea1SDimitry Andric 
17725ffd83dbSDimitry Andric ProgramStateRef
ensureStreamNonNull(SVal StreamVal,const Expr * StreamE,CheckerContext & C,ProgramStateRef State) const1773fe6060f1SDimitry Andric StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
1774fe6060f1SDimitry Andric                                    CheckerContext &C,
17755ffd83dbSDimitry Andric                                    ProgramStateRef State) const {
17765ffd83dbSDimitry Andric   auto Stream = StreamVal.getAs<DefinedSVal>();
17775ffd83dbSDimitry Andric   if (!Stream)
17785ffd83dbSDimitry Andric     return State;
1779480093f4SDimitry Andric 
1780480093f4SDimitry Andric   ConstraintManager &CM = C.getConstraintManager();
17815ffd83dbSDimitry Andric 
1782480093f4SDimitry Andric   ProgramStateRef StateNotNull, StateNull;
17835f757f3fSDimitry Andric   std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream);
1784480093f4SDimitry Andric 
1785480093f4SDimitry Andric   if (!StateNotNull && StateNull) {
178606c3fb27SDimitry Andric     if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
178706c3fb27SDimitry Andric       auto R = std::make_unique<PathSensitiveBugReport>(
178806c3fb27SDimitry Andric           BT_FileNull, "Stream pointer might be NULL.", N);
178906c3fb27SDimitry Andric       if (StreamE)
179006c3fb27SDimitry Andric         bugreporter::trackExpressionValue(N, StreamE, *R);
179106c3fb27SDimitry Andric       C.emitReport(std::move(R));
179206c3fb27SDimitry Andric     }
17935ffd83dbSDimitry Andric     return nullptr;
1794480093f4SDimitry Andric   }
1795480093f4SDimitry Andric 
17965ffd83dbSDimitry Andric   return StateNotNull;
1797480093f4SDimitry Andric }
1798480093f4SDimitry Andric 
ensureStreamOpened(SVal StreamVal,CheckerContext & C,ProgramStateRef State) const17995ffd83dbSDimitry Andric ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
18005ffd83dbSDimitry Andric                                                   CheckerContext &C,
18015ffd83dbSDimitry Andric                                                   ProgramStateRef State) const {
18025ffd83dbSDimitry Andric   SymbolRef Sym = StreamVal.getAsSymbol();
18035ffd83dbSDimitry Andric   if (!Sym)
18045ffd83dbSDimitry Andric     return State;
18055ffd83dbSDimitry Andric 
18065ffd83dbSDimitry Andric   const StreamState *SS = State->get<StreamMap>(Sym);
18075ffd83dbSDimitry Andric   if (!SS)
18085ffd83dbSDimitry Andric     return State;
18095ffd83dbSDimitry Andric 
18105ffd83dbSDimitry Andric   if (SS->isClosed()) {
18115ffd83dbSDimitry Andric     // Using a stream pointer after 'fclose' causes undefined behavior
18125ffd83dbSDimitry Andric     // according to cppreference.com .
18135ffd83dbSDimitry Andric     ExplodedNode *N = C.generateErrorNode();
18145ffd83dbSDimitry Andric     if (N) {
18155ffd83dbSDimitry Andric       C.emitReport(std::make_unique<PathSensitiveBugReport>(
18165ffd83dbSDimitry Andric           BT_UseAfterClose,
18175ffd83dbSDimitry Andric           "Stream might be already closed. Causes undefined behaviour.", N));
18185ffd83dbSDimitry Andric       return nullptr;
1819480093f4SDimitry Andric     }
1820480093f4SDimitry Andric 
18215ffd83dbSDimitry Andric     return State;
18225ffd83dbSDimitry Andric   }
18235ffd83dbSDimitry Andric 
18245ffd83dbSDimitry Andric   if (SS->isOpenFailed()) {
18255ffd83dbSDimitry Andric     // Using a stream that has failed to open is likely to cause problems.
18265ffd83dbSDimitry Andric     // This should usually not occur because stream pointer is NULL.
18275ffd83dbSDimitry Andric     // But freopen can cause a state when stream pointer remains non-null but
18285ffd83dbSDimitry Andric     // failed to open.
18295ffd83dbSDimitry Andric     ExplodedNode *N = C.generateErrorNode();
18305ffd83dbSDimitry Andric     if (N) {
18315ffd83dbSDimitry Andric       C.emitReport(std::make_unique<PathSensitiveBugReport>(
18325ffd83dbSDimitry Andric           BT_UseAfterOpenFailed,
18335ffd83dbSDimitry Andric           "Stream might be invalid after "
18345ffd83dbSDimitry Andric           "(re-)opening it has failed. "
18355ffd83dbSDimitry Andric           "Can cause undefined behaviour.",
18365ffd83dbSDimitry Andric           N));
18375ffd83dbSDimitry Andric       return nullptr;
18385ffd83dbSDimitry Andric     }
18395ffd83dbSDimitry Andric   }
18405ffd83dbSDimitry Andric 
18415ffd83dbSDimitry Andric   return State;
18425ffd83dbSDimitry Andric }
18435ffd83dbSDimitry Andric 
ensureNoFilePositionIndeterminate(SVal StreamVal,CheckerContext & C,ProgramStateRef State) const18445ffd83dbSDimitry Andric ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
18455ffd83dbSDimitry Andric     SVal StreamVal, CheckerContext &C, ProgramStateRef State) const {
18465ffd83dbSDimitry Andric   static const char *BugMessage =
18475ffd83dbSDimitry Andric       "File position of the stream might be 'indeterminate' "
18485ffd83dbSDimitry Andric       "after a failed operation. "
18495ffd83dbSDimitry Andric       "Can cause undefined behavior.";
18505ffd83dbSDimitry Andric 
18515ffd83dbSDimitry Andric   SymbolRef Sym = StreamVal.getAsSymbol();
18525ffd83dbSDimitry Andric   if (!Sym)
18535ffd83dbSDimitry Andric     return State;
18545ffd83dbSDimitry Andric 
18555ffd83dbSDimitry Andric   const StreamState *SS = State->get<StreamMap>(Sym);
18565ffd83dbSDimitry Andric   if (!SS)
18575ffd83dbSDimitry Andric     return State;
18585ffd83dbSDimitry Andric 
18595ffd83dbSDimitry Andric   assert(SS->isOpened() && "First ensure that stream is opened.");
18605ffd83dbSDimitry Andric 
18615ffd83dbSDimitry Andric   if (SS->FilePositionIndeterminate) {
18625ffd83dbSDimitry Andric     if (SS->ErrorState & ErrorFEof) {
18635ffd83dbSDimitry Andric       // The error is unknown but may be FEOF.
18645ffd83dbSDimitry Andric       // Continue analysis with the FEOF error state.
18655ffd83dbSDimitry Andric       // Report warning because the other possible error states.
18665ffd83dbSDimitry Andric       ExplodedNode *N = C.generateNonFatalErrorNode(State);
18675ffd83dbSDimitry Andric       if (!N)
18685ffd83dbSDimitry Andric         return nullptr;
18695ffd83dbSDimitry Andric 
1870*0fca6ea1SDimitry Andric       auto R = std::make_unique<PathSensitiveBugReport>(
1871*0fca6ea1SDimitry Andric           BT_IndeterminatePosition, BugMessage, N);
1872*0fca6ea1SDimitry Andric       R->markInteresting(Sym);
1873*0fca6ea1SDimitry Andric       C.emitReport(std::move(R));
18745ffd83dbSDimitry Andric       return State->set<StreamMap>(
18755ffd83dbSDimitry Andric           Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));
18765ffd83dbSDimitry Andric     }
18775ffd83dbSDimitry Andric 
18785ffd83dbSDimitry Andric     // Known or unknown error state without FEOF possible.
18795ffd83dbSDimitry Andric     // Stop analysis, report error.
1880*0fca6ea1SDimitry Andric     if (ExplodedNode *N = C.generateErrorNode(State)) {
1881*0fca6ea1SDimitry Andric       auto R = std::make_unique<PathSensitiveBugReport>(
1882*0fca6ea1SDimitry Andric           BT_IndeterminatePosition, BugMessage, N);
1883*0fca6ea1SDimitry Andric       R->markInteresting(Sym);
1884*0fca6ea1SDimitry Andric       C.emitReport(std::move(R));
1885*0fca6ea1SDimitry Andric     }
18865ffd83dbSDimitry Andric 
18875ffd83dbSDimitry Andric     return nullptr;
18885ffd83dbSDimitry Andric   }
18895ffd83dbSDimitry Andric 
18905ffd83dbSDimitry Andric   return State;
18915ffd83dbSDimitry Andric }
18925ffd83dbSDimitry Andric 
18935ffd83dbSDimitry Andric ProgramStateRef
ensureFseekWhenceCorrect(SVal WhenceVal,CheckerContext & C,ProgramStateRef State) const18945ffd83dbSDimitry Andric StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
18955ffd83dbSDimitry Andric                                         ProgramStateRef State) const {
1896bdd1243dSDimitry Andric   std::optional<nonloc::ConcreteInt> CI =
1897bdd1243dSDimitry Andric       WhenceVal.getAs<nonloc::ConcreteInt>();
18980b57cec5SDimitry Andric   if (!CI)
18995ffd83dbSDimitry Andric     return State;
19000b57cec5SDimitry Andric 
1901480093f4SDimitry Andric   int64_t X = CI->getValue().getSExtValue();
190206c3fb27SDimitry Andric   if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal)
19035ffd83dbSDimitry Andric     return State;
19040b57cec5SDimitry Andric 
1905480093f4SDimitry Andric   if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
19065ffd83dbSDimitry Andric     C.emitReport(std::make_unique<PathSensitiveBugReport>(
19075ffd83dbSDimitry Andric         BT_IllegalWhence,
19080b57cec5SDimitry Andric         "The whence argument to fseek() should be "
19095ffd83dbSDimitry Andric         "SEEK_SET, SEEK_END, or SEEK_CUR.",
19105ffd83dbSDimitry Andric         N));
19115ffd83dbSDimitry Andric     return nullptr;
19125ffd83dbSDimitry Andric   }
19135ffd83dbSDimitry Andric 
19145ffd83dbSDimitry Andric   return State;
19155ffd83dbSDimitry Andric }
19165ffd83dbSDimitry Andric 
reportFEofWarning(SymbolRef StreamSym,CheckerContext & C,ProgramStateRef State) const1917fe6060f1SDimitry Andric void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
19185ffd83dbSDimitry Andric                                       ProgramStateRef State) const {
19195ffd83dbSDimitry Andric   if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1920fe6060f1SDimitry Andric     auto R = std::make_unique<PathSensitiveBugReport>(
19215ffd83dbSDimitry Andric         BT_StreamEof,
19225ffd83dbSDimitry Andric         "Read function called when stream is in EOF state. "
19235ffd83dbSDimitry Andric         "Function has no effect.",
1924fe6060f1SDimitry Andric         N);
1925fe6060f1SDimitry Andric     R->markInteresting(StreamSym);
1926fe6060f1SDimitry Andric     C.emitReport(std::move(R));
19275ffd83dbSDimitry Andric     return;
19280b57cec5SDimitry Andric   }
19295ffd83dbSDimitry Andric   C.addTransition(State);
19300b57cec5SDimitry Andric }
19310b57cec5SDimitry Andric 
1932e8d8bef9SDimitry Andric ExplodedNode *
reportLeaks(const SmallVector<SymbolRef,2> & LeakedSyms,CheckerContext & C,ExplodedNode * Pred) const1933e8d8bef9SDimitry Andric StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
1934e8d8bef9SDimitry Andric                            CheckerContext &C, ExplodedNode *Pred) const {
1935e8d8bef9SDimitry Andric   ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);
1936e8d8bef9SDimitry Andric   if (!Err)
1937e8d8bef9SDimitry Andric     return Pred;
19380b57cec5SDimitry Andric 
1939e8d8bef9SDimitry Andric   for (SymbolRef LeakSym : LeakedSyms) {
19405ffd83dbSDimitry Andric     // Resource leaks can result in multiple warning that describe the same kind
19415ffd83dbSDimitry Andric     // of programming error:
19425ffd83dbSDimitry Andric     //  void f() {
19435ffd83dbSDimitry Andric     //    FILE *F = fopen("a.txt");
19445ffd83dbSDimitry Andric     //    if (rand()) // state split
19455ffd83dbSDimitry Andric     //      return; // warning
19465ffd83dbSDimitry Andric     //  } // warning
19475ffd83dbSDimitry Andric     // While this isn't necessarily true (leaking the same stream could result
19485ffd83dbSDimitry Andric     // from a different kinds of errors), the reduction in redundant reports
19495ffd83dbSDimitry Andric     // makes this a worthwhile heuristic.
19505ffd83dbSDimitry Andric     // FIXME: Add a checker option to turn this uniqueing feature off.
1951e8d8bef9SDimitry Andric     const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
19525ffd83dbSDimitry Andric     assert(StreamOpenNode && "Could not find place of stream opening.");
195306c3fb27SDimitry Andric 
195406c3fb27SDimitry Andric     PathDiagnosticLocation LocUsedForUniqueing;
195506c3fb27SDimitry Andric     if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics())
195606c3fb27SDimitry Andric       LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
195706c3fb27SDimitry Andric           StreamStmt, C.getSourceManager(),
19585ffd83dbSDimitry Andric           StreamOpenNode->getLocationContext());
19595ffd83dbSDimitry Andric 
19605ffd83dbSDimitry Andric     std::unique_ptr<PathSensitiveBugReport> R =
19615ffd83dbSDimitry Andric         std::make_unique<PathSensitiveBugReport>(
19625ffd83dbSDimitry Andric             BT_ResourceLeak,
1963e8d8bef9SDimitry Andric             "Opened stream never closed. Potential resource leak.", Err,
19645ffd83dbSDimitry Andric             LocUsedForUniqueing,
19655ffd83dbSDimitry Andric             StreamOpenNode->getLocationContext()->getDecl());
1966e8d8bef9SDimitry Andric     R->markInteresting(LeakSym);
1967*0fca6ea1SDimitry Andric     R->addVisitor<NoStreamStateChangeVisitor>(LeakSym, this);
19685ffd83dbSDimitry Andric     C.emitReport(std::move(R));
19690b57cec5SDimitry Andric   }
1970e8d8bef9SDimitry Andric 
1971e8d8bef9SDimitry Andric   return Err;
1972e8d8bef9SDimitry Andric }
1973e8d8bef9SDimitry Andric 
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const1974e8d8bef9SDimitry Andric void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1975e8d8bef9SDimitry Andric                                      CheckerContext &C) const {
1976e8d8bef9SDimitry Andric   ProgramStateRef State = C.getState();
1977e8d8bef9SDimitry Andric 
1978e8d8bef9SDimitry Andric   llvm::SmallVector<SymbolRef, 2> LeakedSyms;
1979e8d8bef9SDimitry Andric 
1980e8d8bef9SDimitry Andric   const StreamMapTy &Map = State->get<StreamMap>();
1981e8d8bef9SDimitry Andric   for (const auto &I : Map) {
1982e8d8bef9SDimitry Andric     SymbolRef Sym = I.first;
1983e8d8bef9SDimitry Andric     const StreamState &SS = I.second;
1984e8d8bef9SDimitry Andric     if (!SymReaper.isDead(Sym))
1985e8d8bef9SDimitry Andric       continue;
1986e8d8bef9SDimitry Andric     if (SS.isOpened())
1987e8d8bef9SDimitry Andric       LeakedSyms.push_back(Sym);
1988e8d8bef9SDimitry Andric     State = State->remove<StreamMap>(Sym);
1989e8d8bef9SDimitry Andric   }
1990e8d8bef9SDimitry Andric 
1991e8d8bef9SDimitry Andric   ExplodedNode *N = C.getPredecessor();
1992e8d8bef9SDimitry Andric   if (!LeakedSyms.empty())
1993e8d8bef9SDimitry Andric     N = reportLeaks(LeakedSyms, C, N);
1994e8d8bef9SDimitry Andric 
1995e8d8bef9SDimitry Andric   C.addTransition(State, N);
19960b57cec5SDimitry Andric }
19970b57cec5SDimitry Andric 
checkPointerEscape(ProgramStateRef State,const InvalidatedSymbols & Escaped,const CallEvent * Call,PointerEscapeKind Kind) const19985ffd83dbSDimitry Andric ProgramStateRef StreamChecker::checkPointerEscape(
19995ffd83dbSDimitry Andric     ProgramStateRef State, const InvalidatedSymbols &Escaped,
20005ffd83dbSDimitry Andric     const CallEvent *Call, PointerEscapeKind Kind) const {
20015ffd83dbSDimitry Andric   // Check for file-handling system call that is not handled by the checker.
20025ffd83dbSDimitry Andric   // FIXME: The checker should be updated to handle all system calls that take
20035ffd83dbSDimitry Andric   // 'FILE*' argument. These are now ignored.
20045ffd83dbSDimitry Andric   if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader())
20055ffd83dbSDimitry Andric     return State;
20065ffd83dbSDimitry Andric 
20075ffd83dbSDimitry Andric   for (SymbolRef Sym : Escaped) {
20085ffd83dbSDimitry Andric     // The symbol escaped.
20095ffd83dbSDimitry Andric     // From now the stream can be manipulated in unknown way to the checker,
20105ffd83dbSDimitry Andric     // it is not possible to handle it any more.
20115ffd83dbSDimitry Andric     // Optimistically, assume that the corresponding file handle will be closed
20125ffd83dbSDimitry Andric     // somewhere else.
20135ffd83dbSDimitry Andric     // Remove symbol from state so the following stream calls on this symbol are
20145ffd83dbSDimitry Andric     // not handled by the checker.
20155ffd83dbSDimitry Andric     State = State->remove<StreamMap>(Sym);
20165ffd83dbSDimitry Andric   }
20175ffd83dbSDimitry Andric   return State;
20180b57cec5SDimitry Andric }
20190b57cec5SDimitry Andric 
2020fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
2021fe6060f1SDimitry Andric // Checker registration.
2022fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
2023fe6060f1SDimitry Andric 
registerStreamChecker(CheckerManager & Mgr)20245ffd83dbSDimitry Andric void ento::registerStreamChecker(CheckerManager &Mgr) {
2025*0fca6ea1SDimitry Andric   auto *Checker = Mgr.registerChecker<StreamChecker>();
2026*0fca6ea1SDimitry Andric   Checker->PedanticMode =
2027*0fca6ea1SDimitry Andric       Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "Pedantic");
20285ffd83dbSDimitry Andric }
20295ffd83dbSDimitry Andric 
shouldRegisterStreamChecker(const CheckerManager & Mgr)20305ffd83dbSDimitry Andric bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
20315ffd83dbSDimitry Andric   return true;
20325ffd83dbSDimitry Andric }
20335ffd83dbSDimitry Andric 
registerStreamTesterChecker(CheckerManager & Mgr)20345ffd83dbSDimitry Andric void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
20355ffd83dbSDimitry Andric   auto *Checker = Mgr.getChecker<StreamChecker>();
20365ffd83dbSDimitry Andric   Checker->TestMode = true;
20375ffd83dbSDimitry Andric }
20385ffd83dbSDimitry Andric 
shouldRegisterStreamTesterChecker(const CheckerManager & Mgr)20395ffd83dbSDimitry Andric bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
20400b57cec5SDimitry Andric   return true;
20410b57cec5SDimitry Andric }
2042