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 30*5ffd83dbSDimitry Andric struct FnDescription; 310b57cec5SDimitry Andric 32*5ffd83dbSDimitry Andric /// State of the stream error flags. 33*5ffd83dbSDimitry Andric /// Sometimes it is not known to the checker what error flags are set. 34*5ffd83dbSDimitry Andric /// This is indicated by setting more than one flag to true. 35*5ffd83dbSDimitry Andric /// This is an optimization to avoid state splits. 36*5ffd83dbSDimitry Andric /// A stream can either be in FEOF or FERROR but not both at the same time. 37*5ffd83dbSDimitry Andric /// Multiple flags are set to handle the corresponding states together. 38*5ffd83dbSDimitry Andric struct StreamErrorState { 39*5ffd83dbSDimitry Andric /// The stream can be in state where none of the error flags set. 40*5ffd83dbSDimitry Andric bool NoError = true; 41*5ffd83dbSDimitry Andric /// The stream can be in state where the EOF indicator is set. 42*5ffd83dbSDimitry Andric bool FEof = false; 43*5ffd83dbSDimitry Andric /// The stream can be in state where the error indicator is set. 44*5ffd83dbSDimitry Andric bool FError = false; 450b57cec5SDimitry Andric 46*5ffd83dbSDimitry Andric bool isNoError() const { return NoError && !FEof && !FError; } 47*5ffd83dbSDimitry Andric bool isFEof() const { return !NoError && FEof && !FError; } 48*5ffd83dbSDimitry Andric bool isFError() const { return !NoError && !FEof && FError; } 490b57cec5SDimitry Andric 50*5ffd83dbSDimitry Andric bool operator==(const StreamErrorState &ES) const { 51*5ffd83dbSDimitry Andric return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError; 52*5ffd83dbSDimitry Andric } 530b57cec5SDimitry Andric 54*5ffd83dbSDimitry Andric bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); } 55*5ffd83dbSDimitry Andric 56*5ffd83dbSDimitry Andric StreamErrorState operator|(const StreamErrorState &E) const { 57*5ffd83dbSDimitry Andric return {NoError || E.NoError, FEof || E.FEof, FError || E.FError}; 58*5ffd83dbSDimitry Andric } 59*5ffd83dbSDimitry Andric 60*5ffd83dbSDimitry Andric StreamErrorState operator&(const StreamErrorState &E) const { 61*5ffd83dbSDimitry Andric return {NoError && E.NoError, FEof && E.FEof, FError && E.FError}; 62*5ffd83dbSDimitry Andric } 63*5ffd83dbSDimitry Andric 64*5ffd83dbSDimitry Andric StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; } 65*5ffd83dbSDimitry Andric 66*5ffd83dbSDimitry Andric /// Returns if the StreamErrorState is a valid object. 67*5ffd83dbSDimitry Andric operator bool() const { return NoError || FEof || FError; } 680b57cec5SDimitry Andric 690b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const { 70*5ffd83dbSDimitry Andric ID.AddBoolean(NoError); 71*5ffd83dbSDimitry Andric ID.AddBoolean(FEof); 72*5ffd83dbSDimitry Andric ID.AddBoolean(FError); 730b57cec5SDimitry Andric } 740b57cec5SDimitry Andric }; 750b57cec5SDimitry Andric 76*5ffd83dbSDimitry Andric const StreamErrorState ErrorNone{true, false, false}; 77*5ffd83dbSDimitry Andric const StreamErrorState ErrorFEof{false, true, false}; 78*5ffd83dbSDimitry Andric const StreamErrorState ErrorFError{false, false, true}; 790b57cec5SDimitry Andric 80*5ffd83dbSDimitry Andric /// Full state information about a stream pointer. 81*5ffd83dbSDimitry Andric struct StreamState { 82*5ffd83dbSDimitry Andric /// The last file operation called in the stream. 83*5ffd83dbSDimitry Andric const FnDescription *LastOperation; 840b57cec5SDimitry Andric 85*5ffd83dbSDimitry Andric /// State of a stream symbol. 86*5ffd83dbSDimitry Andric /// FIXME: We need maybe an "escaped" state later. 87*5ffd83dbSDimitry Andric enum KindTy { 88*5ffd83dbSDimitry Andric Opened, /// Stream is opened. 89*5ffd83dbSDimitry Andric Closed, /// Closed stream (an invalid stream pointer after it was closed). 90*5ffd83dbSDimitry Andric OpenFailed /// The last open operation has failed. 91*5ffd83dbSDimitry Andric } State; 920b57cec5SDimitry Andric 93*5ffd83dbSDimitry Andric /// State of the error flags. 94*5ffd83dbSDimitry Andric /// Ignored in non-opened stream state but must be NoError. 95*5ffd83dbSDimitry Andric StreamErrorState const ErrorState; 96*5ffd83dbSDimitry Andric 97*5ffd83dbSDimitry Andric /// Indicate if the file has an "indeterminate file position indicator". 98*5ffd83dbSDimitry Andric /// This can be set at a failing read or write or seek operation. 99*5ffd83dbSDimitry Andric /// If it is set no more read or write is allowed. 100*5ffd83dbSDimitry Andric /// This value is not dependent on the stream error flags: 101*5ffd83dbSDimitry Andric /// The error flag may be cleared with `clearerr` but the file position 102*5ffd83dbSDimitry Andric /// remains still indeterminate. 103*5ffd83dbSDimitry Andric /// This value applies to all error states in ErrorState except FEOF. 104*5ffd83dbSDimitry Andric /// An EOF+indeterminate state is the same as EOF state. 105*5ffd83dbSDimitry Andric bool const FilePositionIndeterminate = false; 106*5ffd83dbSDimitry Andric 107*5ffd83dbSDimitry Andric StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES, 108*5ffd83dbSDimitry Andric bool IsFilePositionIndeterminate) 109*5ffd83dbSDimitry Andric : LastOperation(L), State(S), ErrorState(ES), 110*5ffd83dbSDimitry Andric FilePositionIndeterminate(IsFilePositionIndeterminate) { 111*5ffd83dbSDimitry Andric assert((!ES.isFEof() || !IsFilePositionIndeterminate) && 112*5ffd83dbSDimitry Andric "FilePositionIndeterminate should be false in FEof case."); 113*5ffd83dbSDimitry Andric assert((State == Opened || ErrorState.isNoError()) && 114*5ffd83dbSDimitry Andric "ErrorState should be None in non-opened stream state."); 115*5ffd83dbSDimitry Andric } 116*5ffd83dbSDimitry Andric 117*5ffd83dbSDimitry Andric bool isOpened() const { return State == Opened; } 118*5ffd83dbSDimitry Andric bool isClosed() const { return State == Closed; } 119*5ffd83dbSDimitry Andric bool isOpenFailed() const { return State == OpenFailed; } 120*5ffd83dbSDimitry Andric 121*5ffd83dbSDimitry Andric bool operator==(const StreamState &X) const { 122*5ffd83dbSDimitry Andric // In not opened state error state should always NoError, so comparison 123*5ffd83dbSDimitry Andric // here is no problem. 124*5ffd83dbSDimitry Andric return LastOperation == X.LastOperation && State == X.State && 125*5ffd83dbSDimitry Andric ErrorState == X.ErrorState && 126*5ffd83dbSDimitry Andric FilePositionIndeterminate == X.FilePositionIndeterminate; 127*5ffd83dbSDimitry Andric } 128*5ffd83dbSDimitry Andric 129*5ffd83dbSDimitry Andric static StreamState getOpened(const FnDescription *L, 130*5ffd83dbSDimitry Andric const StreamErrorState &ES = ErrorNone, 131*5ffd83dbSDimitry Andric bool IsFilePositionIndeterminate = false) { 132*5ffd83dbSDimitry Andric return StreamState{L, Opened, ES, IsFilePositionIndeterminate}; 133*5ffd83dbSDimitry Andric } 134*5ffd83dbSDimitry Andric static StreamState getClosed(const FnDescription *L) { 135*5ffd83dbSDimitry Andric return StreamState{L, Closed, {}, false}; 136*5ffd83dbSDimitry Andric } 137*5ffd83dbSDimitry Andric static StreamState getOpenFailed(const FnDescription *L) { 138*5ffd83dbSDimitry Andric return StreamState{L, OpenFailed, {}, false}; 139*5ffd83dbSDimitry Andric } 140*5ffd83dbSDimitry Andric 141*5ffd83dbSDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const { 142*5ffd83dbSDimitry Andric ID.AddPointer(LastOperation); 143*5ffd83dbSDimitry Andric ID.AddInteger(State); 144*5ffd83dbSDimitry Andric ID.AddInteger(ErrorState); 145*5ffd83dbSDimitry Andric ID.AddBoolean(FilePositionIndeterminate); 146*5ffd83dbSDimitry Andric } 147480093f4SDimitry Andric }; 1480b57cec5SDimitry Andric 149*5ffd83dbSDimitry Andric class StreamChecker; 150*5ffd83dbSDimitry Andric using FnCheck = std::function<void(const StreamChecker *, const FnDescription *, 151*5ffd83dbSDimitry Andric const CallEvent &, CheckerContext &)>; 152480093f4SDimitry Andric 153*5ffd83dbSDimitry Andric using ArgNoTy = unsigned int; 154*5ffd83dbSDimitry Andric static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max(); 155*5ffd83dbSDimitry Andric 156*5ffd83dbSDimitry Andric struct FnDescription { 157*5ffd83dbSDimitry Andric FnCheck PreFn; 158*5ffd83dbSDimitry Andric FnCheck EvalFn; 159*5ffd83dbSDimitry Andric ArgNoTy StreamArgNo; 160*5ffd83dbSDimitry Andric }; 161*5ffd83dbSDimitry Andric 162*5ffd83dbSDimitry Andric /// Get the value of the stream argument out of the passed call event. 163*5ffd83dbSDimitry Andric /// The call should contain a function that is described by Desc. 164*5ffd83dbSDimitry Andric SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) { 165*5ffd83dbSDimitry Andric assert(Desc && Desc->StreamArgNo != ArgNone && 166*5ffd83dbSDimitry Andric "Try to get a non-existing stream argument."); 167*5ffd83dbSDimitry Andric return Call.getArgSVal(Desc->StreamArgNo); 168*5ffd83dbSDimitry Andric } 169*5ffd83dbSDimitry Andric 170*5ffd83dbSDimitry Andric /// Create a conjured symbol return value for a call expression. 171*5ffd83dbSDimitry Andric DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) { 172*5ffd83dbSDimitry Andric assert(CE && "Expecting a call expression."); 173*5ffd83dbSDimitry Andric 174*5ffd83dbSDimitry Andric const LocationContext *LCtx = C.getLocationContext(); 175*5ffd83dbSDimitry Andric return C.getSValBuilder() 176*5ffd83dbSDimitry Andric .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()) 177*5ffd83dbSDimitry Andric .castAs<DefinedSVal>(); 178*5ffd83dbSDimitry Andric } 179*5ffd83dbSDimitry Andric 180*5ffd83dbSDimitry Andric ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C, 181*5ffd83dbSDimitry Andric const CallExpr *CE) { 182*5ffd83dbSDimitry Andric DefinedSVal RetVal = makeRetVal(C, CE); 183*5ffd83dbSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(), RetVal); 184*5ffd83dbSDimitry Andric State = State->assume(RetVal, true); 185*5ffd83dbSDimitry Andric assert(State && "Assumption on new value should not fail."); 186*5ffd83dbSDimitry Andric return State; 187*5ffd83dbSDimitry Andric } 188*5ffd83dbSDimitry Andric 189*5ffd83dbSDimitry Andric ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, 190*5ffd83dbSDimitry Andric CheckerContext &C, const CallExpr *CE) { 191*5ffd83dbSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(), 192*5ffd83dbSDimitry Andric C.getSValBuilder().makeIntVal(Value, false)); 193*5ffd83dbSDimitry Andric return State; 194*5ffd83dbSDimitry Andric } 195*5ffd83dbSDimitry Andric 196*5ffd83dbSDimitry Andric class StreamChecker : public Checker<check::PreCall, eval::Call, 197*5ffd83dbSDimitry Andric check::DeadSymbols, check::PointerEscape> { 198*5ffd83dbSDimitry Andric BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"}; 199*5ffd83dbSDimitry Andric BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"}; 200*5ffd83dbSDimitry Andric BugType BT_UseAfterOpenFailed{this, "Invalid stream", 201*5ffd83dbSDimitry Andric "Stream handling error"}; 202*5ffd83dbSDimitry Andric BugType BT_IndeterminatePosition{this, "Invalid stream state", 203*5ffd83dbSDimitry Andric "Stream handling error"}; 204*5ffd83dbSDimitry Andric BugType BT_IllegalWhence{this, "Illegal whence argument", 205*5ffd83dbSDimitry Andric "Stream handling error"}; 206*5ffd83dbSDimitry Andric BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"}; 207*5ffd83dbSDimitry Andric BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error"}; 208*5ffd83dbSDimitry Andric 209*5ffd83dbSDimitry Andric public: 210*5ffd83dbSDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 211*5ffd83dbSDimitry Andric bool evalCall(const CallEvent &Call, CheckerContext &C) const; 212*5ffd83dbSDimitry Andric void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 213*5ffd83dbSDimitry Andric ProgramStateRef checkPointerEscape(ProgramStateRef State, 214*5ffd83dbSDimitry Andric const InvalidatedSymbols &Escaped, 215*5ffd83dbSDimitry Andric const CallEvent *Call, 216*5ffd83dbSDimitry Andric PointerEscapeKind Kind) const; 217*5ffd83dbSDimitry Andric 218*5ffd83dbSDimitry Andric /// If true, evaluate special testing stream functions. 219*5ffd83dbSDimitry Andric bool TestMode = false; 220*5ffd83dbSDimitry Andric 221*5ffd83dbSDimitry Andric private: 222*5ffd83dbSDimitry Andric CallDescriptionMap<FnDescription> FnDescriptions = { 223*5ffd83dbSDimitry Andric {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 224*5ffd83dbSDimitry Andric {{"freopen", 3}, 225*5ffd83dbSDimitry Andric {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, 226*5ffd83dbSDimitry Andric {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 227*5ffd83dbSDimitry Andric {{"fclose", 1}, 228*5ffd83dbSDimitry Andric {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, 229*5ffd83dbSDimitry Andric {{"fread", 4}, 230*5ffd83dbSDimitry Andric {&StreamChecker::preFread, 231*5ffd83dbSDimitry Andric std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, 232*5ffd83dbSDimitry Andric {{"fwrite", 4}, 233*5ffd83dbSDimitry Andric {&StreamChecker::preFwrite, 234*5ffd83dbSDimitry Andric std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, 235*5ffd83dbSDimitry Andric {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 236*5ffd83dbSDimitry Andric {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 237*5ffd83dbSDimitry Andric {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 238*5ffd83dbSDimitry Andric {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 239*5ffd83dbSDimitry Andric {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 240*5ffd83dbSDimitry Andric {{"clearerr", 1}, 241*5ffd83dbSDimitry Andric {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, 242*5ffd83dbSDimitry Andric {{"feof", 1}, 243*5ffd83dbSDimitry Andric {&StreamChecker::preDefault, 244*5ffd83dbSDimitry Andric std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), 245*5ffd83dbSDimitry Andric 0}}, 246*5ffd83dbSDimitry Andric {{"ferror", 1}, 247*5ffd83dbSDimitry Andric {&StreamChecker::preDefault, 248*5ffd83dbSDimitry Andric std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError), 249*5ffd83dbSDimitry Andric 0}}, 250*5ffd83dbSDimitry Andric {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 251*5ffd83dbSDimitry Andric }; 252*5ffd83dbSDimitry Andric 253*5ffd83dbSDimitry Andric CallDescriptionMap<FnDescription> FnTestDescriptions = { 254*5ffd83dbSDimitry Andric {{"StreamTesterChecker_make_feof_stream", 1}, 255*5ffd83dbSDimitry Andric {nullptr, 256*5ffd83dbSDimitry Andric std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof), 257*5ffd83dbSDimitry Andric 0}}, 258*5ffd83dbSDimitry Andric {{"StreamTesterChecker_make_ferror_stream", 1}, 259*5ffd83dbSDimitry Andric {nullptr, 260*5ffd83dbSDimitry Andric std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, 261*5ffd83dbSDimitry Andric ErrorFError), 262*5ffd83dbSDimitry Andric 0}}, 263*5ffd83dbSDimitry Andric }; 264*5ffd83dbSDimitry Andric 265*5ffd83dbSDimitry Andric void evalFopen(const FnDescription *Desc, const CallEvent &Call, 266*5ffd83dbSDimitry Andric CheckerContext &C) const; 267*5ffd83dbSDimitry Andric 268*5ffd83dbSDimitry Andric void preFreopen(const FnDescription *Desc, const CallEvent &Call, 269*5ffd83dbSDimitry Andric CheckerContext &C) const; 270*5ffd83dbSDimitry Andric void evalFreopen(const FnDescription *Desc, const CallEvent &Call, 271*5ffd83dbSDimitry Andric CheckerContext &C) const; 272*5ffd83dbSDimitry Andric 273*5ffd83dbSDimitry Andric void evalFclose(const FnDescription *Desc, const CallEvent &Call, 274*5ffd83dbSDimitry Andric CheckerContext &C) const; 275*5ffd83dbSDimitry Andric 276*5ffd83dbSDimitry Andric void preFread(const FnDescription *Desc, const CallEvent &Call, 277*5ffd83dbSDimitry Andric CheckerContext &C) const; 278*5ffd83dbSDimitry Andric 279*5ffd83dbSDimitry Andric void preFwrite(const FnDescription *Desc, const CallEvent &Call, 280*5ffd83dbSDimitry Andric CheckerContext &C) const; 281*5ffd83dbSDimitry Andric 282*5ffd83dbSDimitry Andric void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, 283*5ffd83dbSDimitry Andric CheckerContext &C, bool IsFread) const; 284*5ffd83dbSDimitry Andric 285*5ffd83dbSDimitry Andric void preFseek(const FnDescription *Desc, const CallEvent &Call, 286*5ffd83dbSDimitry Andric CheckerContext &C) const; 287*5ffd83dbSDimitry Andric void evalFseek(const FnDescription *Desc, const CallEvent &Call, 288*5ffd83dbSDimitry Andric CheckerContext &C) const; 289*5ffd83dbSDimitry Andric 290*5ffd83dbSDimitry Andric void preDefault(const FnDescription *Desc, const CallEvent &Call, 291*5ffd83dbSDimitry Andric CheckerContext &C) const; 292*5ffd83dbSDimitry Andric 293*5ffd83dbSDimitry Andric void evalClearerr(const FnDescription *Desc, const CallEvent &Call, 294*5ffd83dbSDimitry Andric CheckerContext &C) const; 295*5ffd83dbSDimitry Andric 296*5ffd83dbSDimitry Andric void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call, 297*5ffd83dbSDimitry Andric CheckerContext &C, 298*5ffd83dbSDimitry Andric const StreamErrorState &ErrorKind) const; 299*5ffd83dbSDimitry Andric 300*5ffd83dbSDimitry Andric void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call, 301*5ffd83dbSDimitry Andric CheckerContext &C, 302*5ffd83dbSDimitry Andric const StreamErrorState &ErrorKind) const; 303*5ffd83dbSDimitry Andric 304*5ffd83dbSDimitry Andric /// Check that the stream (in StreamVal) is not NULL. 305*5ffd83dbSDimitry Andric /// If it can only be NULL a fatal error is emitted and nullptr returned. 306*5ffd83dbSDimitry Andric /// Otherwise the return value is a new state where the stream is constrained 307*5ffd83dbSDimitry Andric /// to be non-null. 308*5ffd83dbSDimitry Andric ProgramStateRef ensureStreamNonNull(SVal StreamVal, CheckerContext &C, 309*5ffd83dbSDimitry Andric ProgramStateRef State) const; 310*5ffd83dbSDimitry Andric 311*5ffd83dbSDimitry Andric /// Check that the stream is the opened state. 312*5ffd83dbSDimitry Andric /// If the stream is known to be not opened an error is generated 313*5ffd83dbSDimitry Andric /// and nullptr returned, otherwise the original state is returned. 314*5ffd83dbSDimitry Andric ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C, 315*5ffd83dbSDimitry Andric ProgramStateRef State) const; 316*5ffd83dbSDimitry Andric 317*5ffd83dbSDimitry Andric /// Check that the stream has not an invalid ("indeterminate") file position, 318*5ffd83dbSDimitry Andric /// generate warning for it. 319*5ffd83dbSDimitry Andric /// (EOF is not an invalid position.) 320*5ffd83dbSDimitry Andric /// The returned state can be nullptr if a fatal error was generated. 321*5ffd83dbSDimitry Andric /// It can return non-null state if the stream has not an invalid position or 322*5ffd83dbSDimitry Andric /// there is execution path with non-invalid position. 323*5ffd83dbSDimitry Andric ProgramStateRef 324*5ffd83dbSDimitry Andric ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C, 325*5ffd83dbSDimitry Andric ProgramStateRef State) const; 326*5ffd83dbSDimitry Andric 327*5ffd83dbSDimitry Andric /// Check the legality of the 'whence' argument of 'fseek'. 328*5ffd83dbSDimitry Andric /// Generate error and return nullptr if it is found to be illegal. 329*5ffd83dbSDimitry Andric /// Otherwise returns the state. 330*5ffd83dbSDimitry Andric /// (State is not changed here because the "whence" value is already known.) 331*5ffd83dbSDimitry Andric ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 332*5ffd83dbSDimitry Andric ProgramStateRef State) const; 333*5ffd83dbSDimitry Andric 334*5ffd83dbSDimitry Andric /// Generate warning about stream in EOF state. 335*5ffd83dbSDimitry Andric /// There will be always a state transition into the passed State, 336*5ffd83dbSDimitry Andric /// by the new non-fatal error node or (if failed) a normal transition, 337*5ffd83dbSDimitry Andric /// to ensure uniform handling. 338*5ffd83dbSDimitry Andric void reportFEofWarning(CheckerContext &C, ProgramStateRef State) const; 339*5ffd83dbSDimitry Andric 340*5ffd83dbSDimitry Andric /// Find the description data of the function called by a call event. 341*5ffd83dbSDimitry Andric /// Returns nullptr if no function is recognized. 342*5ffd83dbSDimitry Andric const FnDescription *lookupFn(const CallEvent &Call) const { 343*5ffd83dbSDimitry Andric // Recognize "global C functions" with only integral or pointer arguments 344*5ffd83dbSDimitry Andric // (and matching name) as stream functions. 345*5ffd83dbSDimitry Andric if (!Call.isGlobalCFunction()) 346*5ffd83dbSDimitry Andric return nullptr; 347*5ffd83dbSDimitry Andric for (auto P : Call.parameters()) { 348*5ffd83dbSDimitry Andric QualType T = P->getType(); 349*5ffd83dbSDimitry Andric if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) 350*5ffd83dbSDimitry Andric return nullptr; 351*5ffd83dbSDimitry Andric } 352*5ffd83dbSDimitry Andric 353*5ffd83dbSDimitry Andric return FnDescriptions.lookup(Call); 354*5ffd83dbSDimitry Andric } 355*5ffd83dbSDimitry Andric 356*5ffd83dbSDimitry Andric /// Generate a message for BugReporterVisitor if the stored symbol is 357*5ffd83dbSDimitry Andric /// marked as interesting by the actual bug report. 358*5ffd83dbSDimitry Andric struct NoteFn { 359*5ffd83dbSDimitry Andric const CheckerNameRef CheckerName; 360*5ffd83dbSDimitry Andric SymbolRef StreamSym; 361*5ffd83dbSDimitry Andric std::string Message; 362*5ffd83dbSDimitry Andric 363*5ffd83dbSDimitry Andric std::string operator()(PathSensitiveBugReport &BR) const { 364*5ffd83dbSDimitry Andric if (BR.isInteresting(StreamSym) && 365*5ffd83dbSDimitry Andric CheckerName == BR.getBugType().getCheckerName()) 366*5ffd83dbSDimitry Andric return Message; 367*5ffd83dbSDimitry Andric 368*5ffd83dbSDimitry Andric return ""; 369*5ffd83dbSDimitry Andric } 370*5ffd83dbSDimitry Andric }; 371*5ffd83dbSDimitry Andric 372*5ffd83dbSDimitry Andric const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym, 373*5ffd83dbSDimitry Andric const std::string &Message) const { 374*5ffd83dbSDimitry Andric return C.getNoteTag(NoteFn{getCheckerName(), StreamSym, Message}); 375*5ffd83dbSDimitry Andric } 376*5ffd83dbSDimitry Andric 377*5ffd83dbSDimitry Andric /// Searches for the ExplodedNode where the file descriptor was acquired for 378*5ffd83dbSDimitry Andric /// StreamSym. 379*5ffd83dbSDimitry Andric static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, 380*5ffd83dbSDimitry Andric SymbolRef StreamSym, 381*5ffd83dbSDimitry Andric CheckerContext &C); 3820b57cec5SDimitry Andric }; 3830b57cec5SDimitry Andric 3840b57cec5SDimitry Andric } // end anonymous namespace 3850b57cec5SDimitry Andric 3860b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 3870b57cec5SDimitry Andric 388*5ffd83dbSDimitry Andric inline void assertStreamStateOpened(const StreamState *SS) { 389*5ffd83dbSDimitry Andric assert(SS->isOpened() && 390*5ffd83dbSDimitry Andric "Previous create of error node for non-opened stream failed?"); 3910b57cec5SDimitry Andric } 3920b57cec5SDimitry Andric 393*5ffd83dbSDimitry Andric const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, 394*5ffd83dbSDimitry Andric SymbolRef StreamSym, 395*5ffd83dbSDimitry Andric CheckerContext &C) { 396*5ffd83dbSDimitry Andric ProgramStateRef State = N->getState(); 397*5ffd83dbSDimitry Andric // When bug type is resource leak, exploded node N may not have state info 398*5ffd83dbSDimitry Andric // for leaked file descriptor, but predecessor should have it. 399*5ffd83dbSDimitry Andric if (!State->get<StreamMap>(StreamSym)) 400*5ffd83dbSDimitry Andric N = N->getFirstPred(); 401*5ffd83dbSDimitry Andric 402*5ffd83dbSDimitry Andric const ExplodedNode *Pred = N; 403*5ffd83dbSDimitry Andric while (N) { 404*5ffd83dbSDimitry Andric State = N->getState(); 405*5ffd83dbSDimitry Andric if (!State->get<StreamMap>(StreamSym)) 406*5ffd83dbSDimitry Andric return Pred; 407*5ffd83dbSDimitry Andric Pred = N; 408*5ffd83dbSDimitry Andric N = N->getFirstPred(); 409*5ffd83dbSDimitry Andric } 410*5ffd83dbSDimitry Andric 411*5ffd83dbSDimitry Andric return nullptr; 412*5ffd83dbSDimitry Andric } 413*5ffd83dbSDimitry Andric 414*5ffd83dbSDimitry Andric void StreamChecker::checkPreCall(const CallEvent &Call, 415*5ffd83dbSDimitry Andric CheckerContext &C) const { 416*5ffd83dbSDimitry Andric const FnDescription *Desc = lookupFn(Call); 417*5ffd83dbSDimitry Andric if (!Desc || !Desc->PreFn) 418*5ffd83dbSDimitry Andric return; 419*5ffd83dbSDimitry Andric 420*5ffd83dbSDimitry Andric Desc->PreFn(this, Desc, Call, C); 421*5ffd83dbSDimitry Andric } 422*5ffd83dbSDimitry Andric 423*5ffd83dbSDimitry Andric bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { 424*5ffd83dbSDimitry Andric const FnDescription *Desc = lookupFn(Call); 425*5ffd83dbSDimitry Andric if (!Desc && TestMode) 426*5ffd83dbSDimitry Andric Desc = FnTestDescriptions.lookup(Call); 427*5ffd83dbSDimitry Andric if (!Desc || !Desc->EvalFn) 428480093f4SDimitry Andric return false; 429480093f4SDimitry Andric 430*5ffd83dbSDimitry Andric Desc->EvalFn(this, Desc, Call, C); 431480093f4SDimitry Andric 432480093f4SDimitry Andric return C.isDifferent(); 4330b57cec5SDimitry Andric } 4340b57cec5SDimitry Andric 435*5ffd83dbSDimitry Andric void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call, 436*5ffd83dbSDimitry Andric CheckerContext &C) const { 437*5ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 438*5ffd83dbSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 439480093f4SDimitry Andric if (!CE) 440480093f4SDimitry Andric return; 441480093f4SDimitry Andric 442*5ffd83dbSDimitry Andric DefinedSVal RetVal = makeRetVal(C, CE); 443*5ffd83dbSDimitry Andric SymbolRef RetSym = RetVal.getAsSymbol(); 444*5ffd83dbSDimitry Andric assert(RetSym && "RetVal must be a symbol here."); 4450b57cec5SDimitry Andric 446*5ffd83dbSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(), RetVal); 447*5ffd83dbSDimitry Andric 4480b57cec5SDimitry Andric // Bifurcate the state into two: one with a valid FILE* pointer, the other 4490b57cec5SDimitry Andric // with a NULL. 450*5ffd83dbSDimitry Andric ProgramStateRef StateNotNull, StateNull; 451*5ffd83dbSDimitry Andric std::tie(StateNotNull, StateNull) = 452*5ffd83dbSDimitry Andric C.getConstraintManager().assumeDual(State, RetVal); 4530b57cec5SDimitry Andric 454*5ffd83dbSDimitry Andric StateNotNull = 455*5ffd83dbSDimitry Andric StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc)); 456*5ffd83dbSDimitry Andric StateNull = 457*5ffd83dbSDimitry Andric StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc)); 4580b57cec5SDimitry Andric 459*5ffd83dbSDimitry Andric C.addTransition(StateNotNull, 460*5ffd83dbSDimitry Andric constructNoteTag(C, RetSym, "Stream opened here")); 461*5ffd83dbSDimitry Andric C.addTransition(StateNull); 4620b57cec5SDimitry Andric } 4630b57cec5SDimitry Andric 464*5ffd83dbSDimitry Andric void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call, 465*5ffd83dbSDimitry Andric CheckerContext &C) const { 466*5ffd83dbSDimitry Andric // Do not allow NULL as passed stream pointer but allow a closed stream. 467*5ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 468*5ffd83dbSDimitry Andric State = ensureStreamNonNull(getStreamArg(Desc, Call), C, State); 469*5ffd83dbSDimitry Andric if (!State) 470*5ffd83dbSDimitry Andric return; 471*5ffd83dbSDimitry Andric 472*5ffd83dbSDimitry Andric C.addTransition(State); 473*5ffd83dbSDimitry Andric } 474*5ffd83dbSDimitry Andric 475*5ffd83dbSDimitry Andric void StreamChecker::evalFreopen(const FnDescription *Desc, 476*5ffd83dbSDimitry Andric const CallEvent &Call, 477480093f4SDimitry Andric CheckerContext &C) const { 478480093f4SDimitry Andric ProgramStateRef State = C.getState(); 4790b57cec5SDimitry Andric 480480093f4SDimitry Andric auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 481480093f4SDimitry Andric if (!CE) 4820b57cec5SDimitry Andric return; 483480093f4SDimitry Andric 484*5ffd83dbSDimitry Andric Optional<DefinedSVal> StreamVal = 485*5ffd83dbSDimitry Andric getStreamArg(Desc, Call).getAs<DefinedSVal>(); 486480093f4SDimitry Andric if (!StreamVal) 487480093f4SDimitry Andric return; 488480093f4SDimitry Andric 489480093f4SDimitry Andric SymbolRef StreamSym = StreamVal->getAsSymbol(); 490*5ffd83dbSDimitry Andric // Do not care about concrete values for stream ("(FILE *)0x12345"?). 491*5ffd83dbSDimitry Andric // FIXME: Can be stdin, stdout, stderr such values? 492480093f4SDimitry Andric if (!StreamSym) 493480093f4SDimitry Andric return; 494480093f4SDimitry Andric 495*5ffd83dbSDimitry Andric // Do not handle untracked stream. It is probably escaped. 496*5ffd83dbSDimitry Andric if (!State->get<StreamMap>(StreamSym)) 497*5ffd83dbSDimitry Andric return; 498*5ffd83dbSDimitry Andric 499480093f4SDimitry Andric // Generate state for non-failed case. 500480093f4SDimitry Andric // Return value is the passed stream pointer. 501480093f4SDimitry Andric // According to the documentations, the stream is closed first 502480093f4SDimitry Andric // but any close error is ignored. The state changes to (or remains) opened. 503480093f4SDimitry Andric ProgramStateRef StateRetNotNull = 504480093f4SDimitry Andric State->BindExpr(CE, C.getLocationContext(), *StreamVal); 505480093f4SDimitry Andric // Generate state for NULL return value. 506480093f4SDimitry Andric // Stream switches to OpenFailed state. 507480093f4SDimitry Andric ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(), 508480093f4SDimitry Andric C.getSValBuilder().makeNull()); 509480093f4SDimitry Andric 510480093f4SDimitry Andric StateRetNotNull = 511*5ffd83dbSDimitry Andric StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 512480093f4SDimitry Andric StateRetNull = 513*5ffd83dbSDimitry Andric StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc)); 514480093f4SDimitry Andric 515*5ffd83dbSDimitry Andric C.addTransition(StateRetNotNull, 516*5ffd83dbSDimitry Andric constructNoteTag(C, StreamSym, "Stream reopened here")); 517480093f4SDimitry Andric C.addTransition(StateRetNull); 5180b57cec5SDimitry Andric } 5190b57cec5SDimitry Andric 520*5ffd83dbSDimitry Andric void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, 521*5ffd83dbSDimitry Andric CheckerContext &C) const { 522480093f4SDimitry Andric ProgramStateRef State = C.getState(); 523*5ffd83dbSDimitry Andric SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); 524*5ffd83dbSDimitry Andric if (!Sym) 525*5ffd83dbSDimitry Andric return; 526*5ffd83dbSDimitry Andric 527*5ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(Sym); 528*5ffd83dbSDimitry Andric if (!SS) 529*5ffd83dbSDimitry Andric return; 530*5ffd83dbSDimitry Andric 531*5ffd83dbSDimitry Andric assertStreamStateOpened(SS); 532*5ffd83dbSDimitry Andric 533*5ffd83dbSDimitry Andric // Close the File Descriptor. 534*5ffd83dbSDimitry Andric // Regardless if the close fails or not, stream becomes "closed" 535*5ffd83dbSDimitry Andric // and can not be used any more. 536*5ffd83dbSDimitry Andric State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc)); 537*5ffd83dbSDimitry Andric 538480093f4SDimitry Andric C.addTransition(State); 5390b57cec5SDimitry Andric } 5400b57cec5SDimitry Andric 541*5ffd83dbSDimitry Andric void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, 542*5ffd83dbSDimitry Andric CheckerContext &C) const { 543480093f4SDimitry Andric ProgramStateRef State = C.getState(); 544*5ffd83dbSDimitry Andric SVal StreamVal = getStreamArg(Desc, Call); 545*5ffd83dbSDimitry Andric State = ensureStreamNonNull(StreamVal, C, State); 546*5ffd83dbSDimitry Andric if (!State) 547*5ffd83dbSDimitry Andric return; 548*5ffd83dbSDimitry Andric State = ensureStreamOpened(StreamVal, C, State); 549*5ffd83dbSDimitry Andric if (!State) 550*5ffd83dbSDimitry Andric return; 551*5ffd83dbSDimitry Andric State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 552*5ffd83dbSDimitry Andric if (!State) 553480093f4SDimitry Andric return; 554480093f4SDimitry Andric 555*5ffd83dbSDimitry Andric SymbolRef Sym = StreamVal.getAsSymbol(); 556*5ffd83dbSDimitry Andric if (Sym && State->get<StreamMap>(Sym)) { 557*5ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(Sym); 558*5ffd83dbSDimitry Andric if (SS->ErrorState & ErrorFEof) 559*5ffd83dbSDimitry Andric reportFEofWarning(C, State); 560*5ffd83dbSDimitry Andric } else { 561480093f4SDimitry Andric C.addTransition(State); 562*5ffd83dbSDimitry Andric } 563480093f4SDimitry Andric } 564480093f4SDimitry Andric 565*5ffd83dbSDimitry Andric void StreamChecker::preFwrite(const FnDescription *Desc, const CallEvent &Call, 566*5ffd83dbSDimitry Andric CheckerContext &C) const { 567480093f4SDimitry Andric ProgramStateRef State = C.getState(); 568*5ffd83dbSDimitry Andric SVal StreamVal = getStreamArg(Desc, Call); 569*5ffd83dbSDimitry Andric State = ensureStreamNonNull(StreamVal, C, State); 570*5ffd83dbSDimitry Andric if (!State) 571*5ffd83dbSDimitry Andric return; 572*5ffd83dbSDimitry Andric State = ensureStreamOpened(StreamVal, C, State); 573*5ffd83dbSDimitry Andric if (!State) 574*5ffd83dbSDimitry Andric return; 575*5ffd83dbSDimitry Andric State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 576*5ffd83dbSDimitry Andric if (!State) 577*5ffd83dbSDimitry Andric return; 578*5ffd83dbSDimitry Andric 579480093f4SDimitry Andric C.addTransition(State); 580480093f4SDimitry Andric } 581480093f4SDimitry Andric 582*5ffd83dbSDimitry Andric void StreamChecker::evalFreadFwrite(const FnDescription *Desc, 583*5ffd83dbSDimitry Andric const CallEvent &Call, CheckerContext &C, 584*5ffd83dbSDimitry Andric bool IsFread) const { 585*5ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 586*5ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 587*5ffd83dbSDimitry Andric if (!StreamSym) 588*5ffd83dbSDimitry Andric return; 589*5ffd83dbSDimitry Andric 590*5ffd83dbSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 591*5ffd83dbSDimitry Andric if (!CE) 592*5ffd83dbSDimitry Andric return; 593*5ffd83dbSDimitry Andric 594*5ffd83dbSDimitry Andric Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); 595*5ffd83dbSDimitry Andric if (!SizeVal) 596*5ffd83dbSDimitry Andric return; 597*5ffd83dbSDimitry Andric Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); 598*5ffd83dbSDimitry Andric if (!NMembVal) 599*5ffd83dbSDimitry Andric return; 600*5ffd83dbSDimitry Andric 601*5ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 602*5ffd83dbSDimitry Andric if (!SS) 603*5ffd83dbSDimitry Andric return; 604*5ffd83dbSDimitry Andric 605*5ffd83dbSDimitry Andric assertStreamStateOpened(SS); 606*5ffd83dbSDimitry Andric 607*5ffd83dbSDimitry Andric // C'99 standard, §7.19.8.1.3, the return value of fread: 608*5ffd83dbSDimitry Andric // The fread function returns the number of elements successfully read, which 609*5ffd83dbSDimitry Andric // may be less than nmemb if a read error or end-of-file is encountered. If 610*5ffd83dbSDimitry Andric // size or nmemb is zero, fread returns zero and the contents of the array and 611*5ffd83dbSDimitry Andric // the state of the stream remain unchanged. 612*5ffd83dbSDimitry Andric 613*5ffd83dbSDimitry Andric if (State->isNull(*SizeVal).isConstrainedTrue() || 614*5ffd83dbSDimitry Andric State->isNull(*NMembVal).isConstrainedTrue()) { 615*5ffd83dbSDimitry Andric // This is the "size or nmemb is zero" case. 616*5ffd83dbSDimitry Andric // Just return 0, do nothing more (not clear the error flags). 617*5ffd83dbSDimitry Andric State = bindInt(0, State, C, CE); 618*5ffd83dbSDimitry Andric C.addTransition(State); 619*5ffd83dbSDimitry Andric return; 620*5ffd83dbSDimitry Andric } 621*5ffd83dbSDimitry Andric 622*5ffd83dbSDimitry Andric // Generate a transition for the success state. 623*5ffd83dbSDimitry Andric // If we know the state to be FEOF at fread, do not add a success state. 624*5ffd83dbSDimitry Andric if (!IsFread || (SS->ErrorState != ErrorFEof)) { 625*5ffd83dbSDimitry Andric ProgramStateRef StateNotFailed = 626*5ffd83dbSDimitry Andric State->BindExpr(CE, C.getLocationContext(), *NMembVal); 627*5ffd83dbSDimitry Andric if (StateNotFailed) { 628*5ffd83dbSDimitry Andric StateNotFailed = StateNotFailed->set<StreamMap>( 629*5ffd83dbSDimitry Andric StreamSym, StreamState::getOpened(Desc)); 630*5ffd83dbSDimitry Andric C.addTransition(StateNotFailed); 631*5ffd83dbSDimitry Andric } 632*5ffd83dbSDimitry Andric } 633*5ffd83dbSDimitry Andric 634*5ffd83dbSDimitry Andric // Add transition for the failed state. 635*5ffd83dbSDimitry Andric Optional<NonLoc> RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 636*5ffd83dbSDimitry Andric assert(RetVal && "Value should be NonLoc."); 637*5ffd83dbSDimitry Andric ProgramStateRef StateFailed = 638*5ffd83dbSDimitry Andric State->BindExpr(CE, C.getLocationContext(), *RetVal); 639*5ffd83dbSDimitry Andric if (!StateFailed) 640*5ffd83dbSDimitry Andric return; 641*5ffd83dbSDimitry Andric auto Cond = C.getSValBuilder() 642*5ffd83dbSDimitry Andric .evalBinOpNN(State, BO_LT, *RetVal, *NMembVal, 643*5ffd83dbSDimitry Andric C.getASTContext().IntTy) 644*5ffd83dbSDimitry Andric .getAs<DefinedOrUnknownSVal>(); 645*5ffd83dbSDimitry Andric if (!Cond) 646*5ffd83dbSDimitry Andric return; 647*5ffd83dbSDimitry Andric StateFailed = StateFailed->assume(*Cond, true); 648*5ffd83dbSDimitry Andric if (!StateFailed) 649*5ffd83dbSDimitry Andric return; 650*5ffd83dbSDimitry Andric 651*5ffd83dbSDimitry Andric StreamErrorState NewES; 652*5ffd83dbSDimitry Andric if (IsFread) 653*5ffd83dbSDimitry Andric NewES = (SS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError; 654*5ffd83dbSDimitry Andric else 655*5ffd83dbSDimitry Andric NewES = ErrorFError; 656*5ffd83dbSDimitry Andric // If a (non-EOF) error occurs, the resulting value of the file position 657*5ffd83dbSDimitry Andric // indicator for the stream is indeterminate. 658*5ffd83dbSDimitry Andric StreamState NewState = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); 659*5ffd83dbSDimitry Andric StateFailed = StateFailed->set<StreamMap>(StreamSym, NewState); 660*5ffd83dbSDimitry Andric C.addTransition(StateFailed); 661*5ffd83dbSDimitry Andric } 662*5ffd83dbSDimitry Andric 663*5ffd83dbSDimitry Andric void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, 664*5ffd83dbSDimitry Andric CheckerContext &C) const { 665*5ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 666*5ffd83dbSDimitry Andric SVal StreamVal = getStreamArg(Desc, Call); 667*5ffd83dbSDimitry Andric State = ensureStreamNonNull(StreamVal, C, State); 668*5ffd83dbSDimitry Andric if (!State) 669*5ffd83dbSDimitry Andric return; 670*5ffd83dbSDimitry Andric State = ensureStreamOpened(StreamVal, C, State); 671*5ffd83dbSDimitry Andric if (!State) 672*5ffd83dbSDimitry Andric return; 673*5ffd83dbSDimitry Andric State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); 674*5ffd83dbSDimitry Andric if (!State) 675*5ffd83dbSDimitry Andric return; 676*5ffd83dbSDimitry Andric 677*5ffd83dbSDimitry Andric C.addTransition(State); 678*5ffd83dbSDimitry Andric } 679*5ffd83dbSDimitry Andric 680*5ffd83dbSDimitry Andric void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, 681*5ffd83dbSDimitry Andric CheckerContext &C) const { 682*5ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 683*5ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 684*5ffd83dbSDimitry Andric if (!StreamSym) 685*5ffd83dbSDimitry Andric return; 686*5ffd83dbSDimitry Andric 687*5ffd83dbSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 688*5ffd83dbSDimitry Andric if (!CE) 689*5ffd83dbSDimitry Andric return; 690*5ffd83dbSDimitry Andric 691*5ffd83dbSDimitry Andric // Ignore the call if the stream is not tracked. 692*5ffd83dbSDimitry Andric if (!State->get<StreamMap>(StreamSym)) 693*5ffd83dbSDimitry Andric return; 694*5ffd83dbSDimitry Andric 695*5ffd83dbSDimitry Andric DefinedSVal RetVal = makeRetVal(C, CE); 696*5ffd83dbSDimitry Andric 697*5ffd83dbSDimitry Andric // Make expression result. 698*5ffd83dbSDimitry Andric State = State->BindExpr(CE, C.getLocationContext(), RetVal); 699*5ffd83dbSDimitry Andric 700*5ffd83dbSDimitry Andric // Bifurcate the state into failed and non-failed. 701*5ffd83dbSDimitry Andric // Return zero on success, nonzero on error. 702*5ffd83dbSDimitry Andric ProgramStateRef StateNotFailed, StateFailed; 703*5ffd83dbSDimitry Andric std::tie(StateFailed, StateNotFailed) = 704*5ffd83dbSDimitry Andric C.getConstraintManager().assumeDual(State, RetVal); 705*5ffd83dbSDimitry Andric 706*5ffd83dbSDimitry Andric // Reset the state to opened with no error. 707*5ffd83dbSDimitry Andric StateNotFailed = 708*5ffd83dbSDimitry Andric StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 709*5ffd83dbSDimitry Andric // We get error. 710*5ffd83dbSDimitry Andric // It is possible that fseek fails but sets none of the error flags. 711*5ffd83dbSDimitry Andric // If fseek failed, assume that the file position becomes indeterminate in any 712*5ffd83dbSDimitry Andric // case. 713*5ffd83dbSDimitry Andric StateFailed = StateFailed->set<StreamMap>( 714*5ffd83dbSDimitry Andric StreamSym, 715*5ffd83dbSDimitry Andric StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true)); 716*5ffd83dbSDimitry Andric 717*5ffd83dbSDimitry Andric C.addTransition(StateNotFailed); 718*5ffd83dbSDimitry Andric C.addTransition(StateFailed); 719*5ffd83dbSDimitry Andric } 720*5ffd83dbSDimitry Andric 721*5ffd83dbSDimitry Andric void StreamChecker::evalClearerr(const FnDescription *Desc, 722*5ffd83dbSDimitry Andric const CallEvent &Call, 723*5ffd83dbSDimitry Andric CheckerContext &C) const { 724*5ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 725*5ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 726*5ffd83dbSDimitry Andric if (!StreamSym) 727*5ffd83dbSDimitry Andric return; 728*5ffd83dbSDimitry Andric 729*5ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 730*5ffd83dbSDimitry Andric if (!SS) 731*5ffd83dbSDimitry Andric return; 732*5ffd83dbSDimitry Andric 733*5ffd83dbSDimitry Andric assertStreamStateOpened(SS); 734*5ffd83dbSDimitry Andric 735*5ffd83dbSDimitry Andric // FilePositionIndeterminate is not cleared. 736*5ffd83dbSDimitry Andric State = State->set<StreamMap>( 737*5ffd83dbSDimitry Andric StreamSym, 738*5ffd83dbSDimitry Andric StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate)); 739*5ffd83dbSDimitry Andric C.addTransition(State); 740*5ffd83dbSDimitry Andric } 741*5ffd83dbSDimitry Andric 742*5ffd83dbSDimitry Andric void StreamChecker::evalFeofFerror(const FnDescription *Desc, 743*5ffd83dbSDimitry Andric const CallEvent &Call, CheckerContext &C, 744*5ffd83dbSDimitry Andric const StreamErrorState &ErrorKind) const { 745*5ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 746*5ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 747*5ffd83dbSDimitry Andric if (!StreamSym) 748*5ffd83dbSDimitry Andric return; 749*5ffd83dbSDimitry Andric 750*5ffd83dbSDimitry Andric const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 751*5ffd83dbSDimitry Andric if (!CE) 752*5ffd83dbSDimitry Andric return; 753*5ffd83dbSDimitry Andric 754*5ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 755*5ffd83dbSDimitry Andric if (!SS) 756*5ffd83dbSDimitry Andric return; 757*5ffd83dbSDimitry Andric 758*5ffd83dbSDimitry Andric assertStreamStateOpened(SS); 759*5ffd83dbSDimitry Andric 760*5ffd83dbSDimitry Andric if (SS->ErrorState & ErrorKind) { 761*5ffd83dbSDimitry Andric // Execution path with error of ErrorKind. 762*5ffd83dbSDimitry Andric // Function returns true. 763*5ffd83dbSDimitry Andric // From now on it is the only one error state. 764*5ffd83dbSDimitry Andric ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE); 765*5ffd83dbSDimitry Andric C.addTransition(TrueState->set<StreamMap>( 766*5ffd83dbSDimitry Andric StreamSym, StreamState::getOpened(Desc, ErrorKind, 767*5ffd83dbSDimitry Andric SS->FilePositionIndeterminate && 768*5ffd83dbSDimitry Andric !ErrorKind.isFEof()))); 769*5ffd83dbSDimitry Andric } 770*5ffd83dbSDimitry Andric if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) { 771*5ffd83dbSDimitry Andric // Execution path(s) with ErrorKind not set. 772*5ffd83dbSDimitry Andric // Function returns false. 773*5ffd83dbSDimitry Andric // New error state is everything before minus ErrorKind. 774*5ffd83dbSDimitry Andric ProgramStateRef FalseState = bindInt(0, State, C, CE); 775*5ffd83dbSDimitry Andric C.addTransition(FalseState->set<StreamMap>( 776*5ffd83dbSDimitry Andric StreamSym, 777*5ffd83dbSDimitry Andric StreamState::getOpened( 778*5ffd83dbSDimitry Andric Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof()))); 779*5ffd83dbSDimitry Andric } 780*5ffd83dbSDimitry Andric } 781*5ffd83dbSDimitry Andric 782*5ffd83dbSDimitry Andric void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, 783*5ffd83dbSDimitry Andric CheckerContext &C) const { 784*5ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 785*5ffd83dbSDimitry Andric SVal StreamVal = getStreamArg(Desc, Call); 786*5ffd83dbSDimitry Andric State = ensureStreamNonNull(StreamVal, C, State); 787*5ffd83dbSDimitry Andric if (!State) 788*5ffd83dbSDimitry Andric return; 789*5ffd83dbSDimitry Andric State = ensureStreamOpened(StreamVal, C, State); 790*5ffd83dbSDimitry Andric if (!State) 791*5ffd83dbSDimitry Andric return; 792*5ffd83dbSDimitry Andric 793*5ffd83dbSDimitry Andric C.addTransition(State); 794*5ffd83dbSDimitry Andric } 795*5ffd83dbSDimitry Andric 796*5ffd83dbSDimitry Andric void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, 797*5ffd83dbSDimitry Andric const CallEvent &Call, CheckerContext &C, 798*5ffd83dbSDimitry Andric const StreamErrorState &ErrorKind) const { 799*5ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 800*5ffd83dbSDimitry Andric SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 801*5ffd83dbSDimitry Andric assert(StreamSym && "Operation not permitted on non-symbolic stream value."); 802*5ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(StreamSym); 803*5ffd83dbSDimitry Andric assert(SS && "Stream should be tracked by the checker."); 804*5ffd83dbSDimitry Andric State = State->set<StreamMap>( 805*5ffd83dbSDimitry Andric StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind)); 806*5ffd83dbSDimitry Andric C.addTransition(State); 807*5ffd83dbSDimitry Andric } 808*5ffd83dbSDimitry Andric 809*5ffd83dbSDimitry Andric ProgramStateRef 810*5ffd83dbSDimitry Andric StreamChecker::ensureStreamNonNull(SVal StreamVal, CheckerContext &C, 811*5ffd83dbSDimitry Andric ProgramStateRef State) const { 812*5ffd83dbSDimitry Andric auto Stream = StreamVal.getAs<DefinedSVal>(); 813*5ffd83dbSDimitry Andric if (!Stream) 814*5ffd83dbSDimitry Andric return State; 815480093f4SDimitry Andric 816480093f4SDimitry Andric ConstraintManager &CM = C.getConstraintManager(); 817*5ffd83dbSDimitry Andric 818480093f4SDimitry Andric ProgramStateRef StateNotNull, StateNull; 819*5ffd83dbSDimitry Andric std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream); 820480093f4SDimitry Andric 821480093f4SDimitry Andric if (!StateNotNull && StateNull) { 822480093f4SDimitry Andric if (ExplodedNode *N = C.generateErrorNode(StateNull)) { 823480093f4SDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 824*5ffd83dbSDimitry Andric BT_FileNull, "Stream pointer might be NULL.", N)); 825480093f4SDimitry Andric } 826*5ffd83dbSDimitry Andric return nullptr; 827480093f4SDimitry Andric } 828480093f4SDimitry Andric 829*5ffd83dbSDimitry Andric return StateNotNull; 830480093f4SDimitry Andric } 831480093f4SDimitry Andric 832*5ffd83dbSDimitry Andric ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, 833*5ffd83dbSDimitry Andric CheckerContext &C, 834*5ffd83dbSDimitry Andric ProgramStateRef State) const { 835*5ffd83dbSDimitry Andric SymbolRef Sym = StreamVal.getAsSymbol(); 836*5ffd83dbSDimitry Andric if (!Sym) 837*5ffd83dbSDimitry Andric return State; 838*5ffd83dbSDimitry Andric 839*5ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(Sym); 840*5ffd83dbSDimitry Andric if (!SS) 841*5ffd83dbSDimitry Andric return State; 842*5ffd83dbSDimitry Andric 843*5ffd83dbSDimitry Andric if (SS->isClosed()) { 844*5ffd83dbSDimitry Andric // Using a stream pointer after 'fclose' causes undefined behavior 845*5ffd83dbSDimitry Andric // according to cppreference.com . 846*5ffd83dbSDimitry Andric ExplodedNode *N = C.generateErrorNode(); 847*5ffd83dbSDimitry Andric if (N) { 848*5ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 849*5ffd83dbSDimitry Andric BT_UseAfterClose, 850*5ffd83dbSDimitry Andric "Stream might be already closed. Causes undefined behaviour.", N)); 851*5ffd83dbSDimitry Andric return nullptr; 852480093f4SDimitry Andric } 853480093f4SDimitry Andric 854*5ffd83dbSDimitry Andric return State; 855*5ffd83dbSDimitry Andric } 856*5ffd83dbSDimitry Andric 857*5ffd83dbSDimitry Andric if (SS->isOpenFailed()) { 858*5ffd83dbSDimitry Andric // Using a stream that has failed to open is likely to cause problems. 859*5ffd83dbSDimitry Andric // This should usually not occur because stream pointer is NULL. 860*5ffd83dbSDimitry Andric // But freopen can cause a state when stream pointer remains non-null but 861*5ffd83dbSDimitry Andric // failed to open. 862*5ffd83dbSDimitry Andric ExplodedNode *N = C.generateErrorNode(); 863*5ffd83dbSDimitry Andric if (N) { 864*5ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 865*5ffd83dbSDimitry Andric BT_UseAfterOpenFailed, 866*5ffd83dbSDimitry Andric "Stream might be invalid after " 867*5ffd83dbSDimitry Andric "(re-)opening it has failed. " 868*5ffd83dbSDimitry Andric "Can cause undefined behaviour.", 869*5ffd83dbSDimitry Andric N)); 870*5ffd83dbSDimitry Andric return nullptr; 871*5ffd83dbSDimitry Andric } 872*5ffd83dbSDimitry Andric return State; 873*5ffd83dbSDimitry Andric } 874*5ffd83dbSDimitry Andric 875*5ffd83dbSDimitry Andric return State; 876*5ffd83dbSDimitry Andric } 877*5ffd83dbSDimitry Andric 878*5ffd83dbSDimitry Andric ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate( 879*5ffd83dbSDimitry Andric SVal StreamVal, CheckerContext &C, ProgramStateRef State) const { 880*5ffd83dbSDimitry Andric static const char *BugMessage = 881*5ffd83dbSDimitry Andric "File position of the stream might be 'indeterminate' " 882*5ffd83dbSDimitry Andric "after a failed operation. " 883*5ffd83dbSDimitry Andric "Can cause undefined behavior."; 884*5ffd83dbSDimitry Andric 885*5ffd83dbSDimitry Andric SymbolRef Sym = StreamVal.getAsSymbol(); 886*5ffd83dbSDimitry Andric if (!Sym) 887*5ffd83dbSDimitry Andric return State; 888*5ffd83dbSDimitry Andric 889*5ffd83dbSDimitry Andric const StreamState *SS = State->get<StreamMap>(Sym); 890*5ffd83dbSDimitry Andric if (!SS) 891*5ffd83dbSDimitry Andric return State; 892*5ffd83dbSDimitry Andric 893*5ffd83dbSDimitry Andric assert(SS->isOpened() && "First ensure that stream is opened."); 894*5ffd83dbSDimitry Andric 895*5ffd83dbSDimitry Andric if (SS->FilePositionIndeterminate) { 896*5ffd83dbSDimitry Andric if (SS->ErrorState & ErrorFEof) { 897*5ffd83dbSDimitry Andric // The error is unknown but may be FEOF. 898*5ffd83dbSDimitry Andric // Continue analysis with the FEOF error state. 899*5ffd83dbSDimitry Andric // Report warning because the other possible error states. 900*5ffd83dbSDimitry Andric ExplodedNode *N = C.generateNonFatalErrorNode(State); 901*5ffd83dbSDimitry Andric if (!N) 902*5ffd83dbSDimitry Andric return nullptr; 903*5ffd83dbSDimitry Andric 904*5ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 905*5ffd83dbSDimitry Andric BT_IndeterminatePosition, BugMessage, N)); 906*5ffd83dbSDimitry Andric return State->set<StreamMap>( 907*5ffd83dbSDimitry Andric Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false)); 908*5ffd83dbSDimitry Andric } 909*5ffd83dbSDimitry Andric 910*5ffd83dbSDimitry Andric // Known or unknown error state without FEOF possible. 911*5ffd83dbSDimitry Andric // Stop analysis, report error. 912*5ffd83dbSDimitry Andric ExplodedNode *N = C.generateErrorNode(State); 913*5ffd83dbSDimitry Andric if (N) 914*5ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 915*5ffd83dbSDimitry Andric BT_IndeterminatePosition, BugMessage, N)); 916*5ffd83dbSDimitry Andric 917*5ffd83dbSDimitry Andric return nullptr; 918*5ffd83dbSDimitry Andric } 919*5ffd83dbSDimitry Andric 920*5ffd83dbSDimitry Andric return State; 921*5ffd83dbSDimitry Andric } 922*5ffd83dbSDimitry Andric 923*5ffd83dbSDimitry Andric ProgramStateRef 924*5ffd83dbSDimitry Andric StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 925*5ffd83dbSDimitry Andric ProgramStateRef State) const { 926*5ffd83dbSDimitry Andric Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>(); 9270b57cec5SDimitry Andric if (!CI) 928*5ffd83dbSDimitry Andric return State; 9290b57cec5SDimitry Andric 930480093f4SDimitry Andric int64_t X = CI->getValue().getSExtValue(); 931480093f4SDimitry Andric if (X >= 0 && X <= 2) 932*5ffd83dbSDimitry Andric return State; 9330b57cec5SDimitry Andric 934480093f4SDimitry Andric if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 935*5ffd83dbSDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 936*5ffd83dbSDimitry Andric BT_IllegalWhence, 9370b57cec5SDimitry Andric "The whence argument to fseek() should be " 938*5ffd83dbSDimitry Andric "SEEK_SET, SEEK_END, or SEEK_CUR.", 939*5ffd83dbSDimitry Andric N)); 940*5ffd83dbSDimitry Andric return nullptr; 941*5ffd83dbSDimitry Andric } 942*5ffd83dbSDimitry Andric 943*5ffd83dbSDimitry Andric return State; 944*5ffd83dbSDimitry Andric } 945*5ffd83dbSDimitry Andric 946*5ffd83dbSDimitry Andric void StreamChecker::reportFEofWarning(CheckerContext &C, 947*5ffd83dbSDimitry Andric ProgramStateRef State) const { 948*5ffd83dbSDimitry Andric if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 949a7dea167SDimitry Andric C.emitReport(std::make_unique<PathSensitiveBugReport>( 950*5ffd83dbSDimitry Andric BT_StreamEof, 951*5ffd83dbSDimitry Andric "Read function called when stream is in EOF state. " 952*5ffd83dbSDimitry Andric "Function has no effect.", 953*5ffd83dbSDimitry Andric N)); 954*5ffd83dbSDimitry Andric return; 9550b57cec5SDimitry Andric } 956*5ffd83dbSDimitry Andric C.addTransition(State); 9570b57cec5SDimitry Andric } 9580b57cec5SDimitry Andric 9590b57cec5SDimitry Andric void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 9600b57cec5SDimitry Andric CheckerContext &C) const { 961480093f4SDimitry Andric ProgramStateRef State = C.getState(); 9620b57cec5SDimitry Andric 9630b57cec5SDimitry Andric // TODO: Clean up the state. 964480093f4SDimitry Andric const StreamMapTy &Map = State->get<StreamMap>(); 9650b57cec5SDimitry Andric for (const auto &I : Map) { 9660b57cec5SDimitry Andric SymbolRef Sym = I.first; 9670b57cec5SDimitry Andric const StreamState &SS = I.second; 9680b57cec5SDimitry Andric if (!SymReaper.isDead(Sym) || !SS.isOpened()) 9690b57cec5SDimitry Andric continue; 9700b57cec5SDimitry Andric 9710b57cec5SDimitry Andric ExplodedNode *N = C.generateErrorNode(); 9720b57cec5SDimitry Andric if (!N) 973480093f4SDimitry Andric continue; 9740b57cec5SDimitry Andric 975*5ffd83dbSDimitry Andric // Do not warn for non-closed stream at program exit. 976*5ffd83dbSDimitry Andric ExplodedNode *Pred = C.getPredecessor(); 977*5ffd83dbSDimitry Andric if (Pred && Pred->getCFGBlock() && 978*5ffd83dbSDimitry Andric Pred->getCFGBlock()->hasNoReturnElement()) 979*5ffd83dbSDimitry Andric continue; 980*5ffd83dbSDimitry Andric 981*5ffd83dbSDimitry Andric // Resource leaks can result in multiple warning that describe the same kind 982*5ffd83dbSDimitry Andric // of programming error: 983*5ffd83dbSDimitry Andric // void f() { 984*5ffd83dbSDimitry Andric // FILE *F = fopen("a.txt"); 985*5ffd83dbSDimitry Andric // if (rand()) // state split 986*5ffd83dbSDimitry Andric // return; // warning 987*5ffd83dbSDimitry Andric // } // warning 988*5ffd83dbSDimitry Andric // While this isn't necessarily true (leaking the same stream could result 989*5ffd83dbSDimitry Andric // from a different kinds of errors), the reduction in redundant reports 990*5ffd83dbSDimitry Andric // makes this a worthwhile heuristic. 991*5ffd83dbSDimitry Andric // FIXME: Add a checker option to turn this uniqueing feature off. 992*5ffd83dbSDimitry Andric 993*5ffd83dbSDimitry Andric const ExplodedNode *StreamOpenNode = getAcquisitionSite(N, Sym, C); 994*5ffd83dbSDimitry Andric assert(StreamOpenNode && "Could not find place of stream opening."); 995*5ffd83dbSDimitry Andric PathDiagnosticLocation LocUsedForUniqueing = 996*5ffd83dbSDimitry Andric PathDiagnosticLocation::createBegin( 997*5ffd83dbSDimitry Andric StreamOpenNode->getStmtForDiagnostics(), C.getSourceManager(), 998*5ffd83dbSDimitry Andric StreamOpenNode->getLocationContext()); 999*5ffd83dbSDimitry Andric 1000*5ffd83dbSDimitry Andric std::unique_ptr<PathSensitiveBugReport> R = 1001*5ffd83dbSDimitry Andric std::make_unique<PathSensitiveBugReport>( 1002*5ffd83dbSDimitry Andric BT_ResourceLeak, 1003*5ffd83dbSDimitry Andric "Opened stream never closed. Potential resource leak.", N, 1004*5ffd83dbSDimitry Andric LocUsedForUniqueing, 1005*5ffd83dbSDimitry Andric StreamOpenNode->getLocationContext()->getDecl()); 1006*5ffd83dbSDimitry Andric R->markInteresting(Sym); 1007*5ffd83dbSDimitry Andric C.emitReport(std::move(R)); 10080b57cec5SDimitry Andric } 10090b57cec5SDimitry Andric } 10100b57cec5SDimitry Andric 1011*5ffd83dbSDimitry Andric ProgramStateRef StreamChecker::checkPointerEscape( 1012*5ffd83dbSDimitry Andric ProgramStateRef State, const InvalidatedSymbols &Escaped, 1013*5ffd83dbSDimitry Andric const CallEvent *Call, PointerEscapeKind Kind) const { 1014*5ffd83dbSDimitry Andric // Check for file-handling system call that is not handled by the checker. 1015*5ffd83dbSDimitry Andric // FIXME: The checker should be updated to handle all system calls that take 1016*5ffd83dbSDimitry Andric // 'FILE*' argument. These are now ignored. 1017*5ffd83dbSDimitry Andric if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader()) 1018*5ffd83dbSDimitry Andric return State; 1019*5ffd83dbSDimitry Andric 1020*5ffd83dbSDimitry Andric for (SymbolRef Sym : Escaped) { 1021*5ffd83dbSDimitry Andric // The symbol escaped. 1022*5ffd83dbSDimitry Andric // From now the stream can be manipulated in unknown way to the checker, 1023*5ffd83dbSDimitry Andric // it is not possible to handle it any more. 1024*5ffd83dbSDimitry Andric // Optimistically, assume that the corresponding file handle will be closed 1025*5ffd83dbSDimitry Andric // somewhere else. 1026*5ffd83dbSDimitry Andric // Remove symbol from state so the following stream calls on this symbol are 1027*5ffd83dbSDimitry Andric // not handled by the checker. 1028*5ffd83dbSDimitry Andric State = State->remove<StreamMap>(Sym); 1029*5ffd83dbSDimitry Andric } 1030*5ffd83dbSDimitry Andric return State; 10310b57cec5SDimitry Andric } 10320b57cec5SDimitry Andric 1033*5ffd83dbSDimitry Andric void ento::registerStreamChecker(CheckerManager &Mgr) { 1034*5ffd83dbSDimitry Andric Mgr.registerChecker<StreamChecker>(); 1035*5ffd83dbSDimitry Andric } 1036*5ffd83dbSDimitry Andric 1037*5ffd83dbSDimitry Andric bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) { 1038*5ffd83dbSDimitry Andric return true; 1039*5ffd83dbSDimitry Andric } 1040*5ffd83dbSDimitry Andric 1041*5ffd83dbSDimitry Andric void ento::registerStreamTesterChecker(CheckerManager &Mgr) { 1042*5ffd83dbSDimitry Andric auto *Checker = Mgr.getChecker<StreamChecker>(); 1043*5ffd83dbSDimitry Andric Checker->TestMode = true; 1044*5ffd83dbSDimitry Andric } 1045*5ffd83dbSDimitry Andric 1046*5ffd83dbSDimitry Andric bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) { 10470b57cec5SDimitry Andric return true; 10480b57cec5SDimitry Andric } 1049