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