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" 170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 22480093f4SDimitry Andric #include <functional> 230b57cec5SDimitry Andric 240b57cec5SDimitry Andric using namespace clang; 250b57cec5SDimitry Andric using namespace ento; 26480093f4SDimitry Andric using namespace std::placeholders; 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric namespace { 290b57cec5SDimitry Andric 305ffd83dbSDimitry Andric struct FnDescription; 310b57cec5SDimitry Andric 325ffd83dbSDimitry Andric /// State of the stream error flags. 335ffd83dbSDimitry Andric /// Sometimes it is not known to the checker what error flags are set. 345ffd83dbSDimitry Andric /// This is indicated by setting more than one flag to true. 355ffd83dbSDimitry Andric /// This is an optimization to avoid state splits. 365ffd83dbSDimitry Andric /// A stream can either be in FEOF or FERROR but not both at the same time. 375ffd83dbSDimitry Andric /// Multiple flags are set to handle the corresponding states together. 385ffd83dbSDimitry Andric struct StreamErrorState { 395ffd83dbSDimitry Andric /// The stream can be in state where none of the error flags set. 405ffd83dbSDimitry Andric bool NoError = true; 415ffd83dbSDimitry Andric /// The stream can be in state where the EOF indicator is set. 425ffd83dbSDimitry Andric bool FEof = false; 435ffd83dbSDimitry Andric /// The stream can be in state where the error indicator is set. 445ffd83dbSDimitry Andric bool FError = false; 450b57cec5SDimitry Andric 465ffd83dbSDimitry Andric bool isNoError() const { return NoError && !FEof && !FError; } 475ffd83dbSDimitry Andric bool isFEof() const { return !NoError && FEof && !FError; } 485ffd83dbSDimitry Andric bool isFError() const { return !NoError && !FEof && FError; } 490b57cec5SDimitry Andric 505ffd83dbSDimitry Andric bool operator==(const StreamErrorState &ES) const { 515ffd83dbSDimitry Andric return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError; 525ffd83dbSDimitry Andric } 530b57cec5SDimitry Andric 545ffd83dbSDimitry Andric bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); } 555ffd83dbSDimitry Andric 565ffd83dbSDimitry Andric StreamErrorState operator|(const StreamErrorState &E) const { 575ffd83dbSDimitry Andric return {NoError || E.NoError, FEof || E.FEof, FError || E.FError}; 585ffd83dbSDimitry Andric } 595ffd83dbSDimitry Andric 605ffd83dbSDimitry Andric StreamErrorState operator&(const StreamErrorState &E) const { 615ffd83dbSDimitry Andric return {NoError && E.NoError, FEof && E.FEof, FError && E.FError}; 625ffd83dbSDimitry Andric } 635ffd83dbSDimitry Andric 645ffd83dbSDimitry Andric StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; } 655ffd83dbSDimitry Andric 665ffd83dbSDimitry Andric /// Returns if the StreamErrorState is a valid object. 675ffd83dbSDimitry Andric operator bool() const { return NoError || FEof || FError; } 680b57cec5SDimitry Andric 690b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const { 705ffd83dbSDimitry Andric ID.AddBoolean(NoError); 715ffd83dbSDimitry Andric ID.AddBoolean(FEof); 725ffd83dbSDimitry Andric ID.AddBoolean(FError); 730b57cec5SDimitry Andric } 740b57cec5SDimitry Andric }; 750b57cec5SDimitry Andric 765ffd83dbSDimitry Andric const StreamErrorState ErrorNone{true, false, false}; 775ffd83dbSDimitry Andric const StreamErrorState ErrorFEof{false, true, false}; 785ffd83dbSDimitry Andric const StreamErrorState ErrorFError{false, false, true}; 790b57cec5SDimitry Andric 805ffd83dbSDimitry Andric /// Full state information about a stream pointer. 815ffd83dbSDimitry Andric struct StreamState { 825ffd83dbSDimitry Andric /// The last file operation called in the stream. 835ffd83dbSDimitry Andric const FnDescription *LastOperation; 840b57cec5SDimitry Andric 855ffd83dbSDimitry Andric /// State of a stream symbol. 865ffd83dbSDimitry Andric /// FIXME: We need maybe an "escaped" state later. 875ffd83dbSDimitry Andric enum KindTy { 885ffd83dbSDimitry Andric Opened, /// Stream is opened. 895ffd83dbSDimitry Andric Closed, /// Closed stream (an invalid stream pointer after it was closed). 905ffd83dbSDimitry Andric OpenFailed /// The last open operation has failed. 915ffd83dbSDimitry Andric } State; 920b57cec5SDimitry Andric 935ffd83dbSDimitry Andric /// State of the error flags. 945ffd83dbSDimitry Andric /// Ignored in non-opened stream state but must be NoError. 955ffd83dbSDimitry Andric StreamErrorState const ErrorState; 965ffd83dbSDimitry Andric 975ffd83dbSDimitry Andric /// Indicate if the file has an "indeterminate file position indicator". 985ffd83dbSDimitry Andric /// This can be set at a failing read or write or seek operation. 995ffd83dbSDimitry Andric /// If it is set no more read or write is allowed. 1005ffd83dbSDimitry Andric /// This value is not dependent on the stream error flags: 1015ffd83dbSDimitry Andric /// The error flag may be cleared with `clearerr` but the file position 1025ffd83dbSDimitry Andric /// remains still indeterminate. 1035ffd83dbSDimitry Andric /// This value applies to all error states in ErrorState except FEOF. 1045ffd83dbSDimitry Andric /// An EOF+indeterminate state is the same as EOF state. 1055ffd83dbSDimitry Andric bool const FilePositionIndeterminate = false; 1065ffd83dbSDimitry Andric 1075ffd83dbSDimitry Andric StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES, 1085ffd83dbSDimitry Andric bool IsFilePositionIndeterminate) 1095ffd83dbSDimitry Andric : LastOperation(L), State(S), ErrorState(ES), 1105ffd83dbSDimitry Andric FilePositionIndeterminate(IsFilePositionIndeterminate) { 1115ffd83dbSDimitry Andric assert((!ES.isFEof() || !IsFilePositionIndeterminate) && 1125ffd83dbSDimitry Andric "FilePositionIndeterminate should be false in FEof case."); 1135ffd83dbSDimitry Andric assert((State == Opened || ErrorState.isNoError()) && 1145ffd83dbSDimitry Andric "ErrorState should be None in non-opened stream state."); 1155ffd83dbSDimitry Andric } 1165ffd83dbSDimitry Andric 1175ffd83dbSDimitry Andric bool isOpened() const { return State == Opened; } 1185ffd83dbSDimitry Andric bool isClosed() const { return State == Closed; } 1195ffd83dbSDimitry Andric bool isOpenFailed() const { return State == OpenFailed; } 1205ffd83dbSDimitry Andric 1215ffd83dbSDimitry Andric bool operator==(const StreamState &X) const { 1225ffd83dbSDimitry Andric // In not opened state error state should always NoError, so comparison 1235ffd83dbSDimitry Andric // here is no problem. 1245ffd83dbSDimitry Andric return LastOperation == X.LastOperation && State == X.State && 1255ffd83dbSDimitry Andric ErrorState == X.ErrorState && 1265ffd83dbSDimitry Andric FilePositionIndeterminate == X.FilePositionIndeterminate; 1275ffd83dbSDimitry Andric } 1285ffd83dbSDimitry Andric 1295ffd83dbSDimitry Andric static StreamState getOpened(const FnDescription *L, 1305ffd83dbSDimitry Andric const StreamErrorState &ES = ErrorNone, 1315ffd83dbSDimitry Andric bool IsFilePositionIndeterminate = false) { 1325ffd83dbSDimitry Andric return StreamState{L, Opened, ES, IsFilePositionIndeterminate}; 1335ffd83dbSDimitry Andric } 1345ffd83dbSDimitry Andric static StreamState getClosed(const FnDescription *L) { 1355ffd83dbSDimitry Andric return StreamState{L, Closed, {}, false}; 1365ffd83dbSDimitry Andric } 1375ffd83dbSDimitry Andric static StreamState getOpenFailed(const FnDescription *L) { 1385ffd83dbSDimitry Andric return StreamState{L, OpenFailed, {}, false}; 1395ffd83dbSDimitry Andric } 1405ffd83dbSDimitry Andric 1415ffd83dbSDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const { 1425ffd83dbSDimitry Andric ID.AddPointer(LastOperation); 1435ffd83dbSDimitry Andric ID.AddInteger(State); 1445ffd83dbSDimitry Andric ID.AddInteger(ErrorState); 1455ffd83dbSDimitry Andric ID.AddBoolean(FilePositionIndeterminate); 1465ffd83dbSDimitry Andric } 147480093f4SDimitry Andric }; 1480b57cec5SDimitry Andric 1495ffd83dbSDimitry Andric class StreamChecker; 1505ffd83dbSDimitry Andric using FnCheck = std::function<void(const StreamChecker *, const FnDescription *, 1515ffd83dbSDimitry Andric const CallEvent &, CheckerContext &)>; 152480093f4SDimitry Andric 1535ffd83dbSDimitry Andric using ArgNoTy = unsigned int; 1545ffd83dbSDimitry Andric static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max(); 1555ffd83dbSDimitry Andric 1565ffd83dbSDimitry Andric struct FnDescription { 1575ffd83dbSDimitry Andric FnCheck PreFn; 1585ffd83dbSDimitry Andric FnCheck EvalFn; 1595ffd83dbSDimitry Andric ArgNoTy StreamArgNo; 1605ffd83dbSDimitry Andric }; 1615ffd83dbSDimitry Andric 1625ffd83dbSDimitry Andric /// Get the value of the stream argument out of the passed call event. 1635ffd83dbSDimitry Andric /// The call should contain a function that is described by Desc. 1645ffd83dbSDimitry Andric SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) { 1655ffd83dbSDimitry Andric assert(Desc && Desc->StreamArgNo != ArgNone && 1665ffd83dbSDimitry Andric "Try to get a non-existing stream argument."); 1675ffd83dbSDimitry Andric return Call.getArgSVal(Desc->StreamArgNo); 1685ffd83dbSDimitry Andric } 1695ffd83dbSDimitry Andric 1705ffd83dbSDimitry Andric /// Create a conjured symbol return value for a call expression. 1715ffd83dbSDimitry Andric DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) { 1725ffd83dbSDimitry Andric assert(CE && "Expecting a call expression."); 1735ffd83dbSDimitry Andric 1745ffd83dbSDimitry Andric const LocationContext *LCtx = C.getLocationContext(); 1755ffd83dbSDimitry Andric return C.getSValBuilder() 1765ffd83dbSDimitry Andric .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()) 1775ffd83dbSDimitry Andric .castAs<DefinedSVal>(); 1785ffd83dbSDimitry Andric } 1795ffd83dbSDimitry Andric 1805ffd83dbSDimitry Andric ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C, 1815ffd83dbSDimitry Andric const CallExpr *CE) { 1825ffd83dbSDimitry Andric DefinedSVal RetVal = makeRetVal(C, CE); 1835ffd83dbSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(), RetVal); 1845ffd83dbSDimitry Andric State = State->assume(RetVal, true); 1855ffd83dbSDimitry Andric assert(State && "Assumption on new value should not fail."); 1865ffd83dbSDimitry Andric return State; 1875ffd83dbSDimitry Andric } 1885ffd83dbSDimitry Andric 1895ffd83dbSDimitry Andric ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, 1905ffd83dbSDimitry Andric CheckerContext &C, const CallExpr *CE) { 1915ffd83dbSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(), 1925ffd83dbSDimitry Andric C.getSValBuilder().makeIntVal(Value, false)); 1935ffd83dbSDimitry Andric return State; 1945ffd83dbSDimitry Andric } 1955ffd83dbSDimitry Andric 1965ffd83dbSDimitry Andric class StreamChecker : public Checker<check::PreCall, eval::Call, 1975ffd83dbSDimitry Andric check::DeadSymbols, check::PointerEscape> { 1985ffd83dbSDimitry Andric BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"}; 1995ffd83dbSDimitry Andric BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"}; 2005ffd83dbSDimitry Andric BugType BT_UseAfterOpenFailed{this, "Invalid stream", 2015ffd83dbSDimitry Andric "Stream handling error"}; 2025ffd83dbSDimitry Andric BugType BT_IndeterminatePosition{this, "Invalid stream state", 2035ffd83dbSDimitry Andric "Stream handling error"}; 2045ffd83dbSDimitry Andric BugType BT_IllegalWhence{this, "Illegal whence argument", 2055ffd83dbSDimitry Andric "Stream handling error"}; 2065ffd83dbSDimitry Andric BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"}; 207*e8d8bef9SDimitry Andric BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error", 208*e8d8bef9SDimitry Andric /*SuppressOnSink =*/true}; 2095ffd83dbSDimitry Andric 2105ffd83dbSDimitry Andric public: 2115ffd83dbSDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 2125ffd83dbSDimitry Andric bool evalCall(const CallEvent &Call, CheckerContext &C) const; 2135ffd83dbSDimitry Andric void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 2145ffd83dbSDimitry Andric ProgramStateRef checkPointerEscape(ProgramStateRef State, 2155ffd83dbSDimitry Andric const InvalidatedSymbols &Escaped, 2165ffd83dbSDimitry Andric const CallEvent *Call, 2175ffd83dbSDimitry Andric PointerEscapeKind Kind) const; 2185ffd83dbSDimitry Andric 2195ffd83dbSDimitry Andric /// If true, evaluate special testing stream functions. 2205ffd83dbSDimitry Andric bool TestMode = false; 2215ffd83dbSDimitry Andric 2225ffd83dbSDimitry Andric private: 2235ffd83dbSDimitry Andric CallDescriptionMap<FnDescription> FnDescriptions = { 2245ffd83dbSDimitry Andric {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 2255ffd83dbSDimitry Andric {{"freopen", 3}, 2265ffd83dbSDimitry Andric {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, 2275ffd83dbSDimitry Andric {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 2285ffd83dbSDimitry Andric {{"fclose", 1}, 2295ffd83dbSDimitry Andric {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, 2305ffd83dbSDimitry Andric {{"fread", 4}, 2315ffd83dbSDimitry Andric {&StreamChecker::preFread, 2325ffd83dbSDimitry Andric std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, 2335ffd83dbSDimitry Andric {{"fwrite", 4}, 2345ffd83dbSDimitry Andric {&StreamChecker::preFwrite, 2355ffd83dbSDimitry Andric std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, 2365ffd83dbSDimitry Andric {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 2375ffd83dbSDimitry Andric {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 2385ffd83dbSDimitry Andric {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 2395ffd83dbSDimitry Andric {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 2405ffd83dbSDimitry Andric {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 2415ffd83dbSDimitry Andric {{"clearerr", 1}, 2425ffd83dbSDimitry Andric {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, 2435ffd83dbSDimitry Andric {{"feof", 1}, 2445ffd83dbSDimitry Andric {&StreamChecker::preDefault, 2455ffd83dbSDimitry Andric std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), 2465ffd83dbSDimitry Andric 0}}, 2475ffd83dbSDimitry Andric {{"ferror", 1}, 2485ffd83dbSDimitry Andric {&StreamChecker::preDefault, 2495ffd83dbSDimitry Andric std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError), 2505ffd83dbSDimitry Andric 0}}, 2515ffd83dbSDimitry Andric {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 2525ffd83dbSDimitry Andric }; 2535ffd83dbSDimitry Andric 2545ffd83dbSDimitry Andric CallDescriptionMap<FnDescription> FnTestDescriptions = { 2555ffd83dbSDimitry Andric {{"StreamTesterChecker_make_feof_stream", 1}, 2565ffd83dbSDimitry Andric {nullptr, 2575ffd83dbSDimitry Andric std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof), 2585ffd83dbSDimitry Andric 0}}, 2595ffd83dbSDimitry Andric {{"StreamTesterChecker_make_ferror_stream", 1}, 2605ffd83dbSDimitry Andric {nullptr, 2615ffd83dbSDimitry Andric std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, 2625ffd83dbSDimitry Andric ErrorFError), 2635ffd83dbSDimitry Andric 0}}, 2645ffd83dbSDimitry Andric }; 2655ffd83dbSDimitry Andric 2665ffd83dbSDimitry Andric void evalFopen(const FnDescription *Desc, const CallEvent &Call, 2675ffd83dbSDimitry Andric CheckerContext &C) const; 2685ffd83dbSDimitry Andric 2695ffd83dbSDimitry Andric void preFreopen(const FnDescription *Desc, const CallEvent &Call, 2705ffd83dbSDimitry Andric CheckerContext &C) const; 2715ffd83dbSDimitry Andric void evalFreopen(const FnDescription *Desc, const CallEvent &Call, 2725ffd83dbSDimitry Andric CheckerContext &C) const; 2735ffd83dbSDimitry Andric 2745ffd83dbSDimitry Andric void evalFclose(const FnDescription *Desc, const CallEvent &Call, 2755ffd83dbSDimitry Andric CheckerContext &C) const; 2765ffd83dbSDimitry Andric 2775ffd83dbSDimitry Andric void preFread(const FnDescription *Desc, const CallEvent &Call, 2785ffd83dbSDimitry Andric CheckerContext &C) const; 2795ffd83dbSDimitry Andric 2805ffd83dbSDimitry Andric void preFwrite(const FnDescription *Desc, const CallEvent &Call, 2815ffd83dbSDimitry Andric CheckerContext &C) const; 2825ffd83dbSDimitry Andric 2835ffd83dbSDimitry Andric void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, 2845ffd83dbSDimitry Andric CheckerContext &C, bool IsFread) const; 2855ffd83dbSDimitry Andric 2865ffd83dbSDimitry Andric void preFseek(const FnDescription *Desc, const CallEvent &Call, 2875ffd83dbSDimitry Andric CheckerContext &C) const; 2885ffd83dbSDimitry Andric void evalFseek(const FnDescription *Desc, const CallEvent &Call, 2895ffd83dbSDimitry Andric CheckerContext &C) const; 2905ffd83dbSDimitry Andric 2915ffd83dbSDimitry Andric void preDefault(const FnDescription *Desc, const CallEvent &Call, 2925ffd83dbSDimitry Andric CheckerContext &C) const; 2935ffd83dbSDimitry Andric 2945ffd83dbSDimitry Andric void evalClearerr(const FnDescription *Desc, const CallEvent &Call, 2955ffd83dbSDimitry Andric CheckerContext &C) const; 2965ffd83dbSDimitry Andric 2975ffd83dbSDimitry Andric void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call, 2985ffd83dbSDimitry Andric CheckerContext &C, 2995ffd83dbSDimitry Andric const StreamErrorState &ErrorKind) const; 3005ffd83dbSDimitry Andric 3015ffd83dbSDimitry Andric void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call, 3025ffd83dbSDimitry Andric CheckerContext &C, 3035ffd83dbSDimitry Andric const StreamErrorState &ErrorKind) const; 3045ffd83dbSDimitry Andric 3055ffd83dbSDimitry Andric /// Check that the stream (in StreamVal) is not NULL. 3065ffd83dbSDimitry Andric /// If it can only be NULL a fatal error is emitted and nullptr returned. 3075ffd83dbSDimitry Andric /// Otherwise the return value is a new state where the stream is constrained 3085ffd83dbSDimitry Andric /// to be non-null. 3095ffd83dbSDimitry Andric ProgramStateRef ensureStreamNonNull(SVal StreamVal, CheckerContext &C, 3105ffd83dbSDimitry Andric ProgramStateRef State) const; 3115ffd83dbSDimitry Andric 3125ffd83dbSDimitry Andric /// Check that the stream is the opened state. 3135ffd83dbSDimitry Andric /// If the stream is known to be not opened an error is generated 3145ffd83dbSDimitry Andric /// and nullptr returned, otherwise the original state is returned. 3155ffd83dbSDimitry Andric ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C, 3165ffd83dbSDimitry Andric ProgramStateRef State) const; 3175ffd83dbSDimitry Andric 3185ffd83dbSDimitry Andric /// Check that the stream has not an invalid ("indeterminate") file position, 3195ffd83dbSDimitry Andric /// generate warning for it. 3205ffd83dbSDimitry Andric /// (EOF is not an invalid position.) 3215ffd83dbSDimitry Andric /// The returned state can be nullptr if a fatal error was generated. 3225ffd83dbSDimitry Andric /// It can return non-null state if the stream has not an invalid position or 3235ffd83dbSDimitry Andric /// there is execution path with non-invalid position. 3245ffd83dbSDimitry Andric ProgramStateRef 3255ffd83dbSDimitry Andric ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C, 3265ffd83dbSDimitry Andric ProgramStateRef State) const; 3275ffd83dbSDimitry Andric 3285ffd83dbSDimitry Andric /// Check the legality of the 'whence' argument of 'fseek'. 3295ffd83dbSDimitry Andric /// Generate error and return nullptr if it is found to be illegal. 3305ffd83dbSDimitry Andric /// Otherwise returns the state. 3315ffd83dbSDimitry Andric /// (State is not changed here because the "whence" value is already known.) 3325ffd83dbSDimitry Andric ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 3335ffd83dbSDimitry Andric ProgramStateRef State) const; 3345ffd83dbSDimitry Andric 3355ffd83dbSDimitry Andric /// Generate warning about stream in EOF state. 3365ffd83dbSDimitry Andric /// There will be always a state transition into the passed State, 3375ffd83dbSDimitry Andric /// by the new non-fatal error node or (if failed) a normal transition, 3385ffd83dbSDimitry Andric /// to ensure uniform handling. 3395ffd83dbSDimitry Andric void reportFEofWarning(CheckerContext &C, ProgramStateRef State) const; 3405ffd83dbSDimitry Andric 341*e8d8bef9SDimitry Andric /// Emit resource leak warnings for the given symbols. 342*e8d8bef9SDimitry Andric /// Createn a non-fatal error node for these, and returns it (if any warnings 343*e8d8bef9SDimitry Andric /// were generated). Return value is non-null. 344*e8d8bef9SDimitry Andric ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 345*e8d8bef9SDimitry Andric CheckerContext &C, ExplodedNode *Pred) const; 346*e8d8bef9SDimitry Andric 3475ffd83dbSDimitry Andric /// Find the description data of the function called by a call event. 3485ffd83dbSDimitry Andric /// Returns nullptr if no function is recognized. 3495ffd83dbSDimitry Andric const FnDescription *lookupFn(const CallEvent &Call) const { 3505ffd83dbSDimitry Andric // Recognize "global C functions" with only integral or pointer arguments 3515ffd83dbSDimitry Andric // (and matching name) as stream functions. 3525ffd83dbSDimitry Andric if (!Call.isGlobalCFunction()) 3535ffd83dbSDimitry Andric return nullptr; 3545ffd83dbSDimitry Andric for (auto P : Call.parameters()) { 3555ffd83dbSDimitry Andric QualType T = P->getType(); 3565ffd83dbSDimitry Andric if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) 3575ffd83dbSDimitry Andric return nullptr; 3585ffd83dbSDimitry Andric } 3595ffd83dbSDimitry Andric 3605ffd83dbSDimitry Andric return FnDescriptions.lookup(Call); 3615ffd83dbSDimitry Andric } 3625ffd83dbSDimitry Andric 3635ffd83dbSDimitry Andric /// Generate a message for BugReporterVisitor if the stored symbol is 3645ffd83dbSDimitry Andric /// marked as interesting by the actual bug report. 3655ffd83dbSDimitry Andric struct NoteFn { 3665ffd83dbSDimitry Andric const CheckerNameRef CheckerName; 3675ffd83dbSDimitry Andric SymbolRef StreamSym; 3685ffd83dbSDimitry Andric std::string Message; 3695ffd83dbSDimitry Andric 3705ffd83dbSDimitry Andric std::string operator()(PathSensitiveBugReport &BR) const { 3715ffd83dbSDimitry Andric if (BR.isInteresting(StreamSym) && 3725ffd83dbSDimitry Andric CheckerName == BR.getBugType().getCheckerName()) 3735ffd83dbSDimitry Andric return Message; 3745ffd83dbSDimitry Andric 3755ffd83dbSDimitry Andric return ""; 3765ffd83dbSDimitry Andric } 3775ffd83dbSDimitry Andric }; 3785ffd83dbSDimitry Andric 3795ffd83dbSDimitry Andric const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym, 3805ffd83dbSDimitry Andric const std::string &Message) const { 3815ffd83dbSDimitry Andric return C.getNoteTag(NoteFn{getCheckerName(), StreamSym, Message}); 3825ffd83dbSDimitry Andric } 3835ffd83dbSDimitry Andric 3845ffd83dbSDimitry Andric /// Searches for the ExplodedNode where the file descriptor was acquired for 3855ffd83dbSDimitry Andric /// StreamSym. 3865ffd83dbSDimitry Andric static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, 3875ffd83dbSDimitry Andric SymbolRef StreamSym, 3885ffd83dbSDimitry Andric CheckerContext &C); 3890b57cec5SDimitry Andric }; 3900b57cec5SDimitry Andric 3910b57cec5SDimitry Andric } // end anonymous namespace 3920b57cec5SDimitry Andric 3930b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 3940b57cec5SDimitry Andric 3955ffd83dbSDimitry Andric inline void assertStreamStateOpened(const StreamState *SS) { 3965ffd83dbSDimitry Andric assert(SS->isOpened() && 3975ffd83dbSDimitry Andric "Previous create of error node for non-opened stream failed?"); 3980b57cec5SDimitry Andric } 3990b57cec5SDimitry Andric 4005ffd83dbSDimitry Andric const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, 4015ffd83dbSDimitry Andric SymbolRef StreamSym, 4025ffd83dbSDimitry Andric CheckerContext &C) { 4035ffd83dbSDimitry Andric ProgramStateRef State = N->getState(); 4045ffd83dbSDimitry Andric // When bug type is resource leak, exploded node N may not have state info 4055ffd83dbSDimitry Andric // for leaked file descriptor, but predecessor should have it. 4065ffd83dbSDimitry Andric if (!State->get<StreamMap>(StreamSym)) 4075ffd83dbSDimitry Andric N = N->getFirstPred(); 4085ffd83dbSDimitry Andric 4095ffd83dbSDimitry Andric const ExplodedNode *Pred = N; 4105ffd83dbSDimitry Andric while (N) { 4115ffd83dbSDimitry Andric State = N->getState(); 4125ffd83dbSDimitry Andric if (!State->get<StreamMap>(StreamSym)) 4135ffd83dbSDimitry Andric return Pred; 4145ffd83dbSDimitry Andric Pred = N; 4155ffd83dbSDimitry Andric N = N->getFirstPred(); 4165ffd83dbSDimitry Andric } 4175ffd83dbSDimitry Andric 4185ffd83dbSDimitry Andric return nullptr; 4195ffd83dbSDimitry Andric } 4205ffd83dbSDimitry Andric 4215ffd83dbSDimitry Andric void StreamChecker::checkPreCall(const CallEvent &Call, 4225ffd83dbSDimitry Andric CheckerContext &C) const { 4235ffd83dbSDimitry Andric const FnDescription *Desc = lookupFn(Call); 4245ffd83dbSDimitry Andric if (!Desc || !Desc->PreFn) 4255ffd83dbSDimitry Andric return; 4265ffd83dbSDimitry Andric 4275ffd83dbSDimitry Andric Desc->PreFn(this, Desc, Call, C); 4285ffd83dbSDimitry Andric } 4295ffd83dbSDimitry Andric 4305ffd83dbSDimitry Andric bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { 4315ffd83dbSDimitry Andric const FnDescription *Desc = lookupFn(Call); 4325ffd83dbSDimitry Andric if (!Desc && TestMode) 4335ffd83dbSDimitry Andric Desc = FnTestDescriptions.lookup(Call); 4345ffd83dbSDimitry Andric if (!Desc || !Desc->EvalFn) 435480093f4SDimitry Andric return false; 436480093f4SDimitry Andric 4375ffd83dbSDimitry Andric Desc->EvalFn(this, Desc, Call, C); 438480093f4SDimitry Andric 439480093f4SDimitry Andric return C.isDifferent(); 4400b57cec5SDimitry Andric } 4410b57cec5SDimitry Andric 4425ffd83dbSDimitry Andric void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call, 4435ffd83dbSDimitry Andric CheckerContext &C) const { 4445ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 4455ffd83dbSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 446480093f4SDimitry Andric if (!CE) 447480093f4SDimitry Andric return; 448480093f4SDimitry Andric 4495ffd83dbSDimitry Andric DefinedSVal RetVal = makeRetVal(C, CE); 4505ffd83dbSDimitry Andric SymbolRef RetSym = RetVal.getAsSymbol(); 4515ffd83dbSDimitry Andric assert(RetSym && "RetVal must be a symbol here."); 4520b57cec5SDimitry Andric 4535ffd83dbSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(), RetVal); 4545ffd83dbSDimitry Andric 4550b57cec5SDimitry Andric // Bifurcate the state into two: one with a valid FILE* pointer, the other 4560b57cec5SDimitry Andric // with a NULL. 4575ffd83dbSDimitry Andric ProgramStateRef StateNotNull, StateNull; 4585ffd83dbSDimitry Andric std::tie(StateNotNull, StateNull) = 4595ffd83dbSDimitry Andric C.getConstraintManager().assumeDual(State, RetVal); 4600b57cec5SDimitry Andric 4615ffd83dbSDimitry Andric StateNotNull = 4625ffd83dbSDimitry Andric StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc)); 4635ffd83dbSDimitry Andric StateNull = 4645ffd83dbSDimitry Andric StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc)); 4650b57cec5SDimitry Andric 4665ffd83dbSDimitry Andric C.addTransition(StateNotNull, 4675ffd83dbSDimitry Andric constructNoteTag(C, RetSym, "Stream opened here")); 4685ffd83dbSDimitry Andric C.addTransition(StateNull); 4690b57cec5SDimitry Andric } 4700b57cec5SDimitry Andric 4715ffd83dbSDimitry Andric void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call, 4725ffd83dbSDimitry Andric CheckerContext &C) const { 4735ffd83dbSDimitry Andric // Do not allow NULL as passed stream pointer but allow a closed stream. 4745ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 4755ffd83dbSDimitry Andric State = ensureStreamNonNull(getStreamArg(Desc, Call), C, State); 4765ffd83dbSDimitry Andric if (!State) 4775ffd83dbSDimitry Andric return; 4785ffd83dbSDimitry Andric 4795ffd83dbSDimitry Andric C.addTransition(State); 4805ffd83dbSDimitry Andric } 4815ffd83dbSDimitry Andric 4825ffd83dbSDimitry Andric void StreamChecker::evalFreopen(const FnDescription *Desc, 4835ffd83dbSDimitry Andric const CallEvent &Call, 484480093f4SDimitry Andric CheckerContext &C) const { 485480093f4SDimitry Andric ProgramStateRef State = C.getState(); 4860b57cec5SDimitry Andric 487480093f4SDimitry Andric auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 488480093f4SDimitry Andric if (!CE) 4890b57cec5SDimitry Andric return; 490480093f4SDimitry Andric 4915ffd83dbSDimitry Andric Optional<DefinedSVal> StreamVal = 4925ffd83dbSDimitry Andric getStreamArg(Desc, Call).getAs<DefinedSVal>(); 493480093f4SDimitry Andric if (!StreamVal) 494480093f4SDimitry Andric return; 495480093f4SDimitry Andric 496480093f4SDimitry Andric SymbolRef StreamSym = StreamVal->getAsSymbol(); 4975ffd83dbSDimitry Andric // Do not care about concrete values for stream ("(FILE *)0x12345"?). 4985ffd83dbSDimitry Andric // FIXME: Can be stdin, stdout, stderr such values? 499480093f4SDimitry Andric if (!StreamSym) 500480093f4SDimitry Andric return; 501480093f4SDimitry Andric 5025ffd83dbSDimitry Andric // Do not handle untracked stream. It is probably escaped. 5035ffd83dbSDimitry Andric if (!State->get<StreamMap>(StreamSym)) 5045ffd83dbSDimitry Andric return; 5055ffd83dbSDimitry Andric 506480093f4SDimitry Andric // Generate state for non-failed case. 507480093f4SDimitry Andric // Return value is the passed stream pointer. 508480093f4SDimitry Andric // According to the documentations, the stream is closed first 509480093f4SDimitry Andric // but any close error is ignored. The state changes to (or remains) opened. 510480093f4SDimitry Andric ProgramStateRef StateRetNotNull = 511480093f4SDimitry Andric State->BindExpr(CE, C.getLocationContext(), *StreamVal); 512480093f4SDimitry Andric // Generate state for NULL return value. 513480093f4SDimitry Andric // Stream switches to OpenFailed state. 514480093f4SDimitry Andric ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(), 515480093f4SDimitry Andric C.getSValBuilder().makeNull()); 516480093f4SDimitry Andric 517480093f4SDimitry Andric StateRetNotNull = 5185ffd83dbSDimitry Andric StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 519480093f4SDimitry Andric StateRetNull = 5205ffd83dbSDimitry Andric StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc)); 521480093f4SDimitry Andric 5225ffd83dbSDimitry Andric C.addTransition(StateRetNotNull, 5235ffd83dbSDimitry Andric constructNoteTag(C, StreamSym, "Stream reopened here")); 524480093f4SDimitry Andric C.addTransition(StateRetNull); 5250b57cec5SDimitry Andric } 5260b57cec5SDimitry Andric 5275ffd83dbSDimitry Andric void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, 5285ffd83dbSDimitry Andric CheckerContext &C) const { 529480093f4SDimitry Andric ProgramStateRef State = C.getState(); 5305ffd83dbSDimitry Andric SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); 5315ffd83dbSDimitry Andric if (!Sym) 5325ffd83dbSDimitry Andric return; 5335ffd83dbSDimitry Andric 5345ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(Sym); 5355ffd83dbSDimitry Andric if (!SS) 5365ffd83dbSDimitry Andric return; 5375ffd83dbSDimitry Andric 5385ffd83dbSDimitry Andric assertStreamStateOpened(SS); 5395ffd83dbSDimitry Andric 5405ffd83dbSDimitry Andric // Close the File Descriptor. 5415ffd83dbSDimitry Andric // Regardless if the close fails or not, stream becomes "closed" 5425ffd83dbSDimitry Andric // and can not be used any more. 5435ffd83dbSDimitry Andric State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc)); 5445ffd83dbSDimitry Andric 545480093f4SDimitry Andric C.addTransition(State); 5460b57cec5SDimitry Andric } 5470b57cec5SDimitry Andric 5485ffd83dbSDimitry Andric void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, 5495ffd83dbSDimitry Andric CheckerContext &C) const { 550480093f4SDimitry Andric ProgramStateRef State = C.getState(); 5515ffd83dbSDimitry Andric SVal StreamVal = getStreamArg(Desc, Call); 5525ffd83dbSDimitry Andric State = ensureStreamNonNull(StreamVal, C, State); 5535ffd83dbSDimitry Andric if (!State) 5545ffd83dbSDimitry Andric return; 5555ffd83dbSDimitry Andric State = ensureStreamOpened(StreamVal, C, State); 5565ffd83dbSDimitry Andric if (!State) 5575ffd83dbSDimitry Andric return; 5585ffd83dbSDimitry Andric State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 5595ffd83dbSDimitry Andric if (!State) 560480093f4SDimitry Andric return; 561480093f4SDimitry Andric 5625ffd83dbSDimitry Andric SymbolRef Sym = StreamVal.getAsSymbol(); 5635ffd83dbSDimitry Andric if (Sym && State->get<StreamMap>(Sym)) { 5645ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(Sym); 5655ffd83dbSDimitry Andric if (SS->ErrorState & ErrorFEof) 5665ffd83dbSDimitry Andric reportFEofWarning(C, State); 5675ffd83dbSDimitry Andric } else { 568480093f4SDimitry Andric C.addTransition(State); 5695ffd83dbSDimitry Andric } 570480093f4SDimitry Andric } 571480093f4SDimitry Andric 5725ffd83dbSDimitry Andric void StreamChecker::preFwrite(const FnDescription *Desc, const CallEvent &Call, 5735ffd83dbSDimitry Andric CheckerContext &C) const { 574480093f4SDimitry Andric ProgramStateRef State = C.getState(); 5755ffd83dbSDimitry Andric SVal StreamVal = getStreamArg(Desc, Call); 5765ffd83dbSDimitry Andric State = ensureStreamNonNull(StreamVal, C, State); 5775ffd83dbSDimitry Andric if (!State) 5785ffd83dbSDimitry Andric return; 5795ffd83dbSDimitry Andric State = ensureStreamOpened(StreamVal, C, State); 5805ffd83dbSDimitry Andric if (!State) 5815ffd83dbSDimitry Andric return; 5825ffd83dbSDimitry Andric State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 5835ffd83dbSDimitry Andric if (!State) 5845ffd83dbSDimitry Andric return; 5855ffd83dbSDimitry Andric 586480093f4SDimitry Andric C.addTransition(State); 587480093f4SDimitry Andric } 588480093f4SDimitry Andric 5895ffd83dbSDimitry Andric void StreamChecker::evalFreadFwrite(const FnDescription *Desc, 5905ffd83dbSDimitry Andric const CallEvent &Call, CheckerContext &C, 5915ffd83dbSDimitry Andric bool IsFread) const { 5925ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 5935ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 5945ffd83dbSDimitry Andric if (!StreamSym) 5955ffd83dbSDimitry Andric return; 5965ffd83dbSDimitry Andric 5975ffd83dbSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 5985ffd83dbSDimitry Andric if (!CE) 5995ffd83dbSDimitry Andric return; 6005ffd83dbSDimitry Andric 6015ffd83dbSDimitry Andric Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); 6025ffd83dbSDimitry Andric if (!SizeVal) 6035ffd83dbSDimitry Andric return; 6045ffd83dbSDimitry Andric Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); 6055ffd83dbSDimitry Andric if (!NMembVal) 6065ffd83dbSDimitry Andric return; 6075ffd83dbSDimitry Andric 6085ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 6095ffd83dbSDimitry Andric if (!SS) 6105ffd83dbSDimitry Andric return; 6115ffd83dbSDimitry Andric 6125ffd83dbSDimitry Andric assertStreamStateOpened(SS); 6135ffd83dbSDimitry Andric 6145ffd83dbSDimitry Andric // C'99 standard, §7.19.8.1.3, the return value of fread: 6155ffd83dbSDimitry Andric // The fread function returns the number of elements successfully read, which 6165ffd83dbSDimitry Andric // may be less than nmemb if a read error or end-of-file is encountered. If 6175ffd83dbSDimitry Andric // size or nmemb is zero, fread returns zero and the contents of the array and 6185ffd83dbSDimitry Andric // the state of the stream remain unchanged. 6195ffd83dbSDimitry Andric 6205ffd83dbSDimitry Andric if (State->isNull(*SizeVal).isConstrainedTrue() || 6215ffd83dbSDimitry Andric State->isNull(*NMembVal).isConstrainedTrue()) { 6225ffd83dbSDimitry Andric // This is the "size or nmemb is zero" case. 6235ffd83dbSDimitry Andric // Just return 0, do nothing more (not clear the error flags). 6245ffd83dbSDimitry Andric State = bindInt(0, State, C, CE); 6255ffd83dbSDimitry Andric C.addTransition(State); 6265ffd83dbSDimitry Andric return; 6275ffd83dbSDimitry Andric } 6285ffd83dbSDimitry Andric 6295ffd83dbSDimitry Andric // Generate a transition for the success state. 6305ffd83dbSDimitry Andric // If we know the state to be FEOF at fread, do not add a success state. 6315ffd83dbSDimitry Andric if (!IsFread || (SS->ErrorState != ErrorFEof)) { 6325ffd83dbSDimitry Andric ProgramStateRef StateNotFailed = 6335ffd83dbSDimitry Andric State->BindExpr(CE, C.getLocationContext(), *NMembVal); 6345ffd83dbSDimitry Andric if (StateNotFailed) { 6355ffd83dbSDimitry Andric StateNotFailed = StateNotFailed->set<StreamMap>( 6365ffd83dbSDimitry Andric StreamSym, StreamState::getOpened(Desc)); 6375ffd83dbSDimitry Andric C.addTransition(StateNotFailed); 6385ffd83dbSDimitry Andric } 6395ffd83dbSDimitry Andric } 6405ffd83dbSDimitry Andric 6415ffd83dbSDimitry Andric // Add transition for the failed state. 6425ffd83dbSDimitry Andric Optional<NonLoc> RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 6435ffd83dbSDimitry Andric assert(RetVal && "Value should be NonLoc."); 6445ffd83dbSDimitry Andric ProgramStateRef StateFailed = 6455ffd83dbSDimitry Andric State->BindExpr(CE, C.getLocationContext(), *RetVal); 6465ffd83dbSDimitry Andric if (!StateFailed) 6475ffd83dbSDimitry Andric return; 6485ffd83dbSDimitry Andric auto Cond = C.getSValBuilder() 6495ffd83dbSDimitry Andric .evalBinOpNN(State, BO_LT, *RetVal, *NMembVal, 6505ffd83dbSDimitry Andric C.getASTContext().IntTy) 6515ffd83dbSDimitry Andric .getAs<DefinedOrUnknownSVal>(); 6525ffd83dbSDimitry Andric if (!Cond) 6535ffd83dbSDimitry Andric return; 6545ffd83dbSDimitry Andric StateFailed = StateFailed->assume(*Cond, true); 6555ffd83dbSDimitry Andric if (!StateFailed) 6565ffd83dbSDimitry Andric return; 6575ffd83dbSDimitry Andric 6585ffd83dbSDimitry Andric StreamErrorState NewES; 6595ffd83dbSDimitry Andric if (IsFread) 6605ffd83dbSDimitry Andric NewES = (SS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError; 6615ffd83dbSDimitry Andric else 6625ffd83dbSDimitry Andric NewES = ErrorFError; 6635ffd83dbSDimitry Andric // If a (non-EOF) error occurs, the resulting value of the file position 6645ffd83dbSDimitry Andric // indicator for the stream is indeterminate. 6655ffd83dbSDimitry Andric StreamState NewState = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); 6665ffd83dbSDimitry Andric StateFailed = StateFailed->set<StreamMap>(StreamSym, NewState); 6675ffd83dbSDimitry Andric C.addTransition(StateFailed); 6685ffd83dbSDimitry Andric } 6695ffd83dbSDimitry Andric 6705ffd83dbSDimitry Andric void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, 6715ffd83dbSDimitry Andric CheckerContext &C) const { 6725ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 6735ffd83dbSDimitry Andric SVal StreamVal = getStreamArg(Desc, Call); 6745ffd83dbSDimitry Andric State = ensureStreamNonNull(StreamVal, C, State); 6755ffd83dbSDimitry Andric if (!State) 6765ffd83dbSDimitry Andric return; 6775ffd83dbSDimitry Andric State = ensureStreamOpened(StreamVal, C, State); 6785ffd83dbSDimitry Andric if (!State) 6795ffd83dbSDimitry Andric return; 6805ffd83dbSDimitry Andric State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); 6815ffd83dbSDimitry Andric if (!State) 6825ffd83dbSDimitry Andric return; 6835ffd83dbSDimitry Andric 6845ffd83dbSDimitry Andric C.addTransition(State); 6855ffd83dbSDimitry Andric } 6865ffd83dbSDimitry Andric 6875ffd83dbSDimitry Andric void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, 6885ffd83dbSDimitry Andric CheckerContext &C) const { 6895ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 6905ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 6915ffd83dbSDimitry Andric if (!StreamSym) 6925ffd83dbSDimitry Andric return; 6935ffd83dbSDimitry Andric 6945ffd83dbSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 6955ffd83dbSDimitry Andric if (!CE) 6965ffd83dbSDimitry Andric return; 6975ffd83dbSDimitry Andric 6985ffd83dbSDimitry Andric // Ignore the call if the stream is not tracked. 6995ffd83dbSDimitry Andric if (!State->get<StreamMap>(StreamSym)) 7005ffd83dbSDimitry Andric return; 7015ffd83dbSDimitry Andric 7025ffd83dbSDimitry Andric DefinedSVal RetVal = makeRetVal(C, CE); 7035ffd83dbSDimitry Andric 7045ffd83dbSDimitry Andric // Make expression result. 7055ffd83dbSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(), RetVal); 7065ffd83dbSDimitry Andric 7075ffd83dbSDimitry Andric // Bifurcate the state into failed and non-failed. 7085ffd83dbSDimitry Andric // Return zero on success, nonzero on error. 7095ffd83dbSDimitry Andric ProgramStateRef StateNotFailed, StateFailed; 7105ffd83dbSDimitry Andric std::tie(StateFailed, StateNotFailed) = 7115ffd83dbSDimitry Andric C.getConstraintManager().assumeDual(State, RetVal); 7125ffd83dbSDimitry Andric 7135ffd83dbSDimitry Andric // Reset the state to opened with no error. 7145ffd83dbSDimitry Andric StateNotFailed = 7155ffd83dbSDimitry Andric StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 7165ffd83dbSDimitry Andric // We get error. 7175ffd83dbSDimitry Andric // It is possible that fseek fails but sets none of the error flags. 7185ffd83dbSDimitry Andric // If fseek failed, assume that the file position becomes indeterminate in any 7195ffd83dbSDimitry Andric // case. 7205ffd83dbSDimitry Andric StateFailed = StateFailed->set<StreamMap>( 7215ffd83dbSDimitry Andric StreamSym, 7225ffd83dbSDimitry Andric StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true)); 7235ffd83dbSDimitry Andric 7245ffd83dbSDimitry Andric C.addTransition(StateNotFailed); 7255ffd83dbSDimitry Andric C.addTransition(StateFailed); 7265ffd83dbSDimitry Andric } 7275ffd83dbSDimitry Andric 7285ffd83dbSDimitry Andric void StreamChecker::evalClearerr(const FnDescription *Desc, 7295ffd83dbSDimitry Andric const CallEvent &Call, 7305ffd83dbSDimitry Andric CheckerContext &C) const { 7315ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 7325ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 7335ffd83dbSDimitry Andric if (!StreamSym) 7345ffd83dbSDimitry Andric return; 7355ffd83dbSDimitry Andric 7365ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 7375ffd83dbSDimitry Andric if (!SS) 7385ffd83dbSDimitry Andric return; 7395ffd83dbSDimitry Andric 7405ffd83dbSDimitry Andric assertStreamStateOpened(SS); 7415ffd83dbSDimitry Andric 7425ffd83dbSDimitry Andric // FilePositionIndeterminate is not cleared. 7435ffd83dbSDimitry Andric State = State->set<StreamMap>( 7445ffd83dbSDimitry Andric StreamSym, 7455ffd83dbSDimitry Andric StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate)); 7465ffd83dbSDimitry Andric C.addTransition(State); 7475ffd83dbSDimitry Andric } 7485ffd83dbSDimitry Andric 7495ffd83dbSDimitry Andric void StreamChecker::evalFeofFerror(const FnDescription *Desc, 7505ffd83dbSDimitry Andric const CallEvent &Call, CheckerContext &C, 7515ffd83dbSDimitry Andric const StreamErrorState &ErrorKind) const { 7525ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 7535ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 7545ffd83dbSDimitry Andric if (!StreamSym) 7555ffd83dbSDimitry Andric return; 7565ffd83dbSDimitry Andric 7575ffd83dbSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 7585ffd83dbSDimitry Andric if (!CE) 7595ffd83dbSDimitry Andric return; 7605ffd83dbSDimitry Andric 7615ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 7625ffd83dbSDimitry Andric if (!SS) 7635ffd83dbSDimitry Andric return; 7645ffd83dbSDimitry Andric 7655ffd83dbSDimitry Andric assertStreamStateOpened(SS); 7665ffd83dbSDimitry Andric 7675ffd83dbSDimitry Andric if (SS->ErrorState & ErrorKind) { 7685ffd83dbSDimitry Andric // Execution path with error of ErrorKind. 7695ffd83dbSDimitry Andric // Function returns true. 7705ffd83dbSDimitry Andric // From now on it is the only one error state. 7715ffd83dbSDimitry Andric ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE); 7725ffd83dbSDimitry Andric C.addTransition(TrueState->set<StreamMap>( 7735ffd83dbSDimitry Andric StreamSym, StreamState::getOpened(Desc, ErrorKind, 7745ffd83dbSDimitry Andric SS->FilePositionIndeterminate && 7755ffd83dbSDimitry Andric !ErrorKind.isFEof()))); 7765ffd83dbSDimitry Andric } 7775ffd83dbSDimitry Andric if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) { 7785ffd83dbSDimitry Andric // Execution path(s) with ErrorKind not set. 7795ffd83dbSDimitry Andric // Function returns false. 7805ffd83dbSDimitry Andric // New error state is everything before minus ErrorKind. 7815ffd83dbSDimitry Andric ProgramStateRef FalseState = bindInt(0, State, C, CE); 7825ffd83dbSDimitry Andric C.addTransition(FalseState->set<StreamMap>( 7835ffd83dbSDimitry Andric StreamSym, 7845ffd83dbSDimitry Andric StreamState::getOpened( 7855ffd83dbSDimitry Andric Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof()))); 7865ffd83dbSDimitry Andric } 7875ffd83dbSDimitry Andric } 7885ffd83dbSDimitry Andric 7895ffd83dbSDimitry Andric void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, 7905ffd83dbSDimitry Andric CheckerContext &C) const { 7915ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 7925ffd83dbSDimitry Andric SVal StreamVal = getStreamArg(Desc, Call); 7935ffd83dbSDimitry Andric State = ensureStreamNonNull(StreamVal, C, State); 7945ffd83dbSDimitry Andric if (!State) 7955ffd83dbSDimitry Andric return; 7965ffd83dbSDimitry Andric State = ensureStreamOpened(StreamVal, C, State); 7975ffd83dbSDimitry Andric if (!State) 7985ffd83dbSDimitry Andric return; 7995ffd83dbSDimitry Andric 8005ffd83dbSDimitry Andric C.addTransition(State); 8015ffd83dbSDimitry Andric } 8025ffd83dbSDimitry Andric 8035ffd83dbSDimitry Andric void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, 8045ffd83dbSDimitry Andric const CallEvent &Call, CheckerContext &C, 8055ffd83dbSDimitry Andric const StreamErrorState &ErrorKind) const { 8065ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 8075ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 8085ffd83dbSDimitry Andric assert(StreamSym && "Operation not permitted on non-symbolic stream value."); 8095ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 8105ffd83dbSDimitry Andric assert(SS && "Stream should be tracked by the checker."); 8115ffd83dbSDimitry Andric State = State->set<StreamMap>( 8125ffd83dbSDimitry Andric StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind)); 8135ffd83dbSDimitry Andric C.addTransition(State); 8145ffd83dbSDimitry Andric } 8155ffd83dbSDimitry Andric 8165ffd83dbSDimitry Andric ProgramStateRef 8175ffd83dbSDimitry Andric StreamChecker::ensureStreamNonNull(SVal StreamVal, CheckerContext &C, 8185ffd83dbSDimitry Andric ProgramStateRef State) const { 8195ffd83dbSDimitry Andric auto Stream = StreamVal.getAs<DefinedSVal>(); 8205ffd83dbSDimitry Andric if (!Stream) 8215ffd83dbSDimitry Andric return State; 822480093f4SDimitry Andric 823480093f4SDimitry Andric ConstraintManager &CM = C.getConstraintManager(); 8245ffd83dbSDimitry Andric 825480093f4SDimitry Andric ProgramStateRef StateNotNull, StateNull; 8265ffd83dbSDimitry Andric std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream); 827480093f4SDimitry Andric 828480093f4SDimitry Andric if (!StateNotNull && StateNull) { 829480093f4SDimitry Andric if (ExplodedNode *N = C.generateErrorNode(StateNull)) { 830480093f4SDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 8315ffd83dbSDimitry Andric BT_FileNull, "Stream pointer might be NULL.", N)); 832480093f4SDimitry Andric } 8335ffd83dbSDimitry Andric return nullptr; 834480093f4SDimitry Andric } 835480093f4SDimitry Andric 8365ffd83dbSDimitry Andric return StateNotNull; 837480093f4SDimitry Andric } 838480093f4SDimitry Andric 8395ffd83dbSDimitry Andric ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, 8405ffd83dbSDimitry Andric CheckerContext &C, 8415ffd83dbSDimitry Andric ProgramStateRef State) const { 8425ffd83dbSDimitry Andric SymbolRef Sym = StreamVal.getAsSymbol(); 8435ffd83dbSDimitry Andric if (!Sym) 8445ffd83dbSDimitry Andric return State; 8455ffd83dbSDimitry Andric 8465ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(Sym); 8475ffd83dbSDimitry Andric if (!SS) 8485ffd83dbSDimitry Andric return State; 8495ffd83dbSDimitry Andric 8505ffd83dbSDimitry Andric if (SS->isClosed()) { 8515ffd83dbSDimitry Andric // Using a stream pointer after 'fclose' causes undefined behavior 8525ffd83dbSDimitry Andric // according to cppreference.com . 8535ffd83dbSDimitry Andric ExplodedNode *N = C.generateErrorNode(); 8545ffd83dbSDimitry Andric if (N) { 8555ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 8565ffd83dbSDimitry Andric BT_UseAfterClose, 8575ffd83dbSDimitry Andric "Stream might be already closed. Causes undefined behaviour.", N)); 8585ffd83dbSDimitry Andric return nullptr; 859480093f4SDimitry Andric } 860480093f4SDimitry Andric 8615ffd83dbSDimitry Andric return State; 8625ffd83dbSDimitry Andric } 8635ffd83dbSDimitry Andric 8645ffd83dbSDimitry Andric if (SS->isOpenFailed()) { 8655ffd83dbSDimitry Andric // Using a stream that has failed to open is likely to cause problems. 8665ffd83dbSDimitry Andric // This should usually not occur because stream pointer is NULL. 8675ffd83dbSDimitry Andric // But freopen can cause a state when stream pointer remains non-null but 8685ffd83dbSDimitry Andric // failed to open. 8695ffd83dbSDimitry Andric ExplodedNode *N = C.generateErrorNode(); 8705ffd83dbSDimitry Andric if (N) { 8715ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 8725ffd83dbSDimitry Andric BT_UseAfterOpenFailed, 8735ffd83dbSDimitry Andric "Stream might be invalid after " 8745ffd83dbSDimitry Andric "(re-)opening it has failed. " 8755ffd83dbSDimitry Andric "Can cause undefined behaviour.", 8765ffd83dbSDimitry Andric N)); 8775ffd83dbSDimitry Andric return nullptr; 8785ffd83dbSDimitry Andric } 8795ffd83dbSDimitry Andric return State; 8805ffd83dbSDimitry Andric } 8815ffd83dbSDimitry Andric 8825ffd83dbSDimitry Andric return State; 8835ffd83dbSDimitry Andric } 8845ffd83dbSDimitry Andric 8855ffd83dbSDimitry Andric ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate( 8865ffd83dbSDimitry Andric SVal StreamVal, CheckerContext &C, ProgramStateRef State) const { 8875ffd83dbSDimitry Andric static const char *BugMessage = 8885ffd83dbSDimitry Andric "File position of the stream might be 'indeterminate' " 8895ffd83dbSDimitry Andric "after a failed operation. " 8905ffd83dbSDimitry Andric "Can cause undefined behavior."; 8915ffd83dbSDimitry Andric 8925ffd83dbSDimitry Andric SymbolRef Sym = StreamVal.getAsSymbol(); 8935ffd83dbSDimitry Andric if (!Sym) 8945ffd83dbSDimitry Andric return State; 8955ffd83dbSDimitry Andric 8965ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(Sym); 8975ffd83dbSDimitry Andric if (!SS) 8985ffd83dbSDimitry Andric return State; 8995ffd83dbSDimitry Andric 9005ffd83dbSDimitry Andric assert(SS->isOpened() && "First ensure that stream is opened."); 9015ffd83dbSDimitry Andric 9025ffd83dbSDimitry Andric if (SS->FilePositionIndeterminate) { 9035ffd83dbSDimitry Andric if (SS->ErrorState & ErrorFEof) { 9045ffd83dbSDimitry Andric // The error is unknown but may be FEOF. 9055ffd83dbSDimitry Andric // Continue analysis with the FEOF error state. 9065ffd83dbSDimitry Andric // Report warning because the other possible error states. 9075ffd83dbSDimitry Andric ExplodedNode *N = C.generateNonFatalErrorNode(State); 9085ffd83dbSDimitry Andric if (!N) 9095ffd83dbSDimitry Andric return nullptr; 9105ffd83dbSDimitry Andric 9115ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 9125ffd83dbSDimitry Andric BT_IndeterminatePosition, BugMessage, N)); 9135ffd83dbSDimitry Andric return State->set<StreamMap>( 9145ffd83dbSDimitry Andric Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false)); 9155ffd83dbSDimitry Andric } 9165ffd83dbSDimitry Andric 9175ffd83dbSDimitry Andric // Known or unknown error state without FEOF possible. 9185ffd83dbSDimitry Andric // Stop analysis, report error. 9195ffd83dbSDimitry Andric ExplodedNode *N = C.generateErrorNode(State); 9205ffd83dbSDimitry Andric if (N) 9215ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 9225ffd83dbSDimitry Andric BT_IndeterminatePosition, BugMessage, N)); 9235ffd83dbSDimitry Andric 9245ffd83dbSDimitry Andric return nullptr; 9255ffd83dbSDimitry Andric } 9265ffd83dbSDimitry Andric 9275ffd83dbSDimitry Andric return State; 9285ffd83dbSDimitry Andric } 9295ffd83dbSDimitry Andric 9305ffd83dbSDimitry Andric ProgramStateRef 9315ffd83dbSDimitry Andric StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 9325ffd83dbSDimitry Andric ProgramStateRef State) const { 9335ffd83dbSDimitry Andric Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>(); 9340b57cec5SDimitry Andric if (!CI) 9355ffd83dbSDimitry Andric return State; 9360b57cec5SDimitry Andric 937480093f4SDimitry Andric int64_t X = CI->getValue().getSExtValue(); 938480093f4SDimitry Andric if (X >= 0 && X <= 2) 9395ffd83dbSDimitry Andric return State; 9400b57cec5SDimitry Andric 941480093f4SDimitry Andric if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 9425ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 9435ffd83dbSDimitry Andric BT_IllegalWhence, 9440b57cec5SDimitry Andric "The whence argument to fseek() should be " 9455ffd83dbSDimitry Andric "SEEK_SET, SEEK_END, or SEEK_CUR.", 9465ffd83dbSDimitry Andric N)); 9475ffd83dbSDimitry Andric return nullptr; 9485ffd83dbSDimitry Andric } 9495ffd83dbSDimitry Andric 9505ffd83dbSDimitry Andric return State; 9515ffd83dbSDimitry Andric } 9525ffd83dbSDimitry Andric 9535ffd83dbSDimitry Andric void StreamChecker::reportFEofWarning(CheckerContext &C, 9545ffd83dbSDimitry Andric ProgramStateRef State) const { 9555ffd83dbSDimitry Andric if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 956a7dea167SDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 9575ffd83dbSDimitry Andric BT_StreamEof, 9585ffd83dbSDimitry Andric "Read function called when stream is in EOF state. " 9595ffd83dbSDimitry Andric "Function has no effect.", 9605ffd83dbSDimitry Andric N)); 9615ffd83dbSDimitry Andric return; 9620b57cec5SDimitry Andric } 9635ffd83dbSDimitry Andric C.addTransition(State); 9640b57cec5SDimitry Andric } 9650b57cec5SDimitry Andric 966*e8d8bef9SDimitry Andric ExplodedNode * 967*e8d8bef9SDimitry Andric StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 968*e8d8bef9SDimitry Andric CheckerContext &C, ExplodedNode *Pred) const { 969*e8d8bef9SDimitry Andric ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred); 970*e8d8bef9SDimitry Andric if (!Err) 971*e8d8bef9SDimitry Andric return Pred; 9720b57cec5SDimitry Andric 973*e8d8bef9SDimitry Andric for (SymbolRef LeakSym : LeakedSyms) { 9745ffd83dbSDimitry Andric // Resource leaks can result in multiple warning that describe the same kind 9755ffd83dbSDimitry Andric // of programming error: 9765ffd83dbSDimitry Andric // void f() { 9775ffd83dbSDimitry Andric // FILE *F = fopen("a.txt"); 9785ffd83dbSDimitry Andric // if (rand()) // state split 9795ffd83dbSDimitry Andric // return; // warning 9805ffd83dbSDimitry Andric // } // warning 9815ffd83dbSDimitry Andric // While this isn't necessarily true (leaking the same stream could result 9825ffd83dbSDimitry Andric // from a different kinds of errors), the reduction in redundant reports 9835ffd83dbSDimitry Andric // makes this a worthwhile heuristic. 9845ffd83dbSDimitry Andric // FIXME: Add a checker option to turn this uniqueing feature off. 985*e8d8bef9SDimitry Andric const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C); 9865ffd83dbSDimitry Andric assert(StreamOpenNode && "Could not find place of stream opening."); 9875ffd83dbSDimitry Andric PathDiagnosticLocation LocUsedForUniqueing = 9885ffd83dbSDimitry Andric PathDiagnosticLocation::createBegin( 9895ffd83dbSDimitry Andric StreamOpenNode->getStmtForDiagnostics(), C.getSourceManager(), 9905ffd83dbSDimitry Andric StreamOpenNode->getLocationContext()); 9915ffd83dbSDimitry Andric 9925ffd83dbSDimitry Andric std::unique_ptr<PathSensitiveBugReport> R = 9935ffd83dbSDimitry Andric std::make_unique<PathSensitiveBugReport>( 9945ffd83dbSDimitry Andric BT_ResourceLeak, 995*e8d8bef9SDimitry Andric "Opened stream never closed. Potential resource leak.", Err, 9965ffd83dbSDimitry Andric LocUsedForUniqueing, 9975ffd83dbSDimitry Andric StreamOpenNode->getLocationContext()->getDecl()); 998*e8d8bef9SDimitry Andric R->markInteresting(LeakSym); 9995ffd83dbSDimitry Andric C.emitReport(std::move(R)); 10000b57cec5SDimitry Andric } 1001*e8d8bef9SDimitry Andric 1002*e8d8bef9SDimitry Andric return Err; 1003*e8d8bef9SDimitry Andric } 1004*e8d8bef9SDimitry Andric 1005*e8d8bef9SDimitry Andric void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 1006*e8d8bef9SDimitry Andric CheckerContext &C) const { 1007*e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 1008*e8d8bef9SDimitry Andric 1009*e8d8bef9SDimitry Andric llvm::SmallVector<SymbolRef, 2> LeakedSyms; 1010*e8d8bef9SDimitry Andric 1011*e8d8bef9SDimitry Andric const StreamMapTy &Map = State->get<StreamMap>(); 1012*e8d8bef9SDimitry Andric for (const auto &I : Map) { 1013*e8d8bef9SDimitry Andric SymbolRef Sym = I.first; 1014*e8d8bef9SDimitry Andric const StreamState &SS = I.second; 1015*e8d8bef9SDimitry Andric if (!SymReaper.isDead(Sym)) 1016*e8d8bef9SDimitry Andric continue; 1017*e8d8bef9SDimitry Andric if (SS.isOpened()) 1018*e8d8bef9SDimitry Andric LeakedSyms.push_back(Sym); 1019*e8d8bef9SDimitry Andric State = State->remove<StreamMap>(Sym); 1020*e8d8bef9SDimitry Andric } 1021*e8d8bef9SDimitry Andric 1022*e8d8bef9SDimitry Andric ExplodedNode *N = C.getPredecessor(); 1023*e8d8bef9SDimitry Andric if (!LeakedSyms.empty()) 1024*e8d8bef9SDimitry Andric N = reportLeaks(LeakedSyms, C, N); 1025*e8d8bef9SDimitry Andric 1026*e8d8bef9SDimitry Andric C.addTransition(State, N); 10270b57cec5SDimitry Andric } 10280b57cec5SDimitry Andric 10295ffd83dbSDimitry Andric ProgramStateRef StreamChecker::checkPointerEscape( 10305ffd83dbSDimitry Andric ProgramStateRef State, const InvalidatedSymbols &Escaped, 10315ffd83dbSDimitry Andric const CallEvent *Call, PointerEscapeKind Kind) const { 10325ffd83dbSDimitry Andric // Check for file-handling system call that is not handled by the checker. 10335ffd83dbSDimitry Andric // FIXME: The checker should be updated to handle all system calls that take 10345ffd83dbSDimitry Andric // 'FILE*' argument. These are now ignored. 10355ffd83dbSDimitry Andric if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader()) 10365ffd83dbSDimitry Andric return State; 10375ffd83dbSDimitry Andric 10385ffd83dbSDimitry Andric for (SymbolRef Sym : Escaped) { 10395ffd83dbSDimitry Andric // The symbol escaped. 10405ffd83dbSDimitry Andric // From now the stream can be manipulated in unknown way to the checker, 10415ffd83dbSDimitry Andric // it is not possible to handle it any more. 10425ffd83dbSDimitry Andric // Optimistically, assume that the corresponding file handle will be closed 10435ffd83dbSDimitry Andric // somewhere else. 10445ffd83dbSDimitry Andric // Remove symbol from state so the following stream calls on this symbol are 10455ffd83dbSDimitry Andric // not handled by the checker. 10465ffd83dbSDimitry Andric State = State->remove<StreamMap>(Sym); 10475ffd83dbSDimitry Andric } 10485ffd83dbSDimitry Andric return State; 10490b57cec5SDimitry Andric } 10500b57cec5SDimitry Andric 10515ffd83dbSDimitry Andric void ento::registerStreamChecker(CheckerManager &Mgr) { 10525ffd83dbSDimitry Andric Mgr.registerChecker<StreamChecker>(); 10535ffd83dbSDimitry Andric } 10545ffd83dbSDimitry Andric 10555ffd83dbSDimitry Andric bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) { 10565ffd83dbSDimitry Andric return true; 10575ffd83dbSDimitry Andric } 10585ffd83dbSDimitry Andric 10595ffd83dbSDimitry Andric void ento::registerStreamTesterChecker(CheckerManager &Mgr) { 10605ffd83dbSDimitry Andric auto *Checker = Mgr.getChecker<StreamChecker>(); 10615ffd83dbSDimitry Andric Checker->TestMode = true; 10625ffd83dbSDimitry Andric } 10635ffd83dbSDimitry Andric 10645ffd83dbSDimitry Andric bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) { 10650b57cec5SDimitry Andric return true; 10660b57cec5SDimitry Andric } 1067