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