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