1 //===-- StreamChecker.cpp -----------------------------------------*- C++ -*--// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines checkers that model and check stream handling functions. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 14 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 15 #include "clang/StaticAnalyzer/Core/Checker.h" 16 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 17 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" 18 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 22 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 23 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 24 #include "llvm/ADT/Sequence.h" 25 #include <functional> 26 #include <optional> 27 28 using namespace clang; 29 using namespace ento; 30 using namespace std::placeholders; 31 32 //===----------------------------------------------------------------------===// 33 // Definition of state data structures. 34 //===----------------------------------------------------------------------===// 35 36 namespace { 37 38 struct FnDescription; 39 40 /// State of the stream error flags. 41 /// Sometimes it is not known to the checker what error flags are set. 42 /// This is indicated by setting more than one flag to true. 43 /// This is an optimization to avoid state splits. 44 /// A stream can either be in FEOF or FERROR but not both at the same time. 45 /// Multiple flags are set to handle the corresponding states together. 46 struct StreamErrorState { 47 /// The stream can be in state where none of the error flags set. 48 bool NoError = true; 49 /// The stream can be in state where the EOF indicator is set. 50 bool FEof = false; 51 /// The stream can be in state where the error indicator is set. 52 bool FError = false; 53 54 bool isNoError() const { return NoError && !FEof && !FError; } 55 bool isFEof() const { return !NoError && FEof && !FError; } 56 bool isFError() const { return !NoError && !FEof && FError; } 57 58 bool operator==(const StreamErrorState &ES) const { 59 return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError; 60 } 61 62 bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); } 63 64 StreamErrorState operator|(const StreamErrorState &E) const { 65 return {NoError || E.NoError, FEof || E.FEof, FError || E.FError}; 66 } 67 68 StreamErrorState operator&(const StreamErrorState &E) const { 69 return {NoError && E.NoError, FEof && E.FEof, FError && E.FError}; 70 } 71 72 StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; } 73 74 /// Returns if the StreamErrorState is a valid object. 75 operator bool() const { return NoError || FEof || FError; } 76 77 void Profile(llvm::FoldingSetNodeID &ID) const { 78 ID.AddBoolean(NoError); 79 ID.AddBoolean(FEof); 80 ID.AddBoolean(FError); 81 } 82 }; 83 84 const StreamErrorState ErrorNone{true, false, false}; 85 const StreamErrorState ErrorFEof{false, true, false}; 86 const StreamErrorState ErrorFError{false, false, true}; 87 88 /// Full state information about a stream pointer. 89 struct StreamState { 90 /// The last file operation called in the stream. 91 /// Can be nullptr. 92 const FnDescription *LastOperation; 93 94 /// State of a stream symbol. 95 enum KindTy { 96 Opened, /// Stream is opened. 97 Closed, /// Closed stream (an invalid stream pointer after it was closed). 98 OpenFailed /// The last open operation has failed. 99 } State; 100 101 /// State of the error flags. 102 /// Ignored in non-opened stream state but must be NoError. 103 StreamErrorState const ErrorState; 104 105 /// Indicate if the file has an "indeterminate file position indicator". 106 /// This can be set at a failing read or write or seek operation. 107 /// If it is set no more read or write is allowed. 108 /// This value is not dependent on the stream error flags: 109 /// The error flag may be cleared with `clearerr` but the file position 110 /// remains still indeterminate. 111 /// This value applies to all error states in ErrorState except FEOF. 112 /// An EOF+indeterminate state is the same as EOF state. 113 bool const FilePositionIndeterminate = false; 114 115 StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES, 116 bool IsFilePositionIndeterminate) 117 : LastOperation(L), State(S), ErrorState(ES), 118 FilePositionIndeterminate(IsFilePositionIndeterminate) { 119 assert((!ES.isFEof() || !IsFilePositionIndeterminate) && 120 "FilePositionIndeterminate should be false in FEof case."); 121 assert((State == Opened || ErrorState.isNoError()) && 122 "ErrorState should be None in non-opened stream state."); 123 } 124 125 bool isOpened() const { return State == Opened; } 126 bool isClosed() const { return State == Closed; } 127 bool isOpenFailed() const { return State == OpenFailed; } 128 129 bool operator==(const StreamState &X) const { 130 // In not opened state error state should always NoError, so comparison 131 // here is no problem. 132 return LastOperation == X.LastOperation && State == X.State && 133 ErrorState == X.ErrorState && 134 FilePositionIndeterminate == X.FilePositionIndeterminate; 135 } 136 137 static StreamState getOpened(const FnDescription *L, 138 const StreamErrorState &ES = ErrorNone, 139 bool IsFilePositionIndeterminate = false) { 140 return StreamState{L, Opened, ES, IsFilePositionIndeterminate}; 141 } 142 static StreamState getClosed(const FnDescription *L) { 143 return StreamState{L, Closed, {}, false}; 144 } 145 static StreamState getOpenFailed(const FnDescription *L) { 146 return StreamState{L, OpenFailed, {}, false}; 147 } 148 149 void Profile(llvm::FoldingSetNodeID &ID) const { 150 ID.AddPointer(LastOperation); 151 ID.AddInteger(State); 152 ErrorState.Profile(ID); 153 ID.AddBoolean(FilePositionIndeterminate); 154 } 155 }; 156 157 } // namespace 158 159 // This map holds the state of a stream. 160 // The stream is identified with a SymbolRef that is created when a stream 161 // opening function is modeled by the checker. 162 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 163 164 //===----------------------------------------------------------------------===// 165 // StreamChecker class and utility functions. 166 //===----------------------------------------------------------------------===// 167 168 namespace { 169 170 class StreamChecker; 171 using FnCheck = std::function<void(const StreamChecker *, const FnDescription *, 172 const CallEvent &, CheckerContext &)>; 173 174 using ArgNoTy = unsigned int; 175 static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max(); 176 177 const char *FeofNote = "Assuming stream reaches end-of-file here"; 178 const char *FerrorNote = "Assuming this stream operation fails"; 179 180 struct FnDescription { 181 FnCheck PreFn; 182 FnCheck EvalFn; 183 ArgNoTy StreamArgNo; 184 }; 185 186 /// Get the value of the stream argument out of the passed call event. 187 /// The call should contain a function that is described by Desc. 188 SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) { 189 assert(Desc && Desc->StreamArgNo != ArgNone && 190 "Try to get a non-existing stream argument."); 191 return Call.getArgSVal(Desc->StreamArgNo); 192 } 193 194 /// Create a conjured symbol return value for a call expression. 195 DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) { 196 assert(CE && "Expecting a call expression."); 197 198 const LocationContext *LCtx = C.getLocationContext(); 199 return C.getSValBuilder() 200 .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()) 201 .castAs<DefinedSVal>(); 202 } 203 204 ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C, 205 const CallExpr *CE) { 206 DefinedSVal RetVal = makeRetVal(C, CE); 207 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 208 State = State->assume(RetVal, true); 209 assert(State && "Assumption on new value should not fail."); 210 return State; 211 } 212 213 ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, 214 CheckerContext &C, const CallExpr *CE) { 215 State = State->BindExpr(CE, C.getLocationContext(), 216 C.getSValBuilder().makeIntVal(Value, CE->getType())); 217 return State; 218 } 219 220 inline void assertStreamStateOpened(const StreamState *SS) { 221 assert(SS->isOpened() && "Stream is expected to be opened"); 222 } 223 224 class StreamChecker : public Checker<check::PreCall, eval::Call, 225 check::DeadSymbols, check::PointerEscape> { 226 BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"}; 227 BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"}; 228 BugType BT_UseAfterOpenFailed{this, "Invalid stream", 229 "Stream handling error"}; 230 BugType BT_IndeterminatePosition{this, "Invalid stream state", 231 "Stream handling error"}; 232 BugType BT_IllegalWhence{this, "Illegal whence argument", 233 "Stream handling error"}; 234 BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"}; 235 BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error", 236 /*SuppressOnSink =*/true}; 237 238 public: 239 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 240 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 241 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 242 ProgramStateRef checkPointerEscape(ProgramStateRef State, 243 const InvalidatedSymbols &Escaped, 244 const CallEvent *Call, 245 PointerEscapeKind Kind) const; 246 247 const BugType *getBT_StreamEof() const { return &BT_StreamEof; } 248 const BugType *getBT_IndeterminatePosition() const { 249 return &BT_IndeterminatePosition; 250 } 251 252 const NoteTag *constructSetEofNoteTag(CheckerContext &C, 253 SymbolRef StreamSym) const { 254 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) { 255 if (!BR.isInteresting(StreamSym) || 256 &BR.getBugType() != this->getBT_StreamEof()) 257 return ""; 258 259 BR.markNotInteresting(StreamSym); 260 261 return FeofNote; 262 }); 263 } 264 265 const NoteTag *constructSetErrorNoteTag(CheckerContext &C, 266 SymbolRef StreamSym) const { 267 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) { 268 if (!BR.isInteresting(StreamSym) || 269 &BR.getBugType() != this->getBT_IndeterminatePosition()) 270 return ""; 271 272 BR.markNotInteresting(StreamSym); 273 274 return FerrorNote; 275 }); 276 } 277 278 const NoteTag *constructSetEofOrErrorNoteTag(CheckerContext &C, 279 SymbolRef StreamSym) const { 280 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) { 281 if (!BR.isInteresting(StreamSym)) 282 return ""; 283 284 if (&BR.getBugType() == this->getBT_StreamEof()) { 285 BR.markNotInteresting(StreamSym); 286 return FeofNote; 287 } 288 if (&BR.getBugType() == this->getBT_IndeterminatePosition()) { 289 BR.markNotInteresting(StreamSym); 290 return FerrorNote; 291 } 292 293 return ""; 294 }); 295 } 296 297 /// If true, evaluate special testing stream functions. 298 bool TestMode = false; 299 300 /// If true, generate failure branches for cases that are often not checked. 301 bool PedanticMode = false; 302 303 private: 304 CallDescriptionMap<FnDescription> FnDescriptions = { 305 {{CDM::CLibrary, {"fopen"}, 2}, 306 {nullptr, &StreamChecker::evalFopen, ArgNone}}, 307 {{CDM::CLibrary, {"fdopen"}, 2}, 308 {nullptr, &StreamChecker::evalFopen, ArgNone}}, 309 {{CDM::CLibrary, {"freopen"}, 3}, 310 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, 311 {{CDM::CLibrary, {"tmpfile"}, 0}, 312 {nullptr, &StreamChecker::evalFopen, ArgNone}}, 313 {{CDM::CLibrary, {"fclose"}, 1}, 314 {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, 315 {{CDM::CLibrary, {"fread"}, 4}, 316 {&StreamChecker::preRead, 317 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, 318 {{CDM::CLibrary, {"fwrite"}, 4}, 319 {&StreamChecker::preWrite, 320 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, 321 {{CDM::CLibrary, {"fgetc"}, 1}, 322 {&StreamChecker::preRead, 323 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}}, 324 {{CDM::CLibrary, {"fgets"}, 3}, 325 {&StreamChecker::preRead, 326 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}}, 327 {{CDM::CLibrary, {"getc"}, 1}, 328 {&StreamChecker::preRead, 329 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}}, 330 {{CDM::CLibrary, {"fputc"}, 2}, 331 {&StreamChecker::preWrite, 332 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}}, 333 {{CDM::CLibrary, {"fputs"}, 2}, 334 {&StreamChecker::preWrite, 335 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}}, 336 {{CDM::CLibrary, {"putc"}, 2}, 337 {&StreamChecker::preWrite, 338 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}}, 339 {{CDM::CLibrary, {"fprintf"}}, 340 {&StreamChecker::preWrite, 341 std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}}, 342 {{CDM::CLibrary, {"vfprintf"}, 3}, 343 {&StreamChecker::preWrite, 344 std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}}, 345 {{CDM::CLibrary, {"fscanf"}}, 346 {&StreamChecker::preRead, 347 std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}}, 348 {{CDM::CLibrary, {"vfscanf"}, 3}, 349 {&StreamChecker::preRead, 350 std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}}, 351 {{CDM::CLibrary, {"ungetc"}, 2}, 352 {&StreamChecker::preWrite, 353 std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}}, 354 {{CDM::CLibrary, {"getdelim"}, 4}, 355 {&StreamChecker::preRead, 356 std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 3}}, 357 {{CDM::CLibrary, {"getline"}, 3}, 358 {&StreamChecker::preRead, 359 std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 2}}, 360 {{CDM::CLibrary, {"fseek"}, 3}, 361 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 362 {{CDM::CLibrary, {"fseeko"}, 3}, 363 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 364 {{CDM::CLibrary, {"ftell"}, 1}, 365 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}}, 366 {{CDM::CLibrary, {"ftello"}, 1}, 367 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}}, 368 {{CDM::CLibrary, {"fflush"}, 1}, 369 {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}}, 370 {{CDM::CLibrary, {"rewind"}, 1}, 371 {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}}, 372 {{CDM::CLibrary, {"fgetpos"}, 2}, 373 {&StreamChecker::preWrite, &StreamChecker::evalFgetpos, 0}}, 374 {{CDM::CLibrary, {"fsetpos"}, 2}, 375 {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}}, 376 {{CDM::CLibrary, {"clearerr"}, 1}, 377 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, 378 {{CDM::CLibrary, {"feof"}, 1}, 379 {&StreamChecker::preDefault, 380 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), 381 0}}, 382 {{CDM::CLibrary, {"ferror"}, 1}, 383 {&StreamChecker::preDefault, 384 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError), 385 0}}, 386 {{CDM::CLibrary, {"fileno"}, 1}, 387 {&StreamChecker::preDefault, &StreamChecker::evalFileno, 0}}, 388 }; 389 390 CallDescriptionMap<FnDescription> FnTestDescriptions = { 391 {{{"StreamTesterChecker_make_feof_stream"}, 1}, 392 {nullptr, 393 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof, 394 false), 395 0}}, 396 {{{"StreamTesterChecker_make_ferror_stream"}, 1}, 397 {nullptr, 398 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, 399 ErrorFError, false), 400 0}}, 401 {{{"StreamTesterChecker_make_ferror_indeterminate_stream"}, 1}, 402 {nullptr, 403 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, 404 ErrorFError, true), 405 0}}, 406 }; 407 408 /// Expanded value of EOF, empty before initialization. 409 mutable std::optional<int> EofVal; 410 /// Expanded value of SEEK_SET, 0 if not found. 411 mutable int SeekSetVal = 0; 412 /// Expanded value of SEEK_CUR, 1 if not found. 413 mutable int SeekCurVal = 1; 414 /// Expanded value of SEEK_END, 2 if not found. 415 mutable int SeekEndVal = 2; 416 /// The built-in va_list type is platform-specific 417 mutable QualType VaListType; 418 419 void evalFopen(const FnDescription *Desc, const CallEvent &Call, 420 CheckerContext &C) const; 421 422 void preFreopen(const FnDescription *Desc, const CallEvent &Call, 423 CheckerContext &C) const; 424 void evalFreopen(const FnDescription *Desc, const CallEvent &Call, 425 CheckerContext &C) const; 426 427 void evalFclose(const FnDescription *Desc, const CallEvent &Call, 428 CheckerContext &C) const; 429 430 void preRead(const FnDescription *Desc, const CallEvent &Call, 431 CheckerContext &C) const; 432 433 void preWrite(const FnDescription *Desc, const CallEvent &Call, 434 CheckerContext &C) const; 435 436 void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, 437 CheckerContext &C, bool IsFread) const; 438 439 void evalFgetx(const FnDescription *Desc, const CallEvent &Call, 440 CheckerContext &C, bool SingleChar) const; 441 442 void evalFputx(const FnDescription *Desc, const CallEvent &Call, 443 CheckerContext &C, bool IsSingleChar) const; 444 445 void evalFprintf(const FnDescription *Desc, const CallEvent &Call, 446 CheckerContext &C) const; 447 448 void evalFscanf(const FnDescription *Desc, const CallEvent &Call, 449 CheckerContext &C) const; 450 451 void evalUngetc(const FnDescription *Desc, const CallEvent &Call, 452 CheckerContext &C) const; 453 454 void evalGetdelim(const FnDescription *Desc, const CallEvent &Call, 455 CheckerContext &C) const; 456 457 void preFseek(const FnDescription *Desc, const CallEvent &Call, 458 CheckerContext &C) const; 459 void evalFseek(const FnDescription *Desc, const CallEvent &Call, 460 CheckerContext &C) const; 461 462 void evalFgetpos(const FnDescription *Desc, const CallEvent &Call, 463 CheckerContext &C) const; 464 465 void evalFsetpos(const FnDescription *Desc, const CallEvent &Call, 466 CheckerContext &C) const; 467 468 void evalFtell(const FnDescription *Desc, const CallEvent &Call, 469 CheckerContext &C) const; 470 471 void evalRewind(const FnDescription *Desc, const CallEvent &Call, 472 CheckerContext &C) const; 473 474 void preDefault(const FnDescription *Desc, const CallEvent &Call, 475 CheckerContext &C) const; 476 477 void evalClearerr(const FnDescription *Desc, const CallEvent &Call, 478 CheckerContext &C) const; 479 480 void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call, 481 CheckerContext &C, 482 const StreamErrorState &ErrorKind) const; 483 484 void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call, 485 CheckerContext &C, const StreamErrorState &ErrorKind, 486 bool Indeterminate) const; 487 488 void preFflush(const FnDescription *Desc, const CallEvent &Call, 489 CheckerContext &C) const; 490 491 void evalFflush(const FnDescription *Desc, const CallEvent &Call, 492 CheckerContext &C) const; 493 494 void evalFileno(const FnDescription *Desc, const CallEvent &Call, 495 CheckerContext &C) const; 496 497 /// Check that the stream (in StreamVal) is not NULL. 498 /// If it can only be NULL a fatal error is emitted and nullptr returned. 499 /// Otherwise the return value is a new state where the stream is constrained 500 /// to be non-null. 501 ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, 502 CheckerContext &C, 503 ProgramStateRef State) const; 504 505 /// Check that the stream is the opened state. 506 /// If the stream is known to be not opened an error is generated 507 /// and nullptr returned, otherwise the original state is returned. 508 ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C, 509 ProgramStateRef State) const; 510 511 /// Check that the stream has not an invalid ("indeterminate") file position, 512 /// generate warning for it. 513 /// (EOF is not an invalid position.) 514 /// The returned state can be nullptr if a fatal error was generated. 515 /// It can return non-null state if the stream has not an invalid position or 516 /// there is execution path with non-invalid position. 517 ProgramStateRef 518 ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C, 519 ProgramStateRef State) const; 520 521 /// Check the legality of the 'whence' argument of 'fseek'. 522 /// Generate error and return nullptr if it is found to be illegal. 523 /// Otherwise returns the state. 524 /// (State is not changed here because the "whence" value is already known.) 525 ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 526 ProgramStateRef State) const; 527 528 /// Generate warning about stream in EOF state. 529 /// There will be always a state transition into the passed State, 530 /// by the new non-fatal error node or (if failed) a normal transition, 531 /// to ensure uniform handling. 532 void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C, 533 ProgramStateRef State) const; 534 535 /// Emit resource leak warnings for the given symbols. 536 /// Createn a non-fatal error node for these, and returns it (if any warnings 537 /// were generated). Return value is non-null. 538 ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 539 CheckerContext &C, ExplodedNode *Pred) const; 540 541 /// Find the description data of the function called by a call event. 542 /// Returns nullptr if no function is recognized. 543 const FnDescription *lookupFn(const CallEvent &Call) const { 544 // Recognize "global C functions" with only integral or pointer arguments 545 // (and matching name) as stream functions. 546 for (auto *P : Call.parameters()) { 547 QualType T = P->getType(); 548 if (!T->isIntegralOrEnumerationType() && !T->isPointerType() && 549 T.getCanonicalType() != VaListType) 550 return nullptr; 551 } 552 553 return FnDescriptions.lookup(Call); 554 } 555 556 /// Generate a message for BugReporterVisitor if the stored symbol is 557 /// marked as interesting by the actual bug report. 558 const NoteTag *constructLeakNoteTag(CheckerContext &C, SymbolRef StreamSym, 559 const std::string &Message) const { 560 return C.getNoteTag([this, StreamSym, 561 Message](PathSensitiveBugReport &BR) -> std::string { 562 if (BR.isInteresting(StreamSym) && &BR.getBugType() == &BT_ResourceLeak) 563 return Message; 564 return ""; 565 }); 566 } 567 568 void initMacroValues(CheckerContext &C) const { 569 if (EofVal) 570 return; 571 572 if (const std::optional<int> OptInt = 573 tryExpandAsInteger("EOF", C.getPreprocessor())) 574 EofVal = *OptInt; 575 else 576 EofVal = -1; 577 if (const std::optional<int> OptInt = 578 tryExpandAsInteger("SEEK_SET", C.getPreprocessor())) 579 SeekSetVal = *OptInt; 580 if (const std::optional<int> OptInt = 581 tryExpandAsInteger("SEEK_END", C.getPreprocessor())) 582 SeekEndVal = *OptInt; 583 if (const std::optional<int> OptInt = 584 tryExpandAsInteger("SEEK_CUR", C.getPreprocessor())) 585 SeekCurVal = *OptInt; 586 } 587 588 void initVaListType(CheckerContext &C) const { 589 VaListType = C.getASTContext().getBuiltinVaListType().getCanonicalType(); 590 } 591 592 /// Searches for the ExplodedNode where the file descriptor was acquired for 593 /// StreamSym. 594 static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, 595 SymbolRef StreamSym, 596 CheckerContext &C); 597 }; 598 599 struct StreamOperationEvaluator { 600 SValBuilder &SVB; 601 const ASTContext &ACtx; 602 603 SymbolRef StreamSym; 604 const StreamState *SS = nullptr; 605 const CallExpr *CE = nullptr; 606 StreamErrorState NewES; 607 608 StreamOperationEvaluator(CheckerContext &C) 609 : SVB(C.getSValBuilder()), ACtx(C.getASTContext()) { 610 ; 611 } 612 613 bool Init(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C, 614 ProgramStateRef State) { 615 StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 616 if (!StreamSym) 617 return false; 618 SS = State->get<StreamMap>(StreamSym); 619 if (!SS) 620 return false; 621 NewES = SS->ErrorState; 622 CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 623 if (!CE) 624 return false; 625 626 assertStreamStateOpened(SS); 627 628 return true; 629 } 630 631 bool isStreamEof() const { return SS->ErrorState == ErrorFEof; } 632 633 NonLoc getZeroVal(const CallEvent &Call) { 634 return *SVB.makeZeroVal(Call.getResultType()).getAs<NonLoc>(); 635 } 636 637 ProgramStateRef setStreamState(ProgramStateRef State, 638 const StreamState &NewSS) { 639 NewES = NewSS.ErrorState; 640 return State->set<StreamMap>(StreamSym, NewSS); 641 } 642 643 ProgramStateRef makeAndBindRetVal(ProgramStateRef State, CheckerContext &C) { 644 NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 645 return State->BindExpr(CE, C.getLocationContext(), RetVal); 646 } 647 648 ProgramStateRef bindReturnValue(ProgramStateRef State, CheckerContext &C, 649 uint64_t Val) { 650 return State->BindExpr(CE, C.getLocationContext(), 651 SVB.makeIntVal(Val, CE->getCallReturnType(ACtx))); 652 } 653 654 ProgramStateRef bindReturnValue(ProgramStateRef State, CheckerContext &C, 655 SVal Val) { 656 return State->BindExpr(CE, C.getLocationContext(), Val); 657 } 658 659 ProgramStateRef bindNullReturnValue(ProgramStateRef State, 660 CheckerContext &C) { 661 return State->BindExpr(CE, C.getLocationContext(), 662 C.getSValBuilder().makeNullWithType(CE->getType())); 663 } 664 665 ProgramStateRef assumeBinOpNN(ProgramStateRef State, 666 BinaryOperator::Opcode Op, NonLoc LHS, 667 NonLoc RHS) { 668 auto Cond = SVB.evalBinOpNN(State, Op, LHS, RHS, SVB.getConditionType()) 669 .getAs<DefinedOrUnknownSVal>(); 670 if (!Cond) 671 return nullptr; 672 return State->assume(*Cond, true); 673 } 674 675 ConstraintManager::ProgramStatePair 676 makeRetValAndAssumeDual(ProgramStateRef State, CheckerContext &C) { 677 DefinedSVal RetVal = makeRetVal(C, CE); 678 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 679 return C.getConstraintManager().assumeDual(State, RetVal); 680 } 681 682 const NoteTag *getFailureNoteTag(const StreamChecker *Ch, CheckerContext &C) { 683 bool SetFeof = NewES.FEof && !SS->ErrorState.FEof; 684 bool SetFerror = NewES.FError && !SS->ErrorState.FError; 685 if (SetFeof && !SetFerror) 686 return Ch->constructSetEofNoteTag(C, StreamSym); 687 if (!SetFeof && SetFerror) 688 return Ch->constructSetErrorNoteTag(C, StreamSym); 689 if (SetFeof && SetFerror) 690 return Ch->constructSetEofOrErrorNoteTag(C, StreamSym); 691 return nullptr; 692 } 693 }; 694 695 } // end anonymous namespace 696 697 const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, 698 SymbolRef StreamSym, 699 CheckerContext &C) { 700 ProgramStateRef State = N->getState(); 701 // When bug type is resource leak, exploded node N may not have state info 702 // for leaked file descriptor, but predecessor should have it. 703 if (!State->get<StreamMap>(StreamSym)) 704 N = N->getFirstPred(); 705 706 const ExplodedNode *Pred = N; 707 while (N) { 708 State = N->getState(); 709 if (!State->get<StreamMap>(StreamSym)) 710 return Pred; 711 Pred = N; 712 N = N->getFirstPred(); 713 } 714 715 return nullptr; 716 } 717 718 static ProgramStateRef escapeArgs(ProgramStateRef State, CheckerContext &C, 719 const CallEvent &Call, 720 ArrayRef<unsigned int> EscapingArgs) { 721 const auto *CE = Call.getOriginExpr(); 722 723 SmallVector<SVal> EscapingVals; 724 EscapingVals.reserve(EscapingArgs.size()); 725 for (auto EscArgIdx : EscapingArgs) 726 EscapingVals.push_back(Call.getArgSVal(EscArgIdx)); 727 State = State->invalidateRegions(EscapingVals, CE, C.blockCount(), 728 C.getLocationContext(), 729 /*CausesPointerEscape=*/false); 730 return State; 731 } 732 733 //===----------------------------------------------------------------------===// 734 // Methods of StreamChecker. 735 //===----------------------------------------------------------------------===// 736 737 void StreamChecker::checkPreCall(const CallEvent &Call, 738 CheckerContext &C) const { 739 initMacroValues(C); 740 initVaListType(C); 741 742 const FnDescription *Desc = lookupFn(Call); 743 if (!Desc || !Desc->PreFn) 744 return; 745 746 Desc->PreFn(this, Desc, Call, C); 747 } 748 749 bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { 750 const FnDescription *Desc = lookupFn(Call); 751 if (!Desc && TestMode) 752 Desc = FnTestDescriptions.lookup(Call); 753 if (!Desc || !Desc->EvalFn) 754 return false; 755 756 Desc->EvalFn(this, Desc, Call, C); 757 758 return C.isDifferent(); 759 } 760 761 void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call, 762 CheckerContext &C) const { 763 ProgramStateRef State = C.getState(); 764 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 765 if (!CE) 766 return; 767 768 DefinedSVal RetVal = makeRetVal(C, CE); 769 SymbolRef RetSym = RetVal.getAsSymbol(); 770 assert(RetSym && "RetVal must be a symbol here."); 771 772 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 773 774 // Bifurcate the state into two: one with a valid FILE* pointer, the other 775 // with a NULL. 776 ProgramStateRef StateNotNull, StateNull; 777 std::tie(StateNotNull, StateNull) = 778 C.getConstraintManager().assumeDual(State, RetVal); 779 780 StateNotNull = 781 StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc)); 782 StateNull = 783 StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc)); 784 785 C.addTransition(StateNotNull, 786 constructLeakNoteTag(C, RetSym, "Stream opened here")); 787 C.addTransition(StateNull); 788 } 789 790 void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call, 791 CheckerContext &C) const { 792 // Do not allow NULL as passed stream pointer but allow a closed stream. 793 ProgramStateRef State = C.getState(); 794 State = ensureStreamNonNull(getStreamArg(Desc, Call), 795 Call.getArgExpr(Desc->StreamArgNo), C, State); 796 if (!State) 797 return; 798 799 C.addTransition(State); 800 } 801 802 void StreamChecker::evalFreopen(const FnDescription *Desc, 803 const CallEvent &Call, 804 CheckerContext &C) const { 805 ProgramStateRef State = C.getState(); 806 807 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 808 if (!CE) 809 return; 810 811 std::optional<DefinedSVal> StreamVal = 812 getStreamArg(Desc, Call).getAs<DefinedSVal>(); 813 if (!StreamVal) 814 return; 815 816 SymbolRef StreamSym = StreamVal->getAsSymbol(); 817 // Do not care about concrete values for stream ("(FILE *)0x12345"?). 818 // FIXME: Can be stdin, stdout, stderr such values? 819 if (!StreamSym) 820 return; 821 822 // Do not handle untracked stream. It is probably escaped. 823 if (!State->get<StreamMap>(StreamSym)) 824 return; 825 826 // Generate state for non-failed case. 827 // Return value is the passed stream pointer. 828 // According to the documentations, the stream is closed first 829 // but any close error is ignored. The state changes to (or remains) opened. 830 ProgramStateRef StateRetNotNull = 831 State->BindExpr(CE, C.getLocationContext(), *StreamVal); 832 // Generate state for NULL return value. 833 // Stream switches to OpenFailed state. 834 ProgramStateRef StateRetNull = 835 State->BindExpr(CE, C.getLocationContext(), 836 C.getSValBuilder().makeNullWithType(CE->getType())); 837 838 StateRetNotNull = 839 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 840 StateRetNull = 841 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc)); 842 843 C.addTransition(StateRetNotNull, 844 constructLeakNoteTag(C, StreamSym, "Stream reopened here")); 845 C.addTransition(StateRetNull); 846 } 847 848 void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, 849 CheckerContext &C) const { 850 ProgramStateRef State = C.getState(); 851 StreamOperationEvaluator E(C); 852 if (!E.Init(Desc, Call, C, State)) 853 return; 854 855 // Close the File Descriptor. 856 // Regardless if the close fails or not, stream becomes "closed" 857 // and can not be used any more. 858 State = E.setStreamState(State, StreamState::getClosed(Desc)); 859 860 // Return 0 on success, EOF on failure. 861 C.addTransition(E.bindReturnValue(State, C, 0)); 862 C.addTransition(E.bindReturnValue(State, C, *EofVal)); 863 } 864 865 void StreamChecker::preRead(const FnDescription *Desc, const CallEvent &Call, 866 CheckerContext &C) const { 867 ProgramStateRef State = C.getState(); 868 SVal StreamVal = getStreamArg(Desc, Call); 869 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 870 State); 871 if (!State) 872 return; 873 State = ensureStreamOpened(StreamVal, C, State); 874 if (!State) 875 return; 876 State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 877 if (!State) 878 return; 879 880 SymbolRef Sym = StreamVal.getAsSymbol(); 881 if (Sym && State->get<StreamMap>(Sym)) { 882 const StreamState *SS = State->get<StreamMap>(Sym); 883 if (SS->ErrorState & ErrorFEof) 884 reportFEofWarning(Sym, C, State); 885 } else { 886 C.addTransition(State); 887 } 888 } 889 890 void StreamChecker::preWrite(const FnDescription *Desc, const CallEvent &Call, 891 CheckerContext &C) const { 892 ProgramStateRef State = C.getState(); 893 SVal StreamVal = getStreamArg(Desc, Call); 894 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 895 State); 896 if (!State) 897 return; 898 State = ensureStreamOpened(StreamVal, C, State); 899 if (!State) 900 return; 901 State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 902 if (!State) 903 return; 904 905 C.addTransition(State); 906 } 907 908 void StreamChecker::evalFreadFwrite(const FnDescription *Desc, 909 const CallEvent &Call, CheckerContext &C, 910 bool IsFread) const { 911 ProgramStateRef State = C.getState(); 912 StreamOperationEvaluator E(C); 913 if (!E.Init(Desc, Call, C, State)) 914 return; 915 916 std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); 917 if (!SizeVal) 918 return; 919 std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); 920 if (!NMembVal) 921 return; 922 923 // C'99 standard, §7.19.8.1.3, the return value of fread: 924 // The fread function returns the number of elements successfully read, which 925 // may be less than nmemb if a read error or end-of-file is encountered. If 926 // size or nmemb is zero, fread returns zero and the contents of the array and 927 // the state of the stream remain unchanged. 928 if (State->isNull(*SizeVal).isConstrainedTrue() || 929 State->isNull(*NMembVal).isConstrainedTrue()) { 930 // This is the "size or nmemb is zero" case. 931 // Just return 0, do nothing more (not clear the error flags). 932 C.addTransition(E.bindReturnValue(State, C, 0)); 933 return; 934 } 935 936 // At read, invalidate the buffer in any case of error or success, 937 // except if EOF was already present. 938 if (IsFread && !E.isStreamEof()) 939 State = escapeArgs(State, C, Call, {0}); 940 941 // Generate a transition for the success state. 942 // If we know the state to be FEOF at fread, do not add a success state. 943 if (!IsFread || !E.isStreamEof()) { 944 ProgramStateRef StateNotFailed = 945 State->BindExpr(E.CE, C.getLocationContext(), *NMembVal); 946 StateNotFailed = 947 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 948 C.addTransition(StateNotFailed); 949 } 950 951 // Add transition for the failed state. 952 // At write, add failure case only if "pedantic mode" is on. 953 if (!IsFread && !PedanticMode) 954 return; 955 956 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 957 ProgramStateRef StateFailed = 958 State->BindExpr(E.CE, C.getLocationContext(), RetVal); 959 StateFailed = E.assumeBinOpNN(StateFailed, BO_LT, RetVal, *NMembVal); 960 if (!StateFailed) 961 return; 962 963 StreamErrorState NewES; 964 if (IsFread) 965 NewES = E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError; 966 else 967 NewES = ErrorFError; 968 // If a (non-EOF) error occurs, the resulting value of the file position 969 // indicator for the stream is indeterminate. 970 StateFailed = E.setStreamState( 971 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof())); 972 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 973 } 974 975 void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call, 976 CheckerContext &C, bool SingleChar) const { 977 // `fgetc` returns the read character on success, otherwise returns EOF. 978 // `fgets` returns the read buffer address on success, otherwise returns NULL. 979 980 ProgramStateRef State = C.getState(); 981 StreamOperationEvaluator E(C); 982 if (!E.Init(Desc, Call, C, State)) 983 return; 984 985 if (!E.isStreamEof()) { 986 // If there was already EOF, assume that read buffer is not changed. 987 // Otherwise it may change at success or failure. 988 State = escapeArgs(State, C, Call, {0}); 989 if (SingleChar) { 990 // Generate a transition for the success state of `fgetc`. 991 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 992 ProgramStateRef StateNotFailed = 993 State->BindExpr(E.CE, C.getLocationContext(), RetVal); 994 // The returned 'unsigned char' of `fgetc` is converted to 'int', 995 // so we need to check if it is in range [0, 255]. 996 StateNotFailed = StateNotFailed->assumeInclusiveRange( 997 RetVal, 998 E.SVB.getBasicValueFactory().getValue(0, E.ACtx.UnsignedCharTy), 999 E.SVB.getBasicValueFactory().getMaxValue(E.ACtx.UnsignedCharTy), 1000 true); 1001 if (!StateNotFailed) 1002 return; 1003 C.addTransition(StateNotFailed); 1004 } else { 1005 // Generate a transition for the success state of `fgets`. 1006 std::optional<DefinedSVal> GetBuf = 1007 Call.getArgSVal(0).getAs<DefinedSVal>(); 1008 if (!GetBuf) 1009 return; 1010 ProgramStateRef StateNotFailed = 1011 State->BindExpr(E.CE, C.getLocationContext(), *GetBuf); 1012 StateNotFailed = 1013 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 1014 C.addTransition(StateNotFailed); 1015 } 1016 } 1017 1018 // Add transition for the failed state. 1019 ProgramStateRef StateFailed; 1020 if (SingleChar) 1021 StateFailed = E.bindReturnValue(State, C, *EofVal); 1022 else 1023 StateFailed = E.bindNullReturnValue(State, C); 1024 1025 // If a (non-EOF) error occurs, the resulting value of the file position 1026 // indicator for the stream is indeterminate. 1027 StreamErrorState NewES = 1028 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError; 1029 StateFailed = E.setStreamState( 1030 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof())); 1031 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1032 } 1033 1034 void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, 1035 CheckerContext &C, bool IsSingleChar) const { 1036 // `fputc` returns the written character on success, otherwise returns EOF. 1037 // `fputs` returns a nonnegative value on success, otherwise returns EOF. 1038 1039 ProgramStateRef State = C.getState(); 1040 StreamOperationEvaluator E(C); 1041 if (!E.Init(Desc, Call, C, State)) 1042 return; 1043 1044 if (IsSingleChar) { 1045 // Generate a transition for the success state of `fputc`. 1046 std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>(); 1047 if (!PutVal) 1048 return; 1049 ProgramStateRef StateNotFailed = 1050 State->BindExpr(E.CE, C.getLocationContext(), *PutVal); 1051 StateNotFailed = 1052 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 1053 C.addTransition(StateNotFailed); 1054 } else { 1055 // Generate a transition for the success state of `fputs`. 1056 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1057 ProgramStateRef StateNotFailed = 1058 State->BindExpr(E.CE, C.getLocationContext(), RetVal); 1059 StateNotFailed = 1060 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call)); 1061 if (!StateNotFailed) 1062 return; 1063 StateNotFailed = 1064 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 1065 C.addTransition(StateNotFailed); 1066 } 1067 1068 if (!PedanticMode) 1069 return; 1070 1071 // Add transition for the failed state. The resulting value of the file 1072 // position indicator for the stream is indeterminate. 1073 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal); 1074 StateFailed = E.setStreamState( 1075 StateFailed, StreamState::getOpened(Desc, ErrorFError, true)); 1076 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1077 } 1078 1079 void StreamChecker::evalFprintf(const FnDescription *Desc, 1080 const CallEvent &Call, 1081 CheckerContext &C) const { 1082 if (Call.getNumArgs() < 2) 1083 return; 1084 1085 ProgramStateRef State = C.getState(); 1086 StreamOperationEvaluator E(C); 1087 if (!E.Init(Desc, Call, C, State)) 1088 return; 1089 1090 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1091 State = State->BindExpr(E.CE, C.getLocationContext(), RetVal); 1092 auto Cond = 1093 E.SVB 1094 .evalBinOp(State, BO_GE, RetVal, E.SVB.makeZeroVal(E.ACtx.IntTy), 1095 E.SVB.getConditionType()) 1096 .getAs<DefinedOrUnknownSVal>(); 1097 if (!Cond) 1098 return; 1099 ProgramStateRef StateNotFailed, StateFailed; 1100 std::tie(StateNotFailed, StateFailed) = State->assume(*Cond); 1101 1102 StateNotFailed = 1103 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 1104 C.addTransition(StateNotFailed); 1105 1106 if (!PedanticMode) 1107 return; 1108 1109 // Add transition for the failed state. The resulting value of the file 1110 // position indicator for the stream is indeterminate. 1111 StateFailed = E.setStreamState( 1112 StateFailed, StreamState::getOpened(Desc, ErrorFError, true)); 1113 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1114 } 1115 1116 void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call, 1117 CheckerContext &C) const { 1118 if (Call.getNumArgs() < 2) 1119 return; 1120 1121 ProgramStateRef State = C.getState(); 1122 StreamOperationEvaluator E(C); 1123 if (!E.Init(Desc, Call, C, State)) 1124 return; 1125 1126 // Add the success state. 1127 // In this context "success" means there is not an EOF or other read error 1128 // before any item is matched in 'fscanf'. But there may be match failure, 1129 // therefore return value can be 0 or greater. 1130 // It is not specified what happens if some items (not all) are matched and 1131 // then EOF or read error happens. Now this case is handled like a "success" 1132 // case, and no error flags are set on the stream. This is probably not 1133 // accurate, and the POSIX documentation does not tell more. 1134 if (!E.isStreamEof()) { 1135 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1136 ProgramStateRef StateNotFailed = 1137 State->BindExpr(E.CE, C.getLocationContext(), RetVal); 1138 StateNotFailed = 1139 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call)); 1140 if (!StateNotFailed) 1141 return; 1142 1143 if (auto const *Callee = Call.getCalleeIdentifier(); 1144 !Callee || !Callee->getName().equals("vfscanf")) { 1145 SmallVector<unsigned int> EscArgs; 1146 for (auto EscArg : llvm::seq(2u, Call.getNumArgs())) 1147 EscArgs.push_back(EscArg); 1148 StateNotFailed = escapeArgs(StateNotFailed, C, Call, EscArgs); 1149 } 1150 1151 if (StateNotFailed) 1152 C.addTransition(StateNotFailed); 1153 } 1154 1155 // Add transition for the failed state. 1156 // Error occurs if nothing is matched yet and reading the input fails. 1157 // Error can be EOF, or other error. At "other error" FERROR or 'errno' can 1158 // be set but it is not further specified if all are required to be set. 1159 // Documentation does not mention, but file position will be set to 1160 // indeterminate similarly as at 'fread'. 1161 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal); 1162 StreamErrorState NewES = 1163 E.isStreamEof() ? ErrorFEof : ErrorNone | ErrorFEof | ErrorFError; 1164 StateFailed = E.setStreamState( 1165 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof())); 1166 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1167 } 1168 1169 void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent &Call, 1170 CheckerContext &C) const { 1171 ProgramStateRef State = C.getState(); 1172 StreamOperationEvaluator E(C); 1173 if (!E.Init(Desc, Call, C, State)) 1174 return; 1175 1176 // Generate a transition for the success state. 1177 std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>(); 1178 if (!PutVal) 1179 return; 1180 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, *PutVal); 1181 StateNotFailed = 1182 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 1183 C.addTransition(StateNotFailed); 1184 1185 // Add transition for the failed state. 1186 // Failure of 'ungetc' does not result in feof or ferror state. 1187 // If the PutVal has value of EofVal the function should "fail", but this is 1188 // the same transition as the success state. 1189 // In this case only one state transition is added by the analyzer (the two 1190 // new states may be similar). 1191 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal); 1192 StateFailed = E.setStreamState(StateFailed, StreamState::getOpened(Desc)); 1193 C.addTransition(StateFailed); 1194 } 1195 1196 void StreamChecker::evalGetdelim(const FnDescription *Desc, 1197 const CallEvent &Call, 1198 CheckerContext &C) const { 1199 ProgramStateRef State = C.getState(); 1200 StreamOperationEvaluator E(C); 1201 if (!E.Init(Desc, Call, C, State)) 1202 return; 1203 1204 // Upon successful completion, the getline() and getdelim() functions shall 1205 // return the number of bytes written into the buffer. 1206 // If the end-of-file indicator for the stream is set, the function shall 1207 // return -1. 1208 // If an error occurs, the function shall return -1 and set 'errno'. 1209 1210 if (!E.isStreamEof()) { 1211 // Escape buffer and size (may change by the call). 1212 // May happen even at error (partial read?). 1213 State = escapeArgs(State, C, Call, {0, 1}); 1214 1215 // Add transition for the successful state. 1216 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1217 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, RetVal); 1218 StateNotFailed = 1219 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call)); 1220 1221 // On success, a buffer is allocated. 1222 auto NewLinePtr = getPointeeVal(Call.getArgSVal(0), State); 1223 if (NewLinePtr && isa<DefinedOrUnknownSVal>(*NewLinePtr)) 1224 StateNotFailed = StateNotFailed->assume( 1225 NewLinePtr->castAs<DefinedOrUnknownSVal>(), true); 1226 1227 // The buffer size `*n` must be enough to hold the whole line, and 1228 // greater than the return value, since it has to account for '\0'. 1229 SVal SizePtrSval = Call.getArgSVal(1); 1230 auto NVal = getPointeeVal(SizePtrSval, State); 1231 if (NVal && isa<NonLoc>(*NVal)) { 1232 StateNotFailed = E.assumeBinOpNN(StateNotFailed, BO_GT, 1233 NVal->castAs<NonLoc>(), RetVal); 1234 StateNotFailed = E.bindReturnValue(StateNotFailed, C, RetVal); 1235 } 1236 if (!StateNotFailed) 1237 return; 1238 C.addTransition(StateNotFailed); 1239 } 1240 1241 // Add transition for the failed state. 1242 // If a (non-EOF) error occurs, the resulting value of the file position 1243 // indicator for the stream is indeterminate. 1244 ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1); 1245 StreamErrorState NewES = 1246 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError; 1247 StateFailed = E.setStreamState( 1248 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof())); 1249 // On failure, the content of the buffer is undefined. 1250 if (auto NewLinePtr = getPointeeVal(Call.getArgSVal(0), State)) 1251 StateFailed = StateFailed->bindLoc(*NewLinePtr, UndefinedVal(), 1252 C.getLocationContext()); 1253 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1254 } 1255 1256 void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, 1257 CheckerContext &C) const { 1258 ProgramStateRef State = C.getState(); 1259 SVal StreamVal = getStreamArg(Desc, Call); 1260 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 1261 State); 1262 if (!State) 1263 return; 1264 State = ensureStreamOpened(StreamVal, C, State); 1265 if (!State) 1266 return; 1267 State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); 1268 if (!State) 1269 return; 1270 1271 C.addTransition(State); 1272 } 1273 1274 void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, 1275 CheckerContext &C) const { 1276 ProgramStateRef State = C.getState(); 1277 StreamOperationEvaluator E(C); 1278 if (!E.Init(Desc, Call, C, State)) 1279 return; 1280 1281 // Add success state. 1282 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, 0); 1283 // No failure: Reset the state to opened with no error. 1284 StateNotFailed = 1285 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 1286 C.addTransition(StateNotFailed); 1287 1288 if (!PedanticMode) 1289 return; 1290 1291 // Add failure state. 1292 // At error it is possible that fseek fails but sets none of the error flags. 1293 // If fseek failed, assume that the file position becomes indeterminate in any 1294 // case. 1295 // It is allowed to set the position beyond the end of the file. EOF error 1296 // should not occur. 1297 ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1); 1298 StateFailed = E.setStreamState( 1299 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true)); 1300 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1301 } 1302 1303 void StreamChecker::evalFgetpos(const FnDescription *Desc, 1304 const CallEvent &Call, 1305 CheckerContext &C) const { 1306 ProgramStateRef State = C.getState(); 1307 StreamOperationEvaluator E(C); 1308 if (!E.Init(Desc, Call, C, State)) 1309 return; 1310 1311 ProgramStateRef StateNotFailed, StateFailed; 1312 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C); 1313 StateNotFailed = escapeArgs(StateNotFailed, C, Call, {1}); 1314 1315 // This function does not affect the stream state. 1316 // Still we add success and failure state with the appropriate return value. 1317 // StdLibraryFunctionsChecker can change these states (set the 'errno' state). 1318 C.addTransition(StateNotFailed); 1319 C.addTransition(StateFailed); 1320 } 1321 1322 void StreamChecker::evalFsetpos(const FnDescription *Desc, 1323 const CallEvent &Call, 1324 CheckerContext &C) const { 1325 ProgramStateRef State = C.getState(); 1326 StreamOperationEvaluator E(C); 1327 if (!E.Init(Desc, Call, C, State)) 1328 return; 1329 1330 ProgramStateRef StateNotFailed, StateFailed; 1331 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C); 1332 1333 StateNotFailed = E.setStreamState( 1334 StateNotFailed, StreamState::getOpened(Desc, ErrorNone, false)); 1335 C.addTransition(StateNotFailed); 1336 1337 if (!PedanticMode) 1338 return; 1339 1340 // At failure ferror could be set. 1341 // The standards do not tell what happens with the file position at failure. 1342 // But we can assume that it is dangerous to make a next I/O operation after 1343 // the position was not set correctly (similar to 'fseek'). 1344 StateFailed = E.setStreamState( 1345 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true)); 1346 1347 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1348 } 1349 1350 void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call, 1351 CheckerContext &C) const { 1352 ProgramStateRef State = C.getState(); 1353 StreamOperationEvaluator E(C); 1354 if (!E.Init(Desc, Call, C, State)) 1355 return; 1356 1357 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1358 ProgramStateRef StateNotFailed = 1359 State->BindExpr(E.CE, C.getLocationContext(), RetVal); 1360 StateNotFailed = 1361 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call)); 1362 if (!StateNotFailed) 1363 return; 1364 1365 ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1); 1366 1367 // This function does not affect the stream state. 1368 // Still we add success and failure state with the appropriate return value. 1369 // StdLibraryFunctionsChecker can change these states (set the 'errno' state). 1370 C.addTransition(StateNotFailed); 1371 C.addTransition(StateFailed); 1372 } 1373 1374 void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call, 1375 CheckerContext &C) const { 1376 ProgramStateRef State = C.getState(); 1377 StreamOperationEvaluator E(C); 1378 if (!E.Init(Desc, Call, C, State)) 1379 return; 1380 1381 State = 1382 E.setStreamState(State, StreamState::getOpened(Desc, ErrorNone, false)); 1383 C.addTransition(State); 1384 } 1385 1386 void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call, 1387 CheckerContext &C) const { 1388 ProgramStateRef State = C.getState(); 1389 SVal StreamVal = getStreamArg(Desc, Call); 1390 std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>(); 1391 if (!Stream) 1392 return; 1393 1394 ProgramStateRef StateNotNull, StateNull; 1395 std::tie(StateNotNull, StateNull) = 1396 C.getConstraintManager().assumeDual(State, *Stream); 1397 if (StateNotNull && !StateNull) 1398 ensureStreamOpened(StreamVal, C, StateNotNull); 1399 } 1400 1401 void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, 1402 CheckerContext &C) const { 1403 ProgramStateRef State = C.getState(); 1404 SVal StreamVal = getStreamArg(Desc, Call); 1405 std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>(); 1406 if (!Stream) 1407 return; 1408 1409 // Skip if the stream can be both NULL and non-NULL. 1410 ProgramStateRef StateNotNull, StateNull; 1411 std::tie(StateNotNull, StateNull) = 1412 C.getConstraintManager().assumeDual(State, *Stream); 1413 if (StateNotNull && StateNull) 1414 return; 1415 if (StateNotNull && !StateNull) 1416 State = StateNotNull; 1417 else 1418 State = StateNull; 1419 1420 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1421 if (!CE) 1422 return; 1423 1424 // `fflush` returns EOF on failure, otherwise returns 0. 1425 ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); 1426 ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); 1427 1428 // Clear error states if `fflush` returns 0, but retain their EOF flags. 1429 auto ClearErrorInNotFailed = [&StateNotFailed, Desc](SymbolRef Sym, 1430 const StreamState *SS) { 1431 if (SS->ErrorState & ErrorFError) { 1432 StreamErrorState NewES = 1433 (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone; 1434 StreamState NewSS = StreamState::getOpened(Desc, NewES, false); 1435 StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS); 1436 } 1437 }; 1438 1439 if (StateNotNull && !StateNull) { 1440 // Skip if the input stream's state is unknown, open-failed or closed. 1441 if (SymbolRef StreamSym = StreamVal.getAsSymbol()) { 1442 const StreamState *SS = State->get<StreamMap>(StreamSym); 1443 if (SS) { 1444 assert(SS->isOpened() && "Stream is expected to be opened"); 1445 ClearErrorInNotFailed(StreamSym, SS); 1446 } else 1447 return; 1448 } 1449 } else { 1450 // Clear error states for all streams. 1451 const StreamMapTy &Map = StateNotFailed->get<StreamMap>(); 1452 for (const auto &I : Map) { 1453 SymbolRef Sym = I.first; 1454 const StreamState &SS = I.second; 1455 if (SS.isOpened()) 1456 ClearErrorInNotFailed(Sym, &SS); 1457 } 1458 } 1459 1460 C.addTransition(StateNotFailed); 1461 C.addTransition(StateFailed); 1462 } 1463 1464 void StreamChecker::evalClearerr(const FnDescription *Desc, 1465 const CallEvent &Call, 1466 CheckerContext &C) const { 1467 ProgramStateRef State = C.getState(); 1468 StreamOperationEvaluator E(C); 1469 if (!E.Init(Desc, Call, C, State)) 1470 return; 1471 1472 // FilePositionIndeterminate is not cleared. 1473 State = E.setStreamState( 1474 State, 1475 StreamState::getOpened(Desc, ErrorNone, E.SS->FilePositionIndeterminate)); 1476 C.addTransition(State); 1477 } 1478 1479 void StreamChecker::evalFeofFerror(const FnDescription *Desc, 1480 const CallEvent &Call, CheckerContext &C, 1481 const StreamErrorState &ErrorKind) const { 1482 ProgramStateRef State = C.getState(); 1483 StreamOperationEvaluator E(C); 1484 if (!E.Init(Desc, Call, C, State)) 1485 return; 1486 1487 if (E.SS->ErrorState & ErrorKind) { 1488 // Execution path with error of ErrorKind. 1489 // Function returns true. 1490 // From now on it is the only one error state. 1491 ProgramStateRef TrueState = bindAndAssumeTrue(State, C, E.CE); 1492 C.addTransition(E.setStreamState( 1493 TrueState, StreamState::getOpened(Desc, ErrorKind, 1494 E.SS->FilePositionIndeterminate && 1495 !ErrorKind.isFEof()))); 1496 } 1497 if (StreamErrorState NewES = E.SS->ErrorState & (~ErrorKind)) { 1498 // Execution path(s) with ErrorKind not set. 1499 // Function returns false. 1500 // New error state is everything before minus ErrorKind. 1501 ProgramStateRef FalseState = E.bindReturnValue(State, C, 0); 1502 C.addTransition(E.setStreamState( 1503 FalseState, 1504 StreamState::getOpened( 1505 Desc, NewES, E.SS->FilePositionIndeterminate && !NewES.isFEof()))); 1506 } 1507 } 1508 1509 void StreamChecker::evalFileno(const FnDescription *Desc, const CallEvent &Call, 1510 CheckerContext &C) const { 1511 // Fileno should fail only if the passed pointer is invalid. 1512 // Some of the preconditions are checked already in preDefault. 1513 // Here we can assume that the operation does not fail, because if we 1514 // introduced a separate branch where fileno() returns -1, then it would cause 1515 // many unexpected and unwanted warnings in situations where fileno() is 1516 // called on valid streams. 1517 // The stream error states are not modified by 'fileno', and 'errno' is also 1518 // left unchanged (so this evalCall does not invalidate it, but we have a 1519 // custom evalCall instead of the default that would invalidate it). 1520 ProgramStateRef State = C.getState(); 1521 StreamOperationEvaluator E(C); 1522 if (!E.Init(Desc, Call, C, State)) 1523 return; 1524 1525 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1526 State = State->BindExpr(E.CE, C.getLocationContext(), RetVal); 1527 State = E.assumeBinOpNN(State, BO_GE, RetVal, E.getZeroVal(Call)); 1528 if (!State) 1529 return; 1530 1531 C.addTransition(State); 1532 } 1533 1534 void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, 1535 CheckerContext &C) const { 1536 ProgramStateRef State = C.getState(); 1537 SVal StreamVal = getStreamArg(Desc, Call); 1538 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 1539 State); 1540 if (!State) 1541 return; 1542 State = ensureStreamOpened(StreamVal, C, State); 1543 if (!State) 1544 return; 1545 1546 C.addTransition(State); 1547 } 1548 1549 void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, 1550 const CallEvent &Call, CheckerContext &C, 1551 const StreamErrorState &ErrorKind, 1552 bool Indeterminate) const { 1553 ProgramStateRef State = C.getState(); 1554 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 1555 assert(StreamSym && "Operation not permitted on non-symbolic stream value."); 1556 const StreamState *SS = State->get<StreamMap>(StreamSym); 1557 assert(SS && "Stream should be tracked by the checker."); 1558 State = State->set<StreamMap>( 1559 StreamSym, 1560 StreamState::getOpened(SS->LastOperation, ErrorKind, Indeterminate)); 1561 C.addTransition(State); 1562 } 1563 1564 ProgramStateRef 1565 StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, 1566 CheckerContext &C, 1567 ProgramStateRef State) const { 1568 auto Stream = StreamVal.getAs<DefinedSVal>(); 1569 if (!Stream) 1570 return State; 1571 1572 ConstraintManager &CM = C.getConstraintManager(); 1573 1574 ProgramStateRef StateNotNull, StateNull; 1575 std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream); 1576 1577 if (!StateNotNull && StateNull) { 1578 if (ExplodedNode *N = C.generateErrorNode(StateNull)) { 1579 auto R = std::make_unique<PathSensitiveBugReport>( 1580 BT_FileNull, "Stream pointer might be NULL.", N); 1581 if (StreamE) 1582 bugreporter::trackExpressionValue(N, StreamE, *R); 1583 C.emitReport(std::move(R)); 1584 } 1585 return nullptr; 1586 } 1587 1588 return StateNotNull; 1589 } 1590 1591 ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, 1592 CheckerContext &C, 1593 ProgramStateRef State) const { 1594 SymbolRef Sym = StreamVal.getAsSymbol(); 1595 if (!Sym) 1596 return State; 1597 1598 const StreamState *SS = State->get<StreamMap>(Sym); 1599 if (!SS) 1600 return State; 1601 1602 if (SS->isClosed()) { 1603 // Using a stream pointer after 'fclose' causes undefined behavior 1604 // according to cppreference.com . 1605 ExplodedNode *N = C.generateErrorNode(); 1606 if (N) { 1607 C.emitReport(std::make_unique<PathSensitiveBugReport>( 1608 BT_UseAfterClose, 1609 "Stream might be already closed. Causes undefined behaviour.", N)); 1610 return nullptr; 1611 } 1612 1613 return State; 1614 } 1615 1616 if (SS->isOpenFailed()) { 1617 // Using a stream that has failed to open is likely to cause problems. 1618 // This should usually not occur because stream pointer is NULL. 1619 // But freopen can cause a state when stream pointer remains non-null but 1620 // failed to open. 1621 ExplodedNode *N = C.generateErrorNode(); 1622 if (N) { 1623 C.emitReport(std::make_unique<PathSensitiveBugReport>( 1624 BT_UseAfterOpenFailed, 1625 "Stream might be invalid after " 1626 "(re-)opening it has failed. " 1627 "Can cause undefined behaviour.", 1628 N)); 1629 return nullptr; 1630 } 1631 } 1632 1633 return State; 1634 } 1635 1636 ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate( 1637 SVal StreamVal, CheckerContext &C, ProgramStateRef State) const { 1638 static const char *BugMessage = 1639 "File position of the stream might be 'indeterminate' " 1640 "after a failed operation. " 1641 "Can cause undefined behavior."; 1642 1643 SymbolRef Sym = StreamVal.getAsSymbol(); 1644 if (!Sym) 1645 return State; 1646 1647 const StreamState *SS = State->get<StreamMap>(Sym); 1648 if (!SS) 1649 return State; 1650 1651 assert(SS->isOpened() && "First ensure that stream is opened."); 1652 1653 if (SS->FilePositionIndeterminate) { 1654 if (SS->ErrorState & ErrorFEof) { 1655 // The error is unknown but may be FEOF. 1656 // Continue analysis with the FEOF error state. 1657 // Report warning because the other possible error states. 1658 ExplodedNode *N = C.generateNonFatalErrorNode(State); 1659 if (!N) 1660 return nullptr; 1661 1662 auto R = std::make_unique<PathSensitiveBugReport>( 1663 BT_IndeterminatePosition, BugMessage, N); 1664 R->markInteresting(Sym); 1665 C.emitReport(std::move(R)); 1666 return State->set<StreamMap>( 1667 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false)); 1668 } 1669 1670 // Known or unknown error state without FEOF possible. 1671 // Stop analysis, report error. 1672 if (ExplodedNode *N = C.generateErrorNode(State)) { 1673 auto R = std::make_unique<PathSensitiveBugReport>( 1674 BT_IndeterminatePosition, BugMessage, N); 1675 R->markInteresting(Sym); 1676 C.emitReport(std::move(R)); 1677 } 1678 1679 return nullptr; 1680 } 1681 1682 return State; 1683 } 1684 1685 ProgramStateRef 1686 StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 1687 ProgramStateRef State) const { 1688 std::optional<nonloc::ConcreteInt> CI = 1689 WhenceVal.getAs<nonloc::ConcreteInt>(); 1690 if (!CI) 1691 return State; 1692 1693 int64_t X = CI->getValue().getSExtValue(); 1694 if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal) 1695 return State; 1696 1697 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 1698 C.emitReport(std::make_unique<PathSensitiveBugReport>( 1699 BT_IllegalWhence, 1700 "The whence argument to fseek() should be " 1701 "SEEK_SET, SEEK_END, or SEEK_CUR.", 1702 N)); 1703 return nullptr; 1704 } 1705 1706 return State; 1707 } 1708 1709 void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C, 1710 ProgramStateRef State) const { 1711 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 1712 auto R = std::make_unique<PathSensitiveBugReport>( 1713 BT_StreamEof, 1714 "Read function called when stream is in EOF state. " 1715 "Function has no effect.", 1716 N); 1717 R->markInteresting(StreamSym); 1718 C.emitReport(std::move(R)); 1719 return; 1720 } 1721 C.addTransition(State); 1722 } 1723 1724 ExplodedNode * 1725 StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 1726 CheckerContext &C, ExplodedNode *Pred) const { 1727 ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred); 1728 if (!Err) 1729 return Pred; 1730 1731 for (SymbolRef LeakSym : LeakedSyms) { 1732 // Resource leaks can result in multiple warning that describe the same kind 1733 // of programming error: 1734 // void f() { 1735 // FILE *F = fopen("a.txt"); 1736 // if (rand()) // state split 1737 // return; // warning 1738 // } // warning 1739 // While this isn't necessarily true (leaking the same stream could result 1740 // from a different kinds of errors), the reduction in redundant reports 1741 // makes this a worthwhile heuristic. 1742 // FIXME: Add a checker option to turn this uniqueing feature off. 1743 const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C); 1744 assert(StreamOpenNode && "Could not find place of stream opening."); 1745 1746 PathDiagnosticLocation LocUsedForUniqueing; 1747 if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics()) 1748 LocUsedForUniqueing = PathDiagnosticLocation::createBegin( 1749 StreamStmt, C.getSourceManager(), 1750 StreamOpenNode->getLocationContext()); 1751 1752 std::unique_ptr<PathSensitiveBugReport> R = 1753 std::make_unique<PathSensitiveBugReport>( 1754 BT_ResourceLeak, 1755 "Opened stream never closed. Potential resource leak.", Err, 1756 LocUsedForUniqueing, 1757 StreamOpenNode->getLocationContext()->getDecl()); 1758 R->markInteresting(LeakSym); 1759 C.emitReport(std::move(R)); 1760 } 1761 1762 return Err; 1763 } 1764 1765 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 1766 CheckerContext &C) const { 1767 ProgramStateRef State = C.getState(); 1768 1769 llvm::SmallVector<SymbolRef, 2> LeakedSyms; 1770 1771 const StreamMapTy &Map = State->get<StreamMap>(); 1772 for (const auto &I : Map) { 1773 SymbolRef Sym = I.first; 1774 const StreamState &SS = I.second; 1775 if (!SymReaper.isDead(Sym)) 1776 continue; 1777 if (SS.isOpened()) 1778 LeakedSyms.push_back(Sym); 1779 State = State->remove<StreamMap>(Sym); 1780 } 1781 1782 ExplodedNode *N = C.getPredecessor(); 1783 if (!LeakedSyms.empty()) 1784 N = reportLeaks(LeakedSyms, C, N); 1785 1786 C.addTransition(State, N); 1787 } 1788 1789 ProgramStateRef StreamChecker::checkPointerEscape( 1790 ProgramStateRef State, const InvalidatedSymbols &Escaped, 1791 const CallEvent *Call, PointerEscapeKind Kind) const { 1792 // Check for file-handling system call that is not handled by the checker. 1793 // FIXME: The checker should be updated to handle all system calls that take 1794 // 'FILE*' argument. These are now ignored. 1795 if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader()) 1796 return State; 1797 1798 for (SymbolRef Sym : Escaped) { 1799 // The symbol escaped. 1800 // From now the stream can be manipulated in unknown way to the checker, 1801 // it is not possible to handle it any more. 1802 // Optimistically, assume that the corresponding file handle will be closed 1803 // somewhere else. 1804 // Remove symbol from state so the following stream calls on this symbol are 1805 // not handled by the checker. 1806 State = State->remove<StreamMap>(Sym); 1807 } 1808 return State; 1809 } 1810 1811 //===----------------------------------------------------------------------===// 1812 // Checker registration. 1813 //===----------------------------------------------------------------------===// 1814 1815 void ento::registerStreamChecker(CheckerManager &Mgr) { 1816 auto *Checker = Mgr.registerChecker<StreamChecker>(); 1817 Checker->PedanticMode = 1818 Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "Pedantic"); 1819 } 1820 1821 bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) { 1822 return true; 1823 } 1824 1825 void ento::registerStreamTesterChecker(CheckerManager &Mgr) { 1826 auto *Checker = Mgr.getChecker<StreamChecker>(); 1827 Checker->TestMode = true; 1828 } 1829 1830 bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) { 1831 return true; 1832 } 1833