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