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