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