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 = { 2415f757f3fSDimitry Andric {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 2421db9f3b2SDimitry Andric {{{"fdopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 243bdd1243dSDimitry Andric {{{"freopen"}, 3}, 2445ffd83dbSDimitry Andric {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, 2455f757f3fSDimitry Andric {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 246bdd1243dSDimitry Andric {{{"fclose"}, 1}, 2475ffd83dbSDimitry Andric {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, 248bdd1243dSDimitry Andric {{{"fread"}, 4}, 2495f757f3fSDimitry Andric {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), 2505ffd83dbSDimitry Andric std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, 251bdd1243dSDimitry Andric {{{"fwrite"}, 4}, 2525f757f3fSDimitry Andric {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), 2535ffd83dbSDimitry Andric std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, 2545f757f3fSDimitry Andric {{{"fgetc"}, 1}, 2555f757f3fSDimitry Andric {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), 2565f757f3fSDimitry Andric std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}}, 2575f757f3fSDimitry Andric {{{"fgets"}, 3}, 2585f757f3fSDimitry Andric {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), 2595f757f3fSDimitry Andric std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}}, 2605f757f3fSDimitry Andric {{{"fputc"}, 2}, 2615f757f3fSDimitry Andric {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), 2625f757f3fSDimitry Andric std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}}, 2635f757f3fSDimitry Andric {{{"fputs"}, 2}, 2645f757f3fSDimitry Andric {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), 2655f757f3fSDimitry Andric std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}}, 266*7a6dacacSDimitry Andric {{{"fprintf"}}, 267*7a6dacacSDimitry Andric {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), 268*7a6dacacSDimitry Andric std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}}, 269*7a6dacacSDimitry Andric {{{"fscanf"}}, 270*7a6dacacSDimitry Andric {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), 271*7a6dacacSDimitry Andric std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}}, 272297eecfbSDimitry Andric {{{"ungetc"}, 2}, 273297eecfbSDimitry Andric {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), 274297eecfbSDimitry Andric std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}}, 275*7a6dacacSDimitry Andric {{{"getdelim"}, 4}, 276*7a6dacacSDimitry Andric {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), 277*7a6dacacSDimitry Andric std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 3}}, 278*7a6dacacSDimitry Andric {{{"getline"}, 3}, 279*7a6dacacSDimitry Andric {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), 280*7a6dacacSDimitry Andric std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 2}}, 281bdd1243dSDimitry Andric {{{"fseek"}, 3}, 282bdd1243dSDimitry Andric {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 283297eecfbSDimitry Andric {{{"fseeko"}, 3}, 284297eecfbSDimitry Andric {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 285bdd1243dSDimitry Andric {{{"ftell"}, 1}, 286bdd1243dSDimitry Andric {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}}, 287297eecfbSDimitry Andric {{{"ftello"}, 1}, 288297eecfbSDimitry Andric {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}}, 289cb14a3feSDimitry Andric {{{"fflush"}, 1}, 290cb14a3feSDimitry Andric {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}}, 291bdd1243dSDimitry Andric {{{"rewind"}, 1}, 292bdd1243dSDimitry Andric {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}}, 293bdd1243dSDimitry Andric {{{"fgetpos"}, 2}, 294bdd1243dSDimitry Andric {&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}}, 295bdd1243dSDimitry Andric {{{"fsetpos"}, 2}, 296bdd1243dSDimitry Andric {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}}, 297bdd1243dSDimitry Andric {{{"clearerr"}, 1}, 2985ffd83dbSDimitry Andric {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, 299bdd1243dSDimitry Andric {{{"feof"}, 1}, 3005ffd83dbSDimitry Andric {&StreamChecker::preDefault, 3015ffd83dbSDimitry Andric std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), 3025ffd83dbSDimitry Andric 0}}, 303bdd1243dSDimitry Andric {{{"ferror"}, 1}, 3045ffd83dbSDimitry Andric {&StreamChecker::preDefault, 3055ffd83dbSDimitry Andric std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError), 3065ffd83dbSDimitry Andric 0}}, 307bdd1243dSDimitry Andric {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}}, 3085ffd83dbSDimitry Andric }; 3095ffd83dbSDimitry Andric 3105ffd83dbSDimitry Andric CallDescriptionMap<FnDescription> FnTestDescriptions = { 311bdd1243dSDimitry Andric {{{"StreamTesterChecker_make_feof_stream"}, 1}, 3125ffd83dbSDimitry Andric {nullptr, 3135ffd83dbSDimitry Andric std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof), 3145ffd83dbSDimitry Andric 0}}, 315bdd1243dSDimitry Andric {{{"StreamTesterChecker_make_ferror_stream"}, 1}, 3165ffd83dbSDimitry Andric {nullptr, 3175ffd83dbSDimitry Andric std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, 3185ffd83dbSDimitry Andric ErrorFError), 3195ffd83dbSDimitry Andric 0}}, 3205ffd83dbSDimitry Andric }; 3215ffd83dbSDimitry Andric 32206c3fb27SDimitry Andric /// Expanded value of EOF, empty before initialization. 323bdd1243dSDimitry Andric mutable std::optional<int> EofVal; 32406c3fb27SDimitry Andric /// Expanded value of SEEK_SET, 0 if not found. 32506c3fb27SDimitry Andric mutable int SeekSetVal = 0; 32606c3fb27SDimitry Andric /// Expanded value of SEEK_CUR, 1 if not found. 32706c3fb27SDimitry Andric mutable int SeekCurVal = 1; 32806c3fb27SDimitry Andric /// Expanded value of SEEK_END, 2 if not found. 32906c3fb27SDimitry Andric mutable int SeekEndVal = 2; 330bdd1243dSDimitry Andric 3315ffd83dbSDimitry Andric void evalFopen(const FnDescription *Desc, const CallEvent &Call, 3325ffd83dbSDimitry Andric CheckerContext &C) const; 3335ffd83dbSDimitry Andric 3345ffd83dbSDimitry Andric void preFreopen(const FnDescription *Desc, const CallEvent &Call, 3355ffd83dbSDimitry Andric CheckerContext &C) const; 3365ffd83dbSDimitry Andric void evalFreopen(const FnDescription *Desc, const CallEvent &Call, 3375ffd83dbSDimitry Andric CheckerContext &C) const; 3385ffd83dbSDimitry Andric 3395ffd83dbSDimitry Andric void evalFclose(const FnDescription *Desc, const CallEvent &Call, 3405ffd83dbSDimitry Andric CheckerContext &C) const; 3415ffd83dbSDimitry Andric 3425f757f3fSDimitry Andric void preReadWrite(const FnDescription *Desc, const CallEvent &Call, 3435f757f3fSDimitry Andric CheckerContext &C, bool IsRead) const; 3445ffd83dbSDimitry Andric 3455ffd83dbSDimitry Andric void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, 3465ffd83dbSDimitry Andric CheckerContext &C, bool IsFread) const; 3475ffd83dbSDimitry Andric 3485f757f3fSDimitry Andric void evalFgetx(const FnDescription *Desc, const CallEvent &Call, 3495f757f3fSDimitry Andric CheckerContext &C, bool SingleChar) const; 3505f757f3fSDimitry Andric 3515f757f3fSDimitry Andric void evalFputx(const FnDescription *Desc, const CallEvent &Call, 3525f757f3fSDimitry Andric CheckerContext &C, bool IsSingleChar) const; 3535f757f3fSDimitry Andric 354*7a6dacacSDimitry Andric void evalFprintf(const FnDescription *Desc, const CallEvent &Call, 355*7a6dacacSDimitry Andric CheckerContext &C) const; 356*7a6dacacSDimitry Andric 357*7a6dacacSDimitry Andric void evalFscanf(const FnDescription *Desc, const CallEvent &Call, 358*7a6dacacSDimitry Andric CheckerContext &C) const; 359*7a6dacacSDimitry Andric 360297eecfbSDimitry Andric void evalUngetc(const FnDescription *Desc, const CallEvent &Call, 361297eecfbSDimitry Andric CheckerContext &C) const; 362297eecfbSDimitry Andric 363*7a6dacacSDimitry Andric void evalGetdelim(const FnDescription *Desc, const CallEvent &Call, 364*7a6dacacSDimitry Andric CheckerContext &C) const; 365*7a6dacacSDimitry Andric 3665ffd83dbSDimitry Andric void preFseek(const FnDescription *Desc, const CallEvent &Call, 3675ffd83dbSDimitry Andric CheckerContext &C) const; 3685ffd83dbSDimitry Andric void evalFseek(const FnDescription *Desc, const CallEvent &Call, 3695ffd83dbSDimitry Andric CheckerContext &C) const; 3705ffd83dbSDimitry Andric 371bdd1243dSDimitry Andric void evalFgetpos(const FnDescription *Desc, const CallEvent &Call, 372bdd1243dSDimitry Andric CheckerContext &C) const; 373bdd1243dSDimitry Andric 374bdd1243dSDimitry Andric void evalFsetpos(const FnDescription *Desc, const CallEvent &Call, 375bdd1243dSDimitry Andric CheckerContext &C) const; 376bdd1243dSDimitry Andric 377bdd1243dSDimitry Andric void evalFtell(const FnDescription *Desc, const CallEvent &Call, 378bdd1243dSDimitry Andric CheckerContext &C) const; 379bdd1243dSDimitry Andric 380bdd1243dSDimitry Andric void evalRewind(const FnDescription *Desc, const CallEvent &Call, 381bdd1243dSDimitry Andric CheckerContext &C) const; 382bdd1243dSDimitry Andric 3835ffd83dbSDimitry Andric void preDefault(const FnDescription *Desc, const CallEvent &Call, 3845ffd83dbSDimitry Andric CheckerContext &C) const; 3855ffd83dbSDimitry Andric 3865ffd83dbSDimitry Andric void evalClearerr(const FnDescription *Desc, const CallEvent &Call, 3875ffd83dbSDimitry Andric CheckerContext &C) const; 3885ffd83dbSDimitry Andric 3895ffd83dbSDimitry Andric void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call, 3905ffd83dbSDimitry Andric CheckerContext &C, 3915ffd83dbSDimitry Andric const StreamErrorState &ErrorKind) const; 3925ffd83dbSDimitry Andric 3935ffd83dbSDimitry Andric void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call, 3945ffd83dbSDimitry Andric CheckerContext &C, 3955ffd83dbSDimitry Andric const StreamErrorState &ErrorKind) const; 3965ffd83dbSDimitry Andric 397cb14a3feSDimitry Andric void preFflush(const FnDescription *Desc, const CallEvent &Call, 398cb14a3feSDimitry Andric CheckerContext &C) const; 399cb14a3feSDimitry Andric 400cb14a3feSDimitry Andric void evalFflush(const FnDescription *Desc, const CallEvent &Call, 401cb14a3feSDimitry Andric CheckerContext &C) const; 402cb14a3feSDimitry Andric 4035ffd83dbSDimitry Andric /// Check that the stream (in StreamVal) is not NULL. 40406c3fb27SDimitry Andric /// If it can only be NULL a fatal error is emitted and nullptr returned. 4055ffd83dbSDimitry Andric /// Otherwise the return value is a new state where the stream is constrained 4065ffd83dbSDimitry Andric /// to be non-null. 407fe6060f1SDimitry Andric ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, 408fe6060f1SDimitry Andric CheckerContext &C, 4095ffd83dbSDimitry Andric ProgramStateRef State) const; 4105ffd83dbSDimitry Andric 4115ffd83dbSDimitry Andric /// Check that the stream is the opened state. 4125ffd83dbSDimitry Andric /// If the stream is known to be not opened an error is generated 4135ffd83dbSDimitry Andric /// and nullptr returned, otherwise the original state is returned. 4145ffd83dbSDimitry Andric ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C, 4155ffd83dbSDimitry Andric ProgramStateRef State) const; 4165ffd83dbSDimitry Andric 4175ffd83dbSDimitry Andric /// Check that the stream has not an invalid ("indeterminate") file position, 4185ffd83dbSDimitry Andric /// generate warning for it. 4195ffd83dbSDimitry Andric /// (EOF is not an invalid position.) 4205ffd83dbSDimitry Andric /// The returned state can be nullptr if a fatal error was generated. 4215ffd83dbSDimitry Andric /// It can return non-null state if the stream has not an invalid position or 4225ffd83dbSDimitry Andric /// there is execution path with non-invalid position. 4235ffd83dbSDimitry Andric ProgramStateRef 4245ffd83dbSDimitry Andric ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C, 4255ffd83dbSDimitry Andric ProgramStateRef State) const; 4265ffd83dbSDimitry Andric 4275ffd83dbSDimitry Andric /// Check the legality of the 'whence' argument of 'fseek'. 4285ffd83dbSDimitry Andric /// Generate error and return nullptr if it is found to be illegal. 4295ffd83dbSDimitry Andric /// Otherwise returns the state. 4305ffd83dbSDimitry Andric /// (State is not changed here because the "whence" value is already known.) 4315ffd83dbSDimitry Andric ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 4325ffd83dbSDimitry Andric ProgramStateRef State) const; 4335ffd83dbSDimitry Andric 4345ffd83dbSDimitry Andric /// Generate warning about stream in EOF state. 4355ffd83dbSDimitry Andric /// There will be always a state transition into the passed State, 4365ffd83dbSDimitry Andric /// by the new non-fatal error node or (if failed) a normal transition, 4375ffd83dbSDimitry Andric /// to ensure uniform handling. 438fe6060f1SDimitry Andric void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C, 439fe6060f1SDimitry Andric ProgramStateRef State) const; 4405ffd83dbSDimitry Andric 441e8d8bef9SDimitry Andric /// Emit resource leak warnings for the given symbols. 442e8d8bef9SDimitry Andric /// Createn a non-fatal error node for these, and returns it (if any warnings 443e8d8bef9SDimitry Andric /// were generated). Return value is non-null. 444e8d8bef9SDimitry Andric ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 445e8d8bef9SDimitry Andric CheckerContext &C, ExplodedNode *Pred) const; 446e8d8bef9SDimitry Andric 4475ffd83dbSDimitry Andric /// Find the description data of the function called by a call event. 4485ffd83dbSDimitry Andric /// Returns nullptr if no function is recognized. 4495ffd83dbSDimitry Andric const FnDescription *lookupFn(const CallEvent &Call) const { 4505ffd83dbSDimitry Andric // Recognize "global C functions" with only integral or pointer arguments 4515ffd83dbSDimitry Andric // (and matching name) as stream functions. 4525ffd83dbSDimitry Andric if (!Call.isGlobalCFunction()) 4535ffd83dbSDimitry Andric return nullptr; 454bdd1243dSDimitry Andric for (auto *P : Call.parameters()) { 4555ffd83dbSDimitry Andric QualType T = P->getType(); 4565ffd83dbSDimitry Andric if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) 4575ffd83dbSDimitry Andric return nullptr; 4585ffd83dbSDimitry Andric } 4595ffd83dbSDimitry Andric 4605ffd83dbSDimitry Andric return FnDescriptions.lookup(Call); 4615ffd83dbSDimitry Andric } 4625ffd83dbSDimitry Andric 4635ffd83dbSDimitry Andric /// Generate a message for BugReporterVisitor if the stored symbol is 4645ffd83dbSDimitry Andric /// marked as interesting by the actual bug report. 4655ffd83dbSDimitry Andric const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym, 4665ffd83dbSDimitry Andric const std::string &Message) const { 4675f757f3fSDimitry Andric return C.getNoteTag([this, StreamSym, 4685f757f3fSDimitry Andric Message](PathSensitiveBugReport &BR) -> std::string { 4695f757f3fSDimitry Andric if (BR.isInteresting(StreamSym) && &BR.getBugType() == &BT_ResourceLeak) 4705f757f3fSDimitry Andric return Message; 4715f757f3fSDimitry Andric return ""; 4725f757f3fSDimitry Andric }); 473fe6060f1SDimitry Andric } 474fe6060f1SDimitry Andric 475fe6060f1SDimitry Andric const NoteTag *constructSetEofNoteTag(CheckerContext &C, 476fe6060f1SDimitry Andric SymbolRef StreamSym) const { 477fe6060f1SDimitry Andric return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) { 478fe6060f1SDimitry Andric if (!BR.isInteresting(StreamSym) || 479fe6060f1SDimitry Andric &BR.getBugType() != this->getBT_StreamEof()) 480fe6060f1SDimitry Andric return ""; 481fe6060f1SDimitry Andric 482fe6060f1SDimitry Andric BR.markNotInteresting(StreamSym); 483fe6060f1SDimitry Andric 484fe6060f1SDimitry Andric return "Assuming stream reaches end-of-file here"; 485fe6060f1SDimitry Andric }); 4865ffd83dbSDimitry Andric } 4875ffd83dbSDimitry Andric 48806c3fb27SDimitry Andric void initMacroValues(CheckerContext &C) const { 489bdd1243dSDimitry Andric if (EofVal) 490bdd1243dSDimitry Andric return; 491bdd1243dSDimitry Andric 492bdd1243dSDimitry Andric if (const std::optional<int> OptInt = 493bdd1243dSDimitry Andric tryExpandAsInteger("EOF", C.getPreprocessor())) 494bdd1243dSDimitry Andric EofVal = *OptInt; 495bdd1243dSDimitry Andric else 496bdd1243dSDimitry Andric EofVal = -1; 49706c3fb27SDimitry Andric if (const std::optional<int> OptInt = 49806c3fb27SDimitry Andric tryExpandAsInteger("SEEK_SET", C.getPreprocessor())) 49906c3fb27SDimitry Andric SeekSetVal = *OptInt; 50006c3fb27SDimitry Andric if (const std::optional<int> OptInt = 50106c3fb27SDimitry Andric tryExpandAsInteger("SEEK_END", C.getPreprocessor())) 50206c3fb27SDimitry Andric SeekEndVal = *OptInt; 50306c3fb27SDimitry Andric if (const std::optional<int> OptInt = 50406c3fb27SDimitry Andric tryExpandAsInteger("SEEK_CUR", C.getPreprocessor())) 50506c3fb27SDimitry Andric SeekCurVal = *OptInt; 506bdd1243dSDimitry Andric } 507bdd1243dSDimitry Andric 5085ffd83dbSDimitry Andric /// Searches for the ExplodedNode where the file descriptor was acquired for 5095ffd83dbSDimitry Andric /// StreamSym. 5105ffd83dbSDimitry Andric static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, 5115ffd83dbSDimitry Andric SymbolRef StreamSym, 5125ffd83dbSDimitry Andric CheckerContext &C); 5130b57cec5SDimitry Andric }; 5140b57cec5SDimitry Andric 5150b57cec5SDimitry Andric } // end anonymous namespace 5160b57cec5SDimitry Andric 517fe6060f1SDimitry Andric // This map holds the state of a stream. 518fe6060f1SDimitry Andric // The stream is identified with a SymbolRef that is created when a stream 519fe6060f1SDimitry Andric // opening function is modeled by the checker. 5200b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 5210b57cec5SDimitry Andric 5225ffd83dbSDimitry Andric inline void assertStreamStateOpened(const StreamState *SS) { 523bdd1243dSDimitry Andric assert(SS->isOpened() && "Stream is expected to be opened"); 5240b57cec5SDimitry Andric } 5250b57cec5SDimitry Andric 5265ffd83dbSDimitry Andric const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, 5275ffd83dbSDimitry Andric SymbolRef StreamSym, 5285ffd83dbSDimitry Andric CheckerContext &C) { 5295ffd83dbSDimitry Andric ProgramStateRef State = N->getState(); 5305ffd83dbSDimitry Andric // When bug type is resource leak, exploded node N may not have state info 5315ffd83dbSDimitry Andric // for leaked file descriptor, but predecessor should have it. 5325ffd83dbSDimitry Andric if (!State->get<StreamMap>(StreamSym)) 5335ffd83dbSDimitry Andric N = N->getFirstPred(); 5345ffd83dbSDimitry Andric 5355ffd83dbSDimitry Andric const ExplodedNode *Pred = N; 5365ffd83dbSDimitry Andric while (N) { 5375ffd83dbSDimitry Andric State = N->getState(); 5385ffd83dbSDimitry Andric if (!State->get<StreamMap>(StreamSym)) 5395ffd83dbSDimitry Andric return Pred; 5405ffd83dbSDimitry Andric Pred = N; 5415ffd83dbSDimitry Andric N = N->getFirstPred(); 5425ffd83dbSDimitry Andric } 5435ffd83dbSDimitry Andric 5445ffd83dbSDimitry Andric return nullptr; 5455ffd83dbSDimitry Andric } 5465ffd83dbSDimitry Andric 547fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 548fe6060f1SDimitry Andric // Methods of StreamChecker. 549fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 550fe6060f1SDimitry Andric 5515ffd83dbSDimitry Andric void StreamChecker::checkPreCall(const CallEvent &Call, 5525ffd83dbSDimitry Andric CheckerContext &C) const { 55306c3fb27SDimitry Andric initMacroValues(C); 554bdd1243dSDimitry Andric 5555ffd83dbSDimitry Andric const FnDescription *Desc = lookupFn(Call); 5565ffd83dbSDimitry Andric if (!Desc || !Desc->PreFn) 5575ffd83dbSDimitry Andric return; 5585ffd83dbSDimitry Andric 5595ffd83dbSDimitry Andric Desc->PreFn(this, Desc, Call, C); 5605ffd83dbSDimitry Andric } 5615ffd83dbSDimitry Andric 5625ffd83dbSDimitry Andric bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { 5635ffd83dbSDimitry Andric const FnDescription *Desc = lookupFn(Call); 5645ffd83dbSDimitry Andric if (!Desc && TestMode) 5655ffd83dbSDimitry Andric Desc = FnTestDescriptions.lookup(Call); 5665ffd83dbSDimitry Andric if (!Desc || !Desc->EvalFn) 567480093f4SDimitry Andric return false; 568480093f4SDimitry Andric 5695ffd83dbSDimitry Andric Desc->EvalFn(this, Desc, Call, C); 570480093f4SDimitry Andric 571480093f4SDimitry Andric return C.isDifferent(); 5720b57cec5SDimitry Andric } 5730b57cec5SDimitry Andric 5745ffd83dbSDimitry Andric void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call, 5755ffd83dbSDimitry Andric CheckerContext &C) const { 5765ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 5775ffd83dbSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 578480093f4SDimitry Andric if (!CE) 579480093f4SDimitry Andric return; 580480093f4SDimitry Andric 5815ffd83dbSDimitry Andric DefinedSVal RetVal = makeRetVal(C, CE); 5825ffd83dbSDimitry Andric SymbolRef RetSym = RetVal.getAsSymbol(); 5835ffd83dbSDimitry Andric assert(RetSym && "RetVal must be a symbol here."); 5840b57cec5SDimitry Andric 5855ffd83dbSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(), RetVal); 5865ffd83dbSDimitry Andric 5870b57cec5SDimitry Andric // Bifurcate the state into two: one with a valid FILE* pointer, the other 5880b57cec5SDimitry Andric // with a NULL. 5895ffd83dbSDimitry Andric ProgramStateRef StateNotNull, StateNull; 5905ffd83dbSDimitry Andric std::tie(StateNotNull, StateNull) = 5915ffd83dbSDimitry Andric C.getConstraintManager().assumeDual(State, RetVal); 5920b57cec5SDimitry Andric 5935ffd83dbSDimitry Andric StateNotNull = 5945ffd83dbSDimitry Andric StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc)); 5955ffd83dbSDimitry Andric StateNull = 5965ffd83dbSDimitry Andric StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc)); 5970b57cec5SDimitry Andric 5985ffd83dbSDimitry Andric C.addTransition(StateNotNull, 5995ffd83dbSDimitry Andric constructNoteTag(C, RetSym, "Stream opened here")); 6005ffd83dbSDimitry Andric C.addTransition(StateNull); 6010b57cec5SDimitry Andric } 6020b57cec5SDimitry Andric 6035ffd83dbSDimitry Andric void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call, 6045ffd83dbSDimitry Andric CheckerContext &C) const { 6055ffd83dbSDimitry Andric // Do not allow NULL as passed stream pointer but allow a closed stream. 6065ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 607fe6060f1SDimitry Andric State = ensureStreamNonNull(getStreamArg(Desc, Call), 608fe6060f1SDimitry Andric Call.getArgExpr(Desc->StreamArgNo), C, State); 6095ffd83dbSDimitry Andric if (!State) 6105ffd83dbSDimitry Andric return; 6115ffd83dbSDimitry Andric 6125ffd83dbSDimitry Andric C.addTransition(State); 6135ffd83dbSDimitry Andric } 6145ffd83dbSDimitry Andric 6155ffd83dbSDimitry Andric void StreamChecker::evalFreopen(const FnDescription *Desc, 6165ffd83dbSDimitry Andric const CallEvent &Call, 617480093f4SDimitry Andric CheckerContext &C) const { 618480093f4SDimitry Andric ProgramStateRef State = C.getState(); 6190b57cec5SDimitry Andric 620480093f4SDimitry Andric auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 621480093f4SDimitry Andric if (!CE) 6220b57cec5SDimitry Andric return; 623480093f4SDimitry Andric 624bdd1243dSDimitry Andric std::optional<DefinedSVal> StreamVal = 6255ffd83dbSDimitry Andric getStreamArg(Desc, Call).getAs<DefinedSVal>(); 626480093f4SDimitry Andric if (!StreamVal) 627480093f4SDimitry Andric return; 628480093f4SDimitry Andric 629480093f4SDimitry Andric SymbolRef StreamSym = StreamVal->getAsSymbol(); 6305ffd83dbSDimitry Andric // Do not care about concrete values for stream ("(FILE *)0x12345"?). 6315ffd83dbSDimitry Andric // FIXME: Can be stdin, stdout, stderr such values? 632480093f4SDimitry Andric if (!StreamSym) 633480093f4SDimitry Andric return; 634480093f4SDimitry Andric 6355ffd83dbSDimitry Andric // Do not handle untracked stream. It is probably escaped. 6365ffd83dbSDimitry Andric if (!State->get<StreamMap>(StreamSym)) 6375ffd83dbSDimitry Andric return; 6385ffd83dbSDimitry Andric 639480093f4SDimitry Andric // Generate state for non-failed case. 640480093f4SDimitry Andric // Return value is the passed stream pointer. 641480093f4SDimitry Andric // According to the documentations, the stream is closed first 642480093f4SDimitry Andric // but any close error is ignored. The state changes to (or remains) opened. 643480093f4SDimitry Andric ProgramStateRef StateRetNotNull = 644480093f4SDimitry Andric State->BindExpr(CE, C.getLocationContext(), *StreamVal); 645480093f4SDimitry Andric // Generate state for NULL return value. 646480093f4SDimitry Andric // Stream switches to OpenFailed state. 64781ad6265SDimitry Andric ProgramStateRef StateRetNull = 64881ad6265SDimitry Andric State->BindExpr(CE, C.getLocationContext(), 64981ad6265SDimitry Andric C.getSValBuilder().makeNullWithType(CE->getType())); 650480093f4SDimitry Andric 651480093f4SDimitry Andric StateRetNotNull = 6525ffd83dbSDimitry Andric StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 653480093f4SDimitry Andric StateRetNull = 6545ffd83dbSDimitry Andric StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc)); 655480093f4SDimitry Andric 6565ffd83dbSDimitry Andric C.addTransition(StateRetNotNull, 6575ffd83dbSDimitry Andric constructNoteTag(C, StreamSym, "Stream reopened here")); 658480093f4SDimitry Andric C.addTransition(StateRetNull); 6590b57cec5SDimitry Andric } 6600b57cec5SDimitry Andric 6615ffd83dbSDimitry Andric void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, 6625ffd83dbSDimitry Andric CheckerContext &C) const { 663480093f4SDimitry Andric ProgramStateRef State = C.getState(); 6645ffd83dbSDimitry Andric SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); 6655ffd83dbSDimitry Andric if (!Sym) 6665ffd83dbSDimitry Andric return; 6675ffd83dbSDimitry Andric 6685ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(Sym); 6695ffd83dbSDimitry Andric if (!SS) 6705ffd83dbSDimitry Andric return; 6715ffd83dbSDimitry Andric 672bdd1243dSDimitry Andric auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 673bdd1243dSDimitry Andric if (!CE) 674bdd1243dSDimitry Andric return; 675bdd1243dSDimitry Andric 6765ffd83dbSDimitry Andric assertStreamStateOpened(SS); 6775ffd83dbSDimitry Andric 6785ffd83dbSDimitry Andric // Close the File Descriptor. 6795ffd83dbSDimitry Andric // Regardless if the close fails or not, stream becomes "closed" 6805ffd83dbSDimitry Andric // and can not be used any more. 6815ffd83dbSDimitry Andric State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc)); 6825ffd83dbSDimitry Andric 683bdd1243dSDimitry Andric // Return 0 on success, EOF on failure. 684bdd1243dSDimitry Andric SValBuilder &SVB = C.getSValBuilder(); 685bdd1243dSDimitry Andric ProgramStateRef StateSuccess = State->BindExpr( 686bdd1243dSDimitry Andric CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy)); 687bdd1243dSDimitry Andric ProgramStateRef StateFailure = 688bdd1243dSDimitry Andric State->BindExpr(CE, C.getLocationContext(), 689bdd1243dSDimitry Andric SVB.makeIntVal(*EofVal, C.getASTContext().IntTy)); 690bdd1243dSDimitry Andric 691bdd1243dSDimitry Andric C.addTransition(StateSuccess); 692bdd1243dSDimitry Andric C.addTransition(StateFailure); 6930b57cec5SDimitry Andric } 6940b57cec5SDimitry Andric 6955f757f3fSDimitry Andric void StreamChecker::preReadWrite(const FnDescription *Desc, 6965f757f3fSDimitry Andric const CallEvent &Call, CheckerContext &C, 6975f757f3fSDimitry Andric bool IsRead) const { 698480093f4SDimitry Andric ProgramStateRef State = C.getState(); 6995ffd83dbSDimitry Andric SVal StreamVal = getStreamArg(Desc, Call); 700fe6060f1SDimitry Andric State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 701fe6060f1SDimitry Andric State); 7025ffd83dbSDimitry Andric if (!State) 7035ffd83dbSDimitry Andric return; 7045ffd83dbSDimitry Andric State = ensureStreamOpened(StreamVal, C, State); 7055ffd83dbSDimitry Andric if (!State) 7065ffd83dbSDimitry Andric return; 7075ffd83dbSDimitry Andric State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 7085ffd83dbSDimitry Andric if (!State) 709480093f4SDimitry Andric return; 710480093f4SDimitry Andric 7115f757f3fSDimitry Andric if (!IsRead) { 7125f757f3fSDimitry Andric C.addTransition(State); 7135f757f3fSDimitry Andric return; 7145f757f3fSDimitry Andric } 7155f757f3fSDimitry Andric 7165ffd83dbSDimitry Andric SymbolRef Sym = StreamVal.getAsSymbol(); 7175ffd83dbSDimitry Andric if (Sym && State->get<StreamMap>(Sym)) { 7185ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(Sym); 7195ffd83dbSDimitry Andric if (SS->ErrorState & ErrorFEof) 720fe6060f1SDimitry Andric reportFEofWarning(Sym, C, State); 7215ffd83dbSDimitry Andric } else { 722480093f4SDimitry Andric C.addTransition(State); 7235ffd83dbSDimitry Andric } 724480093f4SDimitry Andric } 725480093f4SDimitry Andric 7265ffd83dbSDimitry Andric void StreamChecker::evalFreadFwrite(const FnDescription *Desc, 7275ffd83dbSDimitry Andric const CallEvent &Call, CheckerContext &C, 7285ffd83dbSDimitry Andric bool IsFread) const { 7295ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 7305ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 7315ffd83dbSDimitry Andric if (!StreamSym) 7325ffd83dbSDimitry Andric return; 7335ffd83dbSDimitry Andric 7345ffd83dbSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 7355ffd83dbSDimitry Andric if (!CE) 7365ffd83dbSDimitry Andric return; 7375ffd83dbSDimitry Andric 738bdd1243dSDimitry Andric std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); 7395ffd83dbSDimitry Andric if (!SizeVal) 7405ffd83dbSDimitry Andric return; 741bdd1243dSDimitry Andric std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); 7425ffd83dbSDimitry Andric if (!NMembVal) 7435ffd83dbSDimitry Andric return; 7445ffd83dbSDimitry Andric 745fe6060f1SDimitry Andric const StreamState *OldSS = State->get<StreamMap>(StreamSym); 746fe6060f1SDimitry Andric if (!OldSS) 7475ffd83dbSDimitry Andric return; 7485ffd83dbSDimitry Andric 749fe6060f1SDimitry Andric assertStreamStateOpened(OldSS); 7505ffd83dbSDimitry Andric 7515ffd83dbSDimitry Andric // C'99 standard, §7.19.8.1.3, the return value of fread: 7525ffd83dbSDimitry Andric // The fread function returns the number of elements successfully read, which 7535ffd83dbSDimitry Andric // may be less than nmemb if a read error or end-of-file is encountered. If 7545ffd83dbSDimitry Andric // size or nmemb is zero, fread returns zero and the contents of the array and 7555ffd83dbSDimitry Andric // the state of the stream remain unchanged. 7565ffd83dbSDimitry Andric 7575ffd83dbSDimitry Andric if (State->isNull(*SizeVal).isConstrainedTrue() || 7585ffd83dbSDimitry Andric State->isNull(*NMembVal).isConstrainedTrue()) { 7595ffd83dbSDimitry Andric // This is the "size or nmemb is zero" case. 7605ffd83dbSDimitry Andric // Just return 0, do nothing more (not clear the error flags). 7615ffd83dbSDimitry Andric State = bindInt(0, State, C, CE); 7625ffd83dbSDimitry Andric C.addTransition(State); 7635ffd83dbSDimitry Andric return; 7645ffd83dbSDimitry Andric } 7655ffd83dbSDimitry Andric 7665ffd83dbSDimitry Andric // Generate a transition for the success state. 7675ffd83dbSDimitry Andric // If we know the state to be FEOF at fread, do not add a success state. 768fe6060f1SDimitry Andric if (!IsFread || (OldSS->ErrorState != ErrorFEof)) { 7695ffd83dbSDimitry Andric ProgramStateRef StateNotFailed = 7705ffd83dbSDimitry Andric State->BindExpr(CE, C.getLocationContext(), *NMembVal); 77181ad6265SDimitry Andric StateNotFailed = 77281ad6265SDimitry Andric StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 7735ffd83dbSDimitry Andric C.addTransition(StateNotFailed); 7745ffd83dbSDimitry Andric } 7755ffd83dbSDimitry Andric 7765ffd83dbSDimitry Andric // Add transition for the failed state. 77781ad6265SDimitry Andric NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 7785ffd83dbSDimitry Andric ProgramStateRef StateFailed = 77981ad6265SDimitry Andric State->BindExpr(CE, C.getLocationContext(), RetVal); 7805f757f3fSDimitry Andric SValBuilder &SVB = C.getSValBuilder(); 78181ad6265SDimitry Andric auto Cond = 7825f757f3fSDimitry Andric SVB.evalBinOpNN(State, BO_LT, RetVal, *NMembVal, SVB.getConditionType()) 7835ffd83dbSDimitry Andric .getAs<DefinedOrUnknownSVal>(); 7845ffd83dbSDimitry Andric if (!Cond) 7855ffd83dbSDimitry Andric return; 7865ffd83dbSDimitry Andric StateFailed = StateFailed->assume(*Cond, true); 7875ffd83dbSDimitry Andric if (!StateFailed) 7885ffd83dbSDimitry Andric return; 7895ffd83dbSDimitry Andric 7905ffd83dbSDimitry Andric StreamErrorState NewES; 7915ffd83dbSDimitry Andric if (IsFread) 792fe6060f1SDimitry Andric NewES = 793fe6060f1SDimitry Andric (OldSS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError; 7945ffd83dbSDimitry Andric else 7955ffd83dbSDimitry Andric NewES = ErrorFError; 7965ffd83dbSDimitry Andric // If a (non-EOF) error occurs, the resulting value of the file position 7975ffd83dbSDimitry Andric // indicator for the stream is indeterminate. 798fe6060f1SDimitry Andric StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); 799fe6060f1SDimitry Andric StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS); 800fe6060f1SDimitry Andric if (IsFread && OldSS->ErrorState != ErrorFEof) 801fe6060f1SDimitry Andric C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); 802fe6060f1SDimitry Andric else 8035ffd83dbSDimitry Andric C.addTransition(StateFailed); 8045ffd83dbSDimitry Andric } 8055ffd83dbSDimitry Andric 8065f757f3fSDimitry Andric void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call, 8075f757f3fSDimitry Andric CheckerContext &C, bool SingleChar) const { 8085f757f3fSDimitry Andric ProgramStateRef State = C.getState(); 8095f757f3fSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 8105f757f3fSDimitry Andric if (!StreamSym) 8115f757f3fSDimitry Andric return; 8125f757f3fSDimitry Andric 8135f757f3fSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 8145f757f3fSDimitry Andric if (!CE) 8155f757f3fSDimitry Andric return; 8165f757f3fSDimitry Andric 8175f757f3fSDimitry Andric const StreamState *OldSS = State->get<StreamMap>(StreamSym); 8185f757f3fSDimitry Andric if (!OldSS) 8195f757f3fSDimitry Andric return; 8205f757f3fSDimitry Andric 8215f757f3fSDimitry Andric assertStreamStateOpened(OldSS); 8225f757f3fSDimitry Andric 8235f757f3fSDimitry Andric // `fgetc` returns the read character on success, otherwise returns EOF. 8245f757f3fSDimitry Andric // `fgets` returns the read buffer address on success, otherwise returns NULL. 8255f757f3fSDimitry Andric 8265f757f3fSDimitry Andric if (OldSS->ErrorState != ErrorFEof) { 8275f757f3fSDimitry Andric if (SingleChar) { 8285f757f3fSDimitry Andric // Generate a transition for the success state of `fgetc`. 8295f757f3fSDimitry Andric NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 8305f757f3fSDimitry Andric ProgramStateRef StateNotFailed = 8315f757f3fSDimitry Andric State->BindExpr(CE, C.getLocationContext(), RetVal); 8325f757f3fSDimitry Andric SValBuilder &SVB = C.getSValBuilder(); 8335f757f3fSDimitry Andric ASTContext &ASTC = C.getASTContext(); 8345f757f3fSDimitry Andric // The returned 'unsigned char' of `fgetc` is converted to 'int', 8355f757f3fSDimitry Andric // so we need to check if it is in range [0, 255]. 8365f757f3fSDimitry Andric auto CondLow = 8375f757f3fSDimitry Andric SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), 8385f757f3fSDimitry Andric SVB.getConditionType()) 8395f757f3fSDimitry Andric .getAs<DefinedOrUnknownSVal>(); 8405f757f3fSDimitry Andric auto CondHigh = 8415f757f3fSDimitry Andric SVB.evalBinOp(State, BO_LE, RetVal, 8425f757f3fSDimitry Andric SVB.makeIntVal(SVB.getBasicValueFactory() 8435f757f3fSDimitry Andric .getMaxValue(ASTC.UnsignedCharTy) 8445f757f3fSDimitry Andric .getLimitedValue(), 8455f757f3fSDimitry Andric ASTC.IntTy), 8465f757f3fSDimitry Andric SVB.getConditionType()) 8475f757f3fSDimitry Andric .getAs<DefinedOrUnknownSVal>(); 8485f757f3fSDimitry Andric if (!CondLow || !CondHigh) 8495f757f3fSDimitry Andric return; 8505f757f3fSDimitry Andric StateNotFailed = StateNotFailed->assume(*CondLow, true); 8515f757f3fSDimitry Andric if (!StateNotFailed) 8525f757f3fSDimitry Andric return; 8535f757f3fSDimitry Andric StateNotFailed = StateNotFailed->assume(*CondHigh, true); 8545f757f3fSDimitry Andric if (!StateNotFailed) 8555f757f3fSDimitry Andric return; 8565f757f3fSDimitry Andric C.addTransition(StateNotFailed); 8575f757f3fSDimitry Andric } else { 8585f757f3fSDimitry Andric // Generate a transition for the success state of `fgets`. 8595f757f3fSDimitry Andric std::optional<DefinedSVal> GetBuf = 8605f757f3fSDimitry Andric Call.getArgSVal(0).getAs<DefinedSVal>(); 8615f757f3fSDimitry Andric if (!GetBuf) 8625f757f3fSDimitry Andric return; 8635f757f3fSDimitry Andric ProgramStateRef StateNotFailed = 8645f757f3fSDimitry Andric State->BindExpr(CE, C.getLocationContext(), *GetBuf); 8655f757f3fSDimitry Andric StateNotFailed = StateNotFailed->set<StreamMap>( 8665f757f3fSDimitry Andric StreamSym, StreamState::getOpened(Desc)); 8675f757f3fSDimitry Andric C.addTransition(StateNotFailed); 8685f757f3fSDimitry Andric } 8695f757f3fSDimitry Andric } 8705f757f3fSDimitry Andric 8715f757f3fSDimitry Andric // Add transition for the failed state. 8725f757f3fSDimitry Andric ProgramStateRef StateFailed; 8735f757f3fSDimitry Andric if (SingleChar) 8745f757f3fSDimitry Andric StateFailed = bindInt(*EofVal, State, C, CE); 8755f757f3fSDimitry Andric else 8765f757f3fSDimitry Andric StateFailed = 8775f757f3fSDimitry Andric State->BindExpr(CE, C.getLocationContext(), 8785f757f3fSDimitry Andric C.getSValBuilder().makeNullWithType(CE->getType())); 8795f757f3fSDimitry Andric 8805f757f3fSDimitry Andric // If a (non-EOF) error occurs, the resulting value of the file position 8815f757f3fSDimitry Andric // indicator for the stream is indeterminate. 8825f757f3fSDimitry Andric StreamErrorState NewES = 8835f757f3fSDimitry Andric OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; 8845f757f3fSDimitry Andric StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); 8855f757f3fSDimitry Andric StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS); 8865f757f3fSDimitry Andric if (OldSS->ErrorState != ErrorFEof) 8875f757f3fSDimitry Andric C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); 8885f757f3fSDimitry Andric else 8895f757f3fSDimitry Andric C.addTransition(StateFailed); 8905f757f3fSDimitry Andric } 8915f757f3fSDimitry Andric 8925f757f3fSDimitry Andric void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, 8935f757f3fSDimitry Andric CheckerContext &C, bool IsSingleChar) const { 8945f757f3fSDimitry Andric ProgramStateRef State = C.getState(); 8955f757f3fSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 8965f757f3fSDimitry Andric if (!StreamSym) 8975f757f3fSDimitry Andric return; 8985f757f3fSDimitry Andric 8995f757f3fSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 9005f757f3fSDimitry Andric if (!CE) 9015f757f3fSDimitry Andric return; 9025f757f3fSDimitry Andric 9035f757f3fSDimitry Andric const StreamState *OldSS = State->get<StreamMap>(StreamSym); 9045f757f3fSDimitry Andric if (!OldSS) 9055f757f3fSDimitry Andric return; 9065f757f3fSDimitry Andric 9075f757f3fSDimitry Andric assertStreamStateOpened(OldSS); 9085f757f3fSDimitry Andric 9095f757f3fSDimitry Andric // `fputc` returns the written character on success, otherwise returns EOF. 9105f757f3fSDimitry Andric // `fputs` returns a non negative value on sucecess, otherwise returns EOF. 9115f757f3fSDimitry Andric 9125f757f3fSDimitry Andric if (IsSingleChar) { 9135f757f3fSDimitry Andric // Generate a transition for the success state of `fputc`. 9145f757f3fSDimitry Andric std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>(); 9155f757f3fSDimitry Andric if (!PutVal) 9165f757f3fSDimitry Andric return; 9175f757f3fSDimitry Andric ProgramStateRef StateNotFailed = 9185f757f3fSDimitry Andric State->BindExpr(CE, C.getLocationContext(), *PutVal); 9195f757f3fSDimitry Andric StateNotFailed = 9205f757f3fSDimitry Andric StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 9215f757f3fSDimitry Andric C.addTransition(StateNotFailed); 9225f757f3fSDimitry Andric } else { 9235f757f3fSDimitry Andric // Generate a transition for the success state of `fputs`. 9245f757f3fSDimitry Andric NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 9255f757f3fSDimitry Andric ProgramStateRef StateNotFailed = 9265f757f3fSDimitry Andric State->BindExpr(CE, C.getLocationContext(), RetVal); 9275f757f3fSDimitry Andric SValBuilder &SVB = C.getSValBuilder(); 9285f757f3fSDimitry Andric auto &ASTC = C.getASTContext(); 9295f757f3fSDimitry Andric auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), 9305f757f3fSDimitry Andric SVB.getConditionType()) 9315f757f3fSDimitry Andric .getAs<DefinedOrUnknownSVal>(); 9325f757f3fSDimitry Andric if (!Cond) 9335f757f3fSDimitry Andric return; 9345f757f3fSDimitry Andric StateNotFailed = StateNotFailed->assume(*Cond, true); 9355f757f3fSDimitry Andric if (!StateNotFailed) 9365f757f3fSDimitry Andric return; 9375f757f3fSDimitry Andric StateNotFailed = 9385f757f3fSDimitry Andric StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 9395f757f3fSDimitry Andric C.addTransition(StateNotFailed); 9405f757f3fSDimitry Andric } 9415f757f3fSDimitry Andric 9425f757f3fSDimitry Andric // Add transition for the failed state. The resulting value of the file 9435f757f3fSDimitry Andric // position indicator for the stream is indeterminate. 9445f757f3fSDimitry Andric ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); 9455f757f3fSDimitry Andric StreamState NewSS = StreamState::getOpened(Desc, ErrorFError, true); 9465f757f3fSDimitry Andric StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS); 9475f757f3fSDimitry Andric C.addTransition(StateFailed); 9485f757f3fSDimitry Andric } 9495f757f3fSDimitry Andric 950*7a6dacacSDimitry Andric void StreamChecker::evalFprintf(const FnDescription *Desc, 951*7a6dacacSDimitry Andric const CallEvent &Call, 952*7a6dacacSDimitry Andric CheckerContext &C) const { 953*7a6dacacSDimitry Andric ProgramStateRef State = C.getState(); 954*7a6dacacSDimitry Andric if (Call.getNumArgs() < 2) 955*7a6dacacSDimitry Andric return; 956*7a6dacacSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 957*7a6dacacSDimitry Andric if (!StreamSym) 958*7a6dacacSDimitry Andric return; 959*7a6dacacSDimitry Andric 960*7a6dacacSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 961*7a6dacacSDimitry Andric if (!CE) 962*7a6dacacSDimitry Andric return; 963*7a6dacacSDimitry Andric 964*7a6dacacSDimitry Andric const StreamState *OldSS = State->get<StreamMap>(StreamSym); 965*7a6dacacSDimitry Andric if (!OldSS) 966*7a6dacacSDimitry Andric return; 967*7a6dacacSDimitry Andric 968*7a6dacacSDimitry Andric assertStreamStateOpened(OldSS); 969*7a6dacacSDimitry Andric 970*7a6dacacSDimitry Andric NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 971*7a6dacacSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(), RetVal); 972*7a6dacacSDimitry Andric SValBuilder &SVB = C.getSValBuilder(); 973*7a6dacacSDimitry Andric auto &ACtx = C.getASTContext(); 974*7a6dacacSDimitry Andric auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ACtx.IntTy), 975*7a6dacacSDimitry Andric SVB.getConditionType()) 976*7a6dacacSDimitry Andric .getAs<DefinedOrUnknownSVal>(); 977*7a6dacacSDimitry Andric if (!Cond) 978*7a6dacacSDimitry Andric return; 979*7a6dacacSDimitry Andric ProgramStateRef StateNotFailed, StateFailed; 980*7a6dacacSDimitry Andric std::tie(StateNotFailed, StateFailed) = State->assume(*Cond); 981*7a6dacacSDimitry Andric 982*7a6dacacSDimitry Andric StateNotFailed = 983*7a6dacacSDimitry Andric StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 984*7a6dacacSDimitry Andric C.addTransition(StateNotFailed); 985*7a6dacacSDimitry Andric 986*7a6dacacSDimitry Andric // Add transition for the failed state. The resulting value of the file 987*7a6dacacSDimitry Andric // position indicator for the stream is indeterminate. 988*7a6dacacSDimitry Andric StateFailed = StateFailed->set<StreamMap>( 989*7a6dacacSDimitry Andric StreamSym, StreamState::getOpened(Desc, ErrorFError, true)); 990*7a6dacacSDimitry Andric C.addTransition(StateFailed); 991*7a6dacacSDimitry Andric } 992*7a6dacacSDimitry Andric 993*7a6dacacSDimitry Andric void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call, 994*7a6dacacSDimitry Andric CheckerContext &C) const { 995*7a6dacacSDimitry Andric ProgramStateRef State = C.getState(); 996*7a6dacacSDimitry Andric if (Call.getNumArgs() < 2) 997*7a6dacacSDimitry Andric return; 998*7a6dacacSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 999*7a6dacacSDimitry Andric if (!StreamSym) 1000*7a6dacacSDimitry Andric return; 1001*7a6dacacSDimitry Andric 1002*7a6dacacSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1003*7a6dacacSDimitry Andric if (!CE) 1004*7a6dacacSDimitry Andric return; 1005*7a6dacacSDimitry Andric 1006*7a6dacacSDimitry Andric const StreamState *OldSS = State->get<StreamMap>(StreamSym); 1007*7a6dacacSDimitry Andric if (!OldSS) 1008*7a6dacacSDimitry Andric return; 1009*7a6dacacSDimitry Andric 1010*7a6dacacSDimitry Andric assertStreamStateOpened(OldSS); 1011*7a6dacacSDimitry Andric 1012*7a6dacacSDimitry Andric SValBuilder &SVB = C.getSValBuilder(); 1013*7a6dacacSDimitry Andric ASTContext &ACtx = C.getASTContext(); 1014*7a6dacacSDimitry Andric 1015*7a6dacacSDimitry Andric // Add the success state. 1016*7a6dacacSDimitry Andric // In this context "success" means there is not an EOF or other read error 1017*7a6dacacSDimitry Andric // before any item is matched in 'fscanf'. But there may be match failure, 1018*7a6dacacSDimitry Andric // therefore return value can be 0 or greater. 1019*7a6dacacSDimitry Andric // It is not specified what happens if some items (not all) are matched and 1020*7a6dacacSDimitry Andric // then EOF or read error happens. Now this case is handled like a "success" 1021*7a6dacacSDimitry Andric // case, and no error flags are set on the stream. This is probably not 1022*7a6dacacSDimitry Andric // accurate, and the POSIX documentation does not tell more. 1023*7a6dacacSDimitry Andric if (OldSS->ErrorState != ErrorFEof) { 1024*7a6dacacSDimitry Andric NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 1025*7a6dacacSDimitry Andric ProgramStateRef StateNotFailed = 1026*7a6dacacSDimitry Andric State->BindExpr(CE, C.getLocationContext(), RetVal); 1027*7a6dacacSDimitry Andric auto RetGeZero = 1028*7a6dacacSDimitry Andric SVB.evalBinOp(StateNotFailed, BO_GE, RetVal, 1029*7a6dacacSDimitry Andric SVB.makeZeroVal(ACtx.IntTy), SVB.getConditionType()) 1030*7a6dacacSDimitry Andric .getAs<DefinedOrUnknownSVal>(); 1031*7a6dacacSDimitry Andric if (!RetGeZero) 1032*7a6dacacSDimitry Andric return; 1033*7a6dacacSDimitry Andric StateNotFailed = StateNotFailed->assume(*RetGeZero, true); 1034*7a6dacacSDimitry Andric 1035*7a6dacacSDimitry Andric C.addTransition(StateNotFailed); 1036*7a6dacacSDimitry Andric } 1037*7a6dacacSDimitry Andric 1038*7a6dacacSDimitry Andric // Add transition for the failed state. 1039*7a6dacacSDimitry Andric // Error occurs if nothing is matched yet and reading the input fails. 1040*7a6dacacSDimitry Andric // Error can be EOF, or other error. At "other error" FERROR or 'errno' can 1041*7a6dacacSDimitry Andric // be set but it is not further specified if all are required to be set. 1042*7a6dacacSDimitry Andric // Documentation does not mention, but file position will be set to 1043*7a6dacacSDimitry Andric // indeterminate similarly as at 'fread'. 1044*7a6dacacSDimitry Andric ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); 1045*7a6dacacSDimitry Andric StreamErrorState NewES = (OldSS->ErrorState == ErrorFEof) 1046*7a6dacacSDimitry Andric ? ErrorFEof 1047*7a6dacacSDimitry Andric : ErrorNone | ErrorFEof | ErrorFError; 1048*7a6dacacSDimitry Andric StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); 1049*7a6dacacSDimitry Andric StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS); 1050*7a6dacacSDimitry Andric if (OldSS->ErrorState != ErrorFEof) 1051*7a6dacacSDimitry Andric C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); 1052*7a6dacacSDimitry Andric else 1053*7a6dacacSDimitry Andric C.addTransition(StateFailed); 1054*7a6dacacSDimitry Andric } 1055*7a6dacacSDimitry Andric 1056297eecfbSDimitry Andric void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent &Call, 1057297eecfbSDimitry Andric CheckerContext &C) const { 1058297eecfbSDimitry Andric ProgramStateRef State = C.getState(); 1059297eecfbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 1060297eecfbSDimitry Andric if (!StreamSym) 1061297eecfbSDimitry Andric return; 1062297eecfbSDimitry Andric 1063297eecfbSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1064297eecfbSDimitry Andric if (!CE) 1065297eecfbSDimitry Andric return; 1066297eecfbSDimitry Andric 1067297eecfbSDimitry Andric const StreamState *OldSS = State->get<StreamMap>(StreamSym); 1068297eecfbSDimitry Andric if (!OldSS) 1069297eecfbSDimitry Andric return; 1070297eecfbSDimitry Andric 1071297eecfbSDimitry Andric assertStreamStateOpened(OldSS); 1072297eecfbSDimitry Andric 1073297eecfbSDimitry Andric // Generate a transition for the success state. 1074297eecfbSDimitry Andric std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>(); 1075297eecfbSDimitry Andric if (!PutVal) 1076297eecfbSDimitry Andric return; 1077297eecfbSDimitry Andric ProgramStateRef StateNotFailed = 1078297eecfbSDimitry Andric State->BindExpr(CE, C.getLocationContext(), *PutVal); 1079297eecfbSDimitry Andric StateNotFailed = 1080297eecfbSDimitry Andric StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 1081297eecfbSDimitry Andric C.addTransition(StateNotFailed); 1082297eecfbSDimitry Andric 1083297eecfbSDimitry Andric // Add transition for the failed state. 1084297eecfbSDimitry Andric // Failure of 'ungetc' does not result in feof or ferror state. 1085297eecfbSDimitry Andric // If the PutVal has value of EofVal the function should "fail", but this is 1086297eecfbSDimitry Andric // the same transition as the success state. 1087297eecfbSDimitry Andric // In this case only one state transition is added by the analyzer (the two 1088297eecfbSDimitry Andric // new states may be similar). 1089297eecfbSDimitry Andric ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); 1090297eecfbSDimitry Andric StateFailed = 1091297eecfbSDimitry Andric StateFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 1092297eecfbSDimitry Andric C.addTransition(StateFailed); 1093297eecfbSDimitry Andric } 1094297eecfbSDimitry Andric 1095*7a6dacacSDimitry Andric void StreamChecker::evalGetdelim(const FnDescription *Desc, 1096*7a6dacacSDimitry Andric const CallEvent &Call, 1097*7a6dacacSDimitry Andric CheckerContext &C) const { 1098*7a6dacacSDimitry Andric ProgramStateRef State = C.getState(); 1099*7a6dacacSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 1100*7a6dacacSDimitry Andric if (!StreamSym) 1101*7a6dacacSDimitry Andric return; 1102*7a6dacacSDimitry Andric 1103*7a6dacacSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1104*7a6dacacSDimitry Andric if (!CE) 1105*7a6dacacSDimitry Andric return; 1106*7a6dacacSDimitry Andric 1107*7a6dacacSDimitry Andric const StreamState *OldSS = State->get<StreamMap>(StreamSym); 1108*7a6dacacSDimitry Andric if (!OldSS) 1109*7a6dacacSDimitry Andric return; 1110*7a6dacacSDimitry Andric 1111*7a6dacacSDimitry Andric assertStreamStateOpened(OldSS); 1112*7a6dacacSDimitry Andric 1113*7a6dacacSDimitry Andric // Upon successful completion, the getline() and getdelim() functions shall 1114*7a6dacacSDimitry Andric // return the number of bytes written into the buffer. 1115*7a6dacacSDimitry Andric // If the end-of-file indicator for the stream is set, the function shall 1116*7a6dacacSDimitry Andric // return -1. 1117*7a6dacacSDimitry Andric // If an error occurs, the function shall return -1 and set 'errno'. 1118*7a6dacacSDimitry Andric 1119*7a6dacacSDimitry Andric // Add transition for the successful state. 1120*7a6dacacSDimitry Andric if (OldSS->ErrorState != ErrorFEof) { 1121*7a6dacacSDimitry Andric NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 1122*7a6dacacSDimitry Andric ProgramStateRef StateNotFailed = 1123*7a6dacacSDimitry Andric State->BindExpr(CE, C.getLocationContext(), RetVal); 1124*7a6dacacSDimitry Andric SValBuilder &SVB = C.getSValBuilder(); 1125*7a6dacacSDimitry Andric auto Cond = 1126*7a6dacacSDimitry Andric SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(CE->getType()), 1127*7a6dacacSDimitry Andric SVB.getConditionType()) 1128*7a6dacacSDimitry Andric .getAs<DefinedOrUnknownSVal>(); 1129*7a6dacacSDimitry Andric if (!Cond) 1130*7a6dacacSDimitry Andric return; 1131*7a6dacacSDimitry Andric StateNotFailed = StateNotFailed->assume(*Cond, true); 1132*7a6dacacSDimitry Andric if (!StateNotFailed) 1133*7a6dacacSDimitry Andric return; 1134*7a6dacacSDimitry Andric C.addTransition(StateNotFailed); 1135*7a6dacacSDimitry Andric } 1136*7a6dacacSDimitry Andric 1137*7a6dacacSDimitry Andric // Add transition for the failed state. 1138*7a6dacacSDimitry Andric // If a (non-EOF) error occurs, the resulting value of the file position 1139*7a6dacacSDimitry Andric // indicator for the stream is indeterminate. 1140*7a6dacacSDimitry Andric ProgramStateRef StateFailed = bindInt(-1, State, C, CE); 1141*7a6dacacSDimitry Andric StreamErrorState NewES = 1142*7a6dacacSDimitry Andric OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; 1143*7a6dacacSDimitry Andric StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); 1144*7a6dacacSDimitry Andric StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS); 1145*7a6dacacSDimitry Andric if (OldSS->ErrorState != ErrorFEof) 1146*7a6dacacSDimitry Andric C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); 1147*7a6dacacSDimitry Andric else 1148*7a6dacacSDimitry Andric C.addTransition(StateFailed); 1149*7a6dacacSDimitry Andric } 1150*7a6dacacSDimitry Andric 11515ffd83dbSDimitry Andric void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, 11525ffd83dbSDimitry Andric CheckerContext &C) const { 11535ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 11545ffd83dbSDimitry Andric SVal StreamVal = getStreamArg(Desc, Call); 1155fe6060f1SDimitry Andric State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 1156fe6060f1SDimitry Andric State); 11575ffd83dbSDimitry Andric if (!State) 11585ffd83dbSDimitry Andric return; 11595ffd83dbSDimitry Andric State = ensureStreamOpened(StreamVal, C, State); 11605ffd83dbSDimitry Andric if (!State) 11615ffd83dbSDimitry Andric return; 11625ffd83dbSDimitry Andric State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); 11635ffd83dbSDimitry Andric if (!State) 11645ffd83dbSDimitry Andric return; 11655ffd83dbSDimitry Andric 11665ffd83dbSDimitry Andric C.addTransition(State); 11675ffd83dbSDimitry Andric } 11685ffd83dbSDimitry Andric 11695ffd83dbSDimitry Andric void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, 11705ffd83dbSDimitry Andric CheckerContext &C) const { 11715ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 11725ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 11735ffd83dbSDimitry Andric if (!StreamSym) 11745ffd83dbSDimitry Andric return; 11755ffd83dbSDimitry Andric 11765ffd83dbSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 11775ffd83dbSDimitry Andric if (!CE) 11785ffd83dbSDimitry Andric return; 11795ffd83dbSDimitry Andric 11805ffd83dbSDimitry Andric // Ignore the call if the stream is not tracked. 11815ffd83dbSDimitry Andric if (!State->get<StreamMap>(StreamSym)) 11825ffd83dbSDimitry Andric return; 11835ffd83dbSDimitry Andric 118406c3fb27SDimitry Andric const llvm::APSInt *PosV = 118506c3fb27SDimitry Andric C.getSValBuilder().getKnownValue(State, Call.getArgSVal(1)); 118606c3fb27SDimitry Andric const llvm::APSInt *WhenceV = 118706c3fb27SDimitry Andric C.getSValBuilder().getKnownValue(State, Call.getArgSVal(2)); 118806c3fb27SDimitry Andric 11895ffd83dbSDimitry Andric DefinedSVal RetVal = makeRetVal(C, CE); 11905ffd83dbSDimitry Andric 11915ffd83dbSDimitry Andric // Make expression result. 11925ffd83dbSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(), RetVal); 11935ffd83dbSDimitry Andric 11945ffd83dbSDimitry Andric // Bifurcate the state into failed and non-failed. 11955ffd83dbSDimitry Andric // Return zero on success, nonzero on error. 11965ffd83dbSDimitry Andric ProgramStateRef StateNotFailed, StateFailed; 11975ffd83dbSDimitry Andric std::tie(StateFailed, StateNotFailed) = 11985ffd83dbSDimitry Andric C.getConstraintManager().assumeDual(State, RetVal); 11995ffd83dbSDimitry Andric 12005ffd83dbSDimitry Andric // Reset the state to opened with no error. 12015ffd83dbSDimitry Andric StateNotFailed = 12025ffd83dbSDimitry Andric StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 12035ffd83dbSDimitry Andric // We get error. 12045ffd83dbSDimitry Andric // It is possible that fseek fails but sets none of the error flags. 12055ffd83dbSDimitry Andric // If fseek failed, assume that the file position becomes indeterminate in any 12065ffd83dbSDimitry Andric // case. 120706c3fb27SDimitry Andric StreamErrorState NewErrS = ErrorNone | ErrorFError; 120806c3fb27SDimitry Andric // Setting the position to start of file never produces EOF error. 120906c3fb27SDimitry Andric if (!(PosV && *PosV == 0 && WhenceV && *WhenceV == SeekSetVal)) 121006c3fb27SDimitry Andric NewErrS = NewErrS | ErrorFEof; 12115ffd83dbSDimitry Andric StateFailed = StateFailed->set<StreamMap>( 121206c3fb27SDimitry Andric StreamSym, StreamState::getOpened(Desc, NewErrS, true)); 12135ffd83dbSDimitry Andric 12145ffd83dbSDimitry Andric C.addTransition(StateNotFailed); 1215fe6060f1SDimitry Andric C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); 12165ffd83dbSDimitry Andric } 12175ffd83dbSDimitry Andric 1218bdd1243dSDimitry Andric void StreamChecker::evalFgetpos(const FnDescription *Desc, 1219bdd1243dSDimitry Andric const CallEvent &Call, 1220bdd1243dSDimitry Andric CheckerContext &C) const { 1221bdd1243dSDimitry Andric ProgramStateRef State = C.getState(); 1222bdd1243dSDimitry Andric SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); 1223bdd1243dSDimitry Andric if (!Sym) 1224bdd1243dSDimitry Andric return; 1225bdd1243dSDimitry Andric 1226bdd1243dSDimitry Andric // Do not evaluate if stream is not found. 1227bdd1243dSDimitry Andric if (!State->get<StreamMap>(Sym)) 1228bdd1243dSDimitry Andric return; 1229bdd1243dSDimitry Andric 1230bdd1243dSDimitry Andric auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1231bdd1243dSDimitry Andric if (!CE) 1232bdd1243dSDimitry Andric return; 1233bdd1243dSDimitry Andric 1234bdd1243dSDimitry Andric DefinedSVal RetVal = makeRetVal(C, CE); 1235bdd1243dSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(), RetVal); 1236bdd1243dSDimitry Andric ProgramStateRef StateNotFailed, StateFailed; 1237bdd1243dSDimitry Andric std::tie(StateFailed, StateNotFailed) = 1238bdd1243dSDimitry Andric C.getConstraintManager().assumeDual(State, RetVal); 1239bdd1243dSDimitry Andric 1240bdd1243dSDimitry Andric // This function does not affect the stream state. 1241bdd1243dSDimitry Andric // Still we add success and failure state with the appropriate return value. 1242bdd1243dSDimitry Andric // StdLibraryFunctionsChecker can change these states (set the 'errno' state). 1243bdd1243dSDimitry Andric C.addTransition(StateNotFailed); 1244bdd1243dSDimitry Andric C.addTransition(StateFailed); 1245bdd1243dSDimitry Andric } 1246bdd1243dSDimitry Andric 1247bdd1243dSDimitry Andric void StreamChecker::evalFsetpos(const FnDescription *Desc, 1248bdd1243dSDimitry Andric const CallEvent &Call, 1249bdd1243dSDimitry Andric CheckerContext &C) const { 1250bdd1243dSDimitry Andric ProgramStateRef State = C.getState(); 1251bdd1243dSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 1252bdd1243dSDimitry Andric if (!StreamSym) 1253bdd1243dSDimitry Andric return; 1254bdd1243dSDimitry Andric 1255bdd1243dSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 1256bdd1243dSDimitry Andric if (!SS) 1257bdd1243dSDimitry Andric return; 1258bdd1243dSDimitry Andric 1259bdd1243dSDimitry Andric auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1260bdd1243dSDimitry Andric if (!CE) 1261bdd1243dSDimitry Andric return; 1262bdd1243dSDimitry Andric 1263bdd1243dSDimitry Andric assertStreamStateOpened(SS); 1264bdd1243dSDimitry Andric 1265bdd1243dSDimitry Andric DefinedSVal RetVal = makeRetVal(C, CE); 1266bdd1243dSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(), RetVal); 1267bdd1243dSDimitry Andric ProgramStateRef StateNotFailed, StateFailed; 1268bdd1243dSDimitry Andric std::tie(StateFailed, StateNotFailed) = 1269bdd1243dSDimitry Andric C.getConstraintManager().assumeDual(State, RetVal); 1270bdd1243dSDimitry Andric 1271bdd1243dSDimitry Andric StateNotFailed = StateNotFailed->set<StreamMap>( 1272bdd1243dSDimitry Andric StreamSym, StreamState::getOpened(Desc, ErrorNone, false)); 1273bdd1243dSDimitry Andric 1274bdd1243dSDimitry Andric // At failure ferror could be set. 1275bdd1243dSDimitry Andric // The standards do not tell what happens with the file position at failure. 1276bdd1243dSDimitry Andric // But we can assume that it is dangerous to make a next I/O operation after 1277bdd1243dSDimitry Andric // the position was not set correctly (similar to 'fseek'). 1278bdd1243dSDimitry Andric StateFailed = StateFailed->set<StreamMap>( 1279bdd1243dSDimitry Andric StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true)); 1280bdd1243dSDimitry Andric 1281bdd1243dSDimitry Andric C.addTransition(StateNotFailed); 1282bdd1243dSDimitry Andric C.addTransition(StateFailed); 1283bdd1243dSDimitry Andric } 1284bdd1243dSDimitry Andric 1285bdd1243dSDimitry Andric void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call, 1286bdd1243dSDimitry Andric CheckerContext &C) const { 1287bdd1243dSDimitry Andric ProgramStateRef State = C.getState(); 1288bdd1243dSDimitry Andric SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); 1289bdd1243dSDimitry Andric if (!Sym) 1290bdd1243dSDimitry Andric return; 1291bdd1243dSDimitry Andric 1292bdd1243dSDimitry Andric if (!State->get<StreamMap>(Sym)) 1293bdd1243dSDimitry Andric return; 1294bdd1243dSDimitry Andric 1295bdd1243dSDimitry Andric auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1296bdd1243dSDimitry Andric if (!CE) 1297bdd1243dSDimitry Andric return; 1298bdd1243dSDimitry Andric 1299bdd1243dSDimitry Andric SValBuilder &SVB = C.getSValBuilder(); 1300bdd1243dSDimitry Andric NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 1301bdd1243dSDimitry Andric ProgramStateRef StateNotFailed = 1302bdd1243dSDimitry Andric State->BindExpr(CE, C.getLocationContext(), RetVal); 1303297eecfbSDimitry Andric auto Cond = 1304297eecfbSDimitry Andric SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(Call.getResultType()), 1305bdd1243dSDimitry Andric SVB.getConditionType()) 1306bdd1243dSDimitry Andric .getAs<DefinedOrUnknownSVal>(); 1307bdd1243dSDimitry Andric if (!Cond) 1308bdd1243dSDimitry Andric return; 1309bdd1243dSDimitry Andric StateNotFailed = StateNotFailed->assume(*Cond, true); 1310bdd1243dSDimitry Andric if (!StateNotFailed) 1311bdd1243dSDimitry Andric return; 1312bdd1243dSDimitry Andric 1313bdd1243dSDimitry Andric ProgramStateRef StateFailed = State->BindExpr( 1314297eecfbSDimitry Andric CE, C.getLocationContext(), SVB.makeIntVal(-1, Call.getResultType())); 1315bdd1243dSDimitry Andric 13165f757f3fSDimitry Andric // This function does not affect the stream state. 13175f757f3fSDimitry Andric // Still we add success and failure state with the appropriate return value. 13185f757f3fSDimitry Andric // StdLibraryFunctionsChecker can change these states (set the 'errno' state). 1319bdd1243dSDimitry Andric C.addTransition(StateNotFailed); 1320bdd1243dSDimitry Andric C.addTransition(StateFailed); 1321bdd1243dSDimitry Andric } 1322bdd1243dSDimitry Andric 1323bdd1243dSDimitry Andric void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call, 1324bdd1243dSDimitry Andric CheckerContext &C) const { 1325bdd1243dSDimitry Andric ProgramStateRef State = C.getState(); 1326bdd1243dSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 1327bdd1243dSDimitry Andric if (!StreamSym) 1328bdd1243dSDimitry Andric return; 1329bdd1243dSDimitry Andric 1330bdd1243dSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 1331bdd1243dSDimitry Andric if (!SS) 1332bdd1243dSDimitry Andric return; 1333bdd1243dSDimitry Andric 1334bdd1243dSDimitry Andric auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1335bdd1243dSDimitry Andric if (!CE) 1336bdd1243dSDimitry Andric return; 1337bdd1243dSDimitry Andric 1338bdd1243dSDimitry Andric assertStreamStateOpened(SS); 1339bdd1243dSDimitry Andric 1340bdd1243dSDimitry Andric State = State->set<StreamMap>(StreamSym, 1341bdd1243dSDimitry Andric StreamState::getOpened(Desc, ErrorNone, false)); 1342bdd1243dSDimitry Andric 1343bdd1243dSDimitry Andric C.addTransition(State); 1344bdd1243dSDimitry Andric } 1345bdd1243dSDimitry Andric 13465ffd83dbSDimitry Andric void StreamChecker::evalClearerr(const FnDescription *Desc, 13475ffd83dbSDimitry Andric const CallEvent &Call, 13485ffd83dbSDimitry Andric CheckerContext &C) const { 13495ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 13505ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 13515ffd83dbSDimitry Andric if (!StreamSym) 13525ffd83dbSDimitry Andric return; 13535ffd83dbSDimitry Andric 13545ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 13555ffd83dbSDimitry Andric if (!SS) 13565ffd83dbSDimitry Andric return; 13575ffd83dbSDimitry Andric 13585ffd83dbSDimitry Andric assertStreamStateOpened(SS); 13595ffd83dbSDimitry Andric 13605ffd83dbSDimitry Andric // FilePositionIndeterminate is not cleared. 13615ffd83dbSDimitry Andric State = State->set<StreamMap>( 13625ffd83dbSDimitry Andric StreamSym, 13635ffd83dbSDimitry Andric StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate)); 13645ffd83dbSDimitry Andric C.addTransition(State); 13655ffd83dbSDimitry Andric } 13665ffd83dbSDimitry Andric 13675ffd83dbSDimitry Andric void StreamChecker::evalFeofFerror(const FnDescription *Desc, 13685ffd83dbSDimitry Andric const CallEvent &Call, CheckerContext &C, 13695ffd83dbSDimitry Andric const StreamErrorState &ErrorKind) const { 13705ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 13715ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 13725ffd83dbSDimitry Andric if (!StreamSym) 13735ffd83dbSDimitry Andric return; 13745ffd83dbSDimitry Andric 13755ffd83dbSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 13765ffd83dbSDimitry Andric if (!CE) 13775ffd83dbSDimitry Andric return; 13785ffd83dbSDimitry Andric 13795ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 13805ffd83dbSDimitry Andric if (!SS) 13815ffd83dbSDimitry Andric return; 13825ffd83dbSDimitry Andric 13835ffd83dbSDimitry Andric assertStreamStateOpened(SS); 13845ffd83dbSDimitry Andric 13855ffd83dbSDimitry Andric if (SS->ErrorState & ErrorKind) { 13865ffd83dbSDimitry Andric // Execution path with error of ErrorKind. 13875ffd83dbSDimitry Andric // Function returns true. 13885ffd83dbSDimitry Andric // From now on it is the only one error state. 13895ffd83dbSDimitry Andric ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE); 13905ffd83dbSDimitry Andric C.addTransition(TrueState->set<StreamMap>( 13915ffd83dbSDimitry Andric StreamSym, StreamState::getOpened(Desc, ErrorKind, 13925ffd83dbSDimitry Andric SS->FilePositionIndeterminate && 13935ffd83dbSDimitry Andric !ErrorKind.isFEof()))); 13945ffd83dbSDimitry Andric } 13955ffd83dbSDimitry Andric if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) { 13965ffd83dbSDimitry Andric // Execution path(s) with ErrorKind not set. 13975ffd83dbSDimitry Andric // Function returns false. 13985ffd83dbSDimitry Andric // New error state is everything before minus ErrorKind. 13995ffd83dbSDimitry Andric ProgramStateRef FalseState = bindInt(0, State, C, CE); 14005ffd83dbSDimitry Andric C.addTransition(FalseState->set<StreamMap>( 14015ffd83dbSDimitry Andric StreamSym, 14025ffd83dbSDimitry Andric StreamState::getOpened( 14035ffd83dbSDimitry Andric Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof()))); 14045ffd83dbSDimitry Andric } 14055ffd83dbSDimitry Andric } 14065ffd83dbSDimitry Andric 14075ffd83dbSDimitry Andric void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, 14085ffd83dbSDimitry Andric CheckerContext &C) const { 14095ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 14105ffd83dbSDimitry Andric SVal StreamVal = getStreamArg(Desc, Call); 1411fe6060f1SDimitry Andric State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 1412fe6060f1SDimitry Andric State); 14135ffd83dbSDimitry Andric if (!State) 14145ffd83dbSDimitry Andric return; 14155ffd83dbSDimitry Andric State = ensureStreamOpened(StreamVal, C, State); 14165ffd83dbSDimitry Andric if (!State) 14175ffd83dbSDimitry Andric return; 14185ffd83dbSDimitry Andric 14195ffd83dbSDimitry Andric C.addTransition(State); 14205ffd83dbSDimitry Andric } 14215ffd83dbSDimitry Andric 14225ffd83dbSDimitry Andric void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, 14235ffd83dbSDimitry Andric const CallEvent &Call, CheckerContext &C, 14245ffd83dbSDimitry Andric const StreamErrorState &ErrorKind) const { 14255ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 14265ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 14275ffd83dbSDimitry Andric assert(StreamSym && "Operation not permitted on non-symbolic stream value."); 14285ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 14295ffd83dbSDimitry Andric assert(SS && "Stream should be tracked by the checker."); 14305ffd83dbSDimitry Andric State = State->set<StreamMap>( 14315ffd83dbSDimitry Andric StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind)); 14325ffd83dbSDimitry Andric C.addTransition(State); 14335ffd83dbSDimitry Andric } 14345ffd83dbSDimitry Andric 1435cb14a3feSDimitry Andric void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call, 1436cb14a3feSDimitry Andric CheckerContext &C) const { 1437cb14a3feSDimitry Andric ProgramStateRef State = C.getState(); 1438cb14a3feSDimitry Andric SVal StreamVal = getStreamArg(Desc, Call); 1439cb14a3feSDimitry Andric std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>(); 1440cb14a3feSDimitry Andric if (!Stream) 1441cb14a3feSDimitry Andric return; 1442cb14a3feSDimitry Andric 1443cb14a3feSDimitry Andric ProgramStateRef StateNotNull, StateNull; 1444cb14a3feSDimitry Andric std::tie(StateNotNull, StateNull) = 1445cb14a3feSDimitry Andric C.getConstraintManager().assumeDual(State, *Stream); 1446cb14a3feSDimitry Andric if (StateNotNull && !StateNull) 1447cb14a3feSDimitry Andric ensureStreamOpened(StreamVal, C, StateNotNull); 1448cb14a3feSDimitry Andric } 1449cb14a3feSDimitry Andric 1450cb14a3feSDimitry Andric void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, 1451cb14a3feSDimitry Andric CheckerContext &C) const { 1452cb14a3feSDimitry Andric ProgramStateRef State = C.getState(); 1453cb14a3feSDimitry Andric SVal StreamVal = getStreamArg(Desc, Call); 1454cb14a3feSDimitry Andric std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>(); 1455cb14a3feSDimitry Andric if (!Stream) 1456cb14a3feSDimitry Andric return; 1457cb14a3feSDimitry Andric 1458cb14a3feSDimitry Andric // Skip if the stream can be both NULL and non-NULL. 1459cb14a3feSDimitry Andric ProgramStateRef StateNotNull, StateNull; 1460cb14a3feSDimitry Andric std::tie(StateNotNull, StateNull) = 1461cb14a3feSDimitry Andric C.getConstraintManager().assumeDual(State, *Stream); 1462cb14a3feSDimitry Andric if (StateNotNull && StateNull) 1463cb14a3feSDimitry Andric return; 1464cb14a3feSDimitry Andric if (StateNotNull && !StateNull) 1465cb14a3feSDimitry Andric State = StateNotNull; 1466cb14a3feSDimitry Andric else 1467cb14a3feSDimitry Andric State = StateNull; 1468cb14a3feSDimitry Andric 1469cb14a3feSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1470cb14a3feSDimitry Andric if (!CE) 1471cb14a3feSDimitry Andric return; 1472cb14a3feSDimitry Andric 1473cb14a3feSDimitry Andric // `fflush` returns EOF on failure, otherwise returns 0. 1474cb14a3feSDimitry Andric ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); 1475cb14a3feSDimitry Andric ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); 1476cb14a3feSDimitry Andric 1477cb14a3feSDimitry Andric // Clear error states if `fflush` returns 0, but retain their EOF flags. 1478cb14a3feSDimitry Andric auto ClearErrorInNotFailed = [&StateNotFailed, Desc](SymbolRef Sym, 1479cb14a3feSDimitry Andric const StreamState *SS) { 1480cb14a3feSDimitry Andric if (SS->ErrorState & ErrorFError) { 1481cb14a3feSDimitry Andric StreamErrorState NewES = 1482cb14a3feSDimitry Andric (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone; 1483cb14a3feSDimitry Andric StreamState NewSS = StreamState::getOpened(Desc, NewES, false); 1484cb14a3feSDimitry Andric StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS); 1485cb14a3feSDimitry Andric } 1486cb14a3feSDimitry Andric }; 1487cb14a3feSDimitry Andric 1488cb14a3feSDimitry Andric if (StateNotNull && !StateNull) { 1489cb14a3feSDimitry Andric // Skip if the input stream's state is unknown, open-failed or closed. 1490cb14a3feSDimitry Andric if (SymbolRef StreamSym = StreamVal.getAsSymbol()) { 1491cb14a3feSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 1492cb14a3feSDimitry Andric if (SS) { 1493cb14a3feSDimitry Andric assert(SS->isOpened() && "Stream is expected to be opened"); 1494cb14a3feSDimitry Andric ClearErrorInNotFailed(StreamSym, SS); 1495cb14a3feSDimitry Andric } else 1496cb14a3feSDimitry Andric return; 1497cb14a3feSDimitry Andric } 1498cb14a3feSDimitry Andric } else { 1499cb14a3feSDimitry Andric // Clear error states for all streams. 1500cb14a3feSDimitry Andric const StreamMapTy &Map = StateNotFailed->get<StreamMap>(); 1501cb14a3feSDimitry Andric for (const auto &I : Map) { 1502cb14a3feSDimitry Andric SymbolRef Sym = I.first; 1503cb14a3feSDimitry Andric const StreamState &SS = I.second; 1504cb14a3feSDimitry Andric if (SS.isOpened()) 1505cb14a3feSDimitry Andric ClearErrorInNotFailed(Sym, &SS); 1506cb14a3feSDimitry Andric } 1507cb14a3feSDimitry Andric } 1508cb14a3feSDimitry Andric 1509cb14a3feSDimitry Andric C.addTransition(StateNotFailed); 1510cb14a3feSDimitry Andric C.addTransition(StateFailed); 1511cb14a3feSDimitry Andric } 1512cb14a3feSDimitry Andric 15135ffd83dbSDimitry Andric ProgramStateRef 1514fe6060f1SDimitry Andric StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, 1515fe6060f1SDimitry Andric CheckerContext &C, 15165ffd83dbSDimitry Andric ProgramStateRef State) const { 15175ffd83dbSDimitry Andric auto Stream = StreamVal.getAs<DefinedSVal>(); 15185ffd83dbSDimitry Andric if (!Stream) 15195ffd83dbSDimitry Andric return State; 1520480093f4SDimitry Andric 1521480093f4SDimitry Andric ConstraintManager &CM = C.getConstraintManager(); 15225ffd83dbSDimitry Andric 1523480093f4SDimitry Andric ProgramStateRef StateNotNull, StateNull; 15245f757f3fSDimitry Andric std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream); 1525480093f4SDimitry Andric 1526480093f4SDimitry Andric if (!StateNotNull && StateNull) { 152706c3fb27SDimitry Andric if (ExplodedNode *N = C.generateErrorNode(StateNull)) { 152806c3fb27SDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>( 152906c3fb27SDimitry Andric BT_FileNull, "Stream pointer might be NULL.", N); 153006c3fb27SDimitry Andric if (StreamE) 153106c3fb27SDimitry Andric bugreporter::trackExpressionValue(N, StreamE, *R); 153206c3fb27SDimitry Andric C.emitReport(std::move(R)); 153306c3fb27SDimitry Andric } 15345ffd83dbSDimitry Andric return nullptr; 1535480093f4SDimitry Andric } 1536480093f4SDimitry Andric 15375ffd83dbSDimitry Andric return StateNotNull; 1538480093f4SDimitry Andric } 1539480093f4SDimitry Andric 15405ffd83dbSDimitry Andric ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, 15415ffd83dbSDimitry Andric CheckerContext &C, 15425ffd83dbSDimitry Andric ProgramStateRef State) const { 15435ffd83dbSDimitry Andric SymbolRef Sym = StreamVal.getAsSymbol(); 15445ffd83dbSDimitry Andric if (!Sym) 15455ffd83dbSDimitry Andric return State; 15465ffd83dbSDimitry Andric 15475ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(Sym); 15485ffd83dbSDimitry Andric if (!SS) 15495ffd83dbSDimitry Andric return State; 15505ffd83dbSDimitry Andric 15515ffd83dbSDimitry Andric if (SS->isClosed()) { 15525ffd83dbSDimitry Andric // Using a stream pointer after 'fclose' causes undefined behavior 15535ffd83dbSDimitry Andric // according to cppreference.com . 15545ffd83dbSDimitry Andric ExplodedNode *N = C.generateErrorNode(); 15555ffd83dbSDimitry Andric if (N) { 15565ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 15575ffd83dbSDimitry Andric BT_UseAfterClose, 15585ffd83dbSDimitry Andric "Stream might be already closed. Causes undefined behaviour.", N)); 15595ffd83dbSDimitry Andric return nullptr; 1560480093f4SDimitry Andric } 1561480093f4SDimitry Andric 15625ffd83dbSDimitry Andric return State; 15635ffd83dbSDimitry Andric } 15645ffd83dbSDimitry Andric 15655ffd83dbSDimitry Andric if (SS->isOpenFailed()) { 15665ffd83dbSDimitry Andric // Using a stream that has failed to open is likely to cause problems. 15675ffd83dbSDimitry Andric // This should usually not occur because stream pointer is NULL. 15685ffd83dbSDimitry Andric // But freopen can cause a state when stream pointer remains non-null but 15695ffd83dbSDimitry Andric // failed to open. 15705ffd83dbSDimitry Andric ExplodedNode *N = C.generateErrorNode(); 15715ffd83dbSDimitry Andric if (N) { 15725ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 15735ffd83dbSDimitry Andric BT_UseAfterOpenFailed, 15745ffd83dbSDimitry Andric "Stream might be invalid after " 15755ffd83dbSDimitry Andric "(re-)opening it has failed. " 15765ffd83dbSDimitry Andric "Can cause undefined behaviour.", 15775ffd83dbSDimitry Andric N)); 15785ffd83dbSDimitry Andric return nullptr; 15795ffd83dbSDimitry Andric } 15805ffd83dbSDimitry Andric } 15815ffd83dbSDimitry Andric 15825ffd83dbSDimitry Andric return State; 15835ffd83dbSDimitry Andric } 15845ffd83dbSDimitry Andric 15855ffd83dbSDimitry Andric ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate( 15865ffd83dbSDimitry Andric SVal StreamVal, CheckerContext &C, ProgramStateRef State) const { 15875ffd83dbSDimitry Andric static const char *BugMessage = 15885ffd83dbSDimitry Andric "File position of the stream might be 'indeterminate' " 15895ffd83dbSDimitry Andric "after a failed operation. " 15905ffd83dbSDimitry Andric "Can cause undefined behavior."; 15915ffd83dbSDimitry Andric 15925ffd83dbSDimitry Andric SymbolRef Sym = StreamVal.getAsSymbol(); 15935ffd83dbSDimitry Andric if (!Sym) 15945ffd83dbSDimitry Andric return State; 15955ffd83dbSDimitry Andric 15965ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(Sym); 15975ffd83dbSDimitry Andric if (!SS) 15985ffd83dbSDimitry Andric return State; 15995ffd83dbSDimitry Andric 16005ffd83dbSDimitry Andric assert(SS->isOpened() && "First ensure that stream is opened."); 16015ffd83dbSDimitry Andric 16025ffd83dbSDimitry Andric if (SS->FilePositionIndeterminate) { 16035ffd83dbSDimitry Andric if (SS->ErrorState & ErrorFEof) { 16045ffd83dbSDimitry Andric // The error is unknown but may be FEOF. 16055ffd83dbSDimitry Andric // Continue analysis with the FEOF error state. 16065ffd83dbSDimitry Andric // Report warning because the other possible error states. 16075ffd83dbSDimitry Andric ExplodedNode *N = C.generateNonFatalErrorNode(State); 16085ffd83dbSDimitry Andric if (!N) 16095ffd83dbSDimitry Andric return nullptr; 16105ffd83dbSDimitry Andric 16115ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 16125ffd83dbSDimitry Andric BT_IndeterminatePosition, BugMessage, N)); 16135ffd83dbSDimitry Andric return State->set<StreamMap>( 16145ffd83dbSDimitry Andric Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false)); 16155ffd83dbSDimitry Andric } 16165ffd83dbSDimitry Andric 16175ffd83dbSDimitry Andric // Known or unknown error state without FEOF possible. 16185ffd83dbSDimitry Andric // Stop analysis, report error. 16195ffd83dbSDimitry Andric ExplodedNode *N = C.generateErrorNode(State); 16205ffd83dbSDimitry Andric if (N) 16215ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 16225ffd83dbSDimitry Andric BT_IndeterminatePosition, BugMessage, N)); 16235ffd83dbSDimitry Andric 16245ffd83dbSDimitry Andric return nullptr; 16255ffd83dbSDimitry Andric } 16265ffd83dbSDimitry Andric 16275ffd83dbSDimitry Andric return State; 16285ffd83dbSDimitry Andric } 16295ffd83dbSDimitry Andric 16305ffd83dbSDimitry Andric ProgramStateRef 16315ffd83dbSDimitry Andric StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 16325ffd83dbSDimitry Andric ProgramStateRef State) const { 1633bdd1243dSDimitry Andric std::optional<nonloc::ConcreteInt> CI = 1634bdd1243dSDimitry Andric WhenceVal.getAs<nonloc::ConcreteInt>(); 16350b57cec5SDimitry Andric if (!CI) 16365ffd83dbSDimitry Andric return State; 16370b57cec5SDimitry Andric 1638480093f4SDimitry Andric int64_t X = CI->getValue().getSExtValue(); 163906c3fb27SDimitry Andric if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal) 16405ffd83dbSDimitry Andric return State; 16410b57cec5SDimitry Andric 1642480093f4SDimitry Andric if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 16435ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 16445ffd83dbSDimitry Andric BT_IllegalWhence, 16450b57cec5SDimitry Andric "The whence argument to fseek() should be " 16465ffd83dbSDimitry Andric "SEEK_SET, SEEK_END, or SEEK_CUR.", 16475ffd83dbSDimitry Andric N)); 16485ffd83dbSDimitry Andric return nullptr; 16495ffd83dbSDimitry Andric } 16505ffd83dbSDimitry Andric 16515ffd83dbSDimitry Andric return State; 16525ffd83dbSDimitry Andric } 16535ffd83dbSDimitry Andric 1654fe6060f1SDimitry Andric void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C, 16555ffd83dbSDimitry Andric ProgramStateRef State) const { 16565ffd83dbSDimitry Andric if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 1657fe6060f1SDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>( 16585ffd83dbSDimitry Andric BT_StreamEof, 16595ffd83dbSDimitry Andric "Read function called when stream is in EOF state. " 16605ffd83dbSDimitry Andric "Function has no effect.", 1661fe6060f1SDimitry Andric N); 1662fe6060f1SDimitry Andric R->markInteresting(StreamSym); 1663fe6060f1SDimitry Andric C.emitReport(std::move(R)); 16645ffd83dbSDimitry Andric return; 16650b57cec5SDimitry Andric } 16665ffd83dbSDimitry Andric C.addTransition(State); 16670b57cec5SDimitry Andric } 16680b57cec5SDimitry Andric 1669e8d8bef9SDimitry Andric ExplodedNode * 1670e8d8bef9SDimitry Andric StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 1671e8d8bef9SDimitry Andric CheckerContext &C, ExplodedNode *Pred) const { 1672e8d8bef9SDimitry Andric ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred); 1673e8d8bef9SDimitry Andric if (!Err) 1674e8d8bef9SDimitry Andric return Pred; 16750b57cec5SDimitry Andric 1676e8d8bef9SDimitry Andric for (SymbolRef LeakSym : LeakedSyms) { 16775ffd83dbSDimitry Andric // Resource leaks can result in multiple warning that describe the same kind 16785ffd83dbSDimitry Andric // of programming error: 16795ffd83dbSDimitry Andric // void f() { 16805ffd83dbSDimitry Andric // FILE *F = fopen("a.txt"); 16815ffd83dbSDimitry Andric // if (rand()) // state split 16825ffd83dbSDimitry Andric // return; // warning 16835ffd83dbSDimitry Andric // } // warning 16845ffd83dbSDimitry Andric // While this isn't necessarily true (leaking the same stream could result 16855ffd83dbSDimitry Andric // from a different kinds of errors), the reduction in redundant reports 16865ffd83dbSDimitry Andric // makes this a worthwhile heuristic. 16875ffd83dbSDimitry Andric // FIXME: Add a checker option to turn this uniqueing feature off. 1688e8d8bef9SDimitry Andric const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C); 16895ffd83dbSDimitry Andric assert(StreamOpenNode && "Could not find place of stream opening."); 169006c3fb27SDimitry Andric 169106c3fb27SDimitry Andric PathDiagnosticLocation LocUsedForUniqueing; 169206c3fb27SDimitry Andric if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics()) 169306c3fb27SDimitry Andric LocUsedForUniqueing = PathDiagnosticLocation::createBegin( 169406c3fb27SDimitry Andric StreamStmt, C.getSourceManager(), 16955ffd83dbSDimitry Andric StreamOpenNode->getLocationContext()); 16965ffd83dbSDimitry Andric 16975ffd83dbSDimitry Andric std::unique_ptr<PathSensitiveBugReport> R = 16985ffd83dbSDimitry Andric std::make_unique<PathSensitiveBugReport>( 16995ffd83dbSDimitry Andric BT_ResourceLeak, 1700e8d8bef9SDimitry Andric "Opened stream never closed. Potential resource leak.", Err, 17015ffd83dbSDimitry Andric LocUsedForUniqueing, 17025ffd83dbSDimitry Andric StreamOpenNode->getLocationContext()->getDecl()); 1703e8d8bef9SDimitry Andric R->markInteresting(LeakSym); 17045ffd83dbSDimitry Andric C.emitReport(std::move(R)); 17050b57cec5SDimitry Andric } 1706e8d8bef9SDimitry Andric 1707e8d8bef9SDimitry Andric return Err; 1708e8d8bef9SDimitry Andric } 1709e8d8bef9SDimitry Andric 1710e8d8bef9SDimitry Andric void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 1711e8d8bef9SDimitry Andric CheckerContext &C) const { 1712e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 1713e8d8bef9SDimitry Andric 1714e8d8bef9SDimitry Andric llvm::SmallVector<SymbolRef, 2> LeakedSyms; 1715e8d8bef9SDimitry Andric 1716e8d8bef9SDimitry Andric const StreamMapTy &Map = State->get<StreamMap>(); 1717e8d8bef9SDimitry Andric for (const auto &I : Map) { 1718e8d8bef9SDimitry Andric SymbolRef Sym = I.first; 1719e8d8bef9SDimitry Andric const StreamState &SS = I.second; 1720e8d8bef9SDimitry Andric if (!SymReaper.isDead(Sym)) 1721e8d8bef9SDimitry Andric continue; 1722e8d8bef9SDimitry Andric if (SS.isOpened()) 1723e8d8bef9SDimitry Andric LeakedSyms.push_back(Sym); 1724e8d8bef9SDimitry Andric State = State->remove<StreamMap>(Sym); 1725e8d8bef9SDimitry Andric } 1726e8d8bef9SDimitry Andric 1727e8d8bef9SDimitry Andric ExplodedNode *N = C.getPredecessor(); 1728e8d8bef9SDimitry Andric if (!LeakedSyms.empty()) 1729e8d8bef9SDimitry Andric N = reportLeaks(LeakedSyms, C, N); 1730e8d8bef9SDimitry Andric 1731e8d8bef9SDimitry Andric C.addTransition(State, N); 17320b57cec5SDimitry Andric } 17330b57cec5SDimitry Andric 17345ffd83dbSDimitry Andric ProgramStateRef StreamChecker::checkPointerEscape( 17355ffd83dbSDimitry Andric ProgramStateRef State, const InvalidatedSymbols &Escaped, 17365ffd83dbSDimitry Andric const CallEvent *Call, PointerEscapeKind Kind) const { 17375ffd83dbSDimitry Andric // Check for file-handling system call that is not handled by the checker. 17385ffd83dbSDimitry Andric // FIXME: The checker should be updated to handle all system calls that take 17395ffd83dbSDimitry Andric // 'FILE*' argument. These are now ignored. 17405ffd83dbSDimitry Andric if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader()) 17415ffd83dbSDimitry Andric return State; 17425ffd83dbSDimitry Andric 17435ffd83dbSDimitry Andric for (SymbolRef Sym : Escaped) { 17445ffd83dbSDimitry Andric // The symbol escaped. 17455ffd83dbSDimitry Andric // From now the stream can be manipulated in unknown way to the checker, 17465ffd83dbSDimitry Andric // it is not possible to handle it any more. 17475ffd83dbSDimitry Andric // Optimistically, assume that the corresponding file handle will be closed 17485ffd83dbSDimitry Andric // somewhere else. 17495ffd83dbSDimitry Andric // Remove symbol from state so the following stream calls on this symbol are 17505ffd83dbSDimitry Andric // not handled by the checker. 17515ffd83dbSDimitry Andric State = State->remove<StreamMap>(Sym); 17525ffd83dbSDimitry Andric } 17535ffd83dbSDimitry Andric return State; 17540b57cec5SDimitry Andric } 17550b57cec5SDimitry Andric 1756fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 1757fe6060f1SDimitry Andric // Checker registration. 1758fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 1759fe6060f1SDimitry Andric 17605ffd83dbSDimitry Andric void ento::registerStreamChecker(CheckerManager &Mgr) { 17615ffd83dbSDimitry Andric Mgr.registerChecker<StreamChecker>(); 17625ffd83dbSDimitry Andric } 17635ffd83dbSDimitry Andric 17645ffd83dbSDimitry Andric bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) { 17655ffd83dbSDimitry Andric return true; 17665ffd83dbSDimitry Andric } 17675ffd83dbSDimitry Andric 17685ffd83dbSDimitry Andric void ento::registerStreamTesterChecker(CheckerManager &Mgr) { 17695ffd83dbSDimitry Andric auto *Checker = Mgr.getChecker<StreamChecker>(); 17705ffd83dbSDimitry Andric Checker->TestMode = true; 17715ffd83dbSDimitry Andric } 17725ffd83dbSDimitry Andric 17735ffd83dbSDimitry Andric bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) { 17740b57cec5SDimitry Andric return true; 17750b57cec5SDimitry Andric } 1776