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/CallEvent.h" 18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 22 #include <functional> 23 24 using namespace clang; 25 using namespace ento; 26 using namespace std::placeholders; 27 28 namespace { 29 30 struct FnDescription; 31 32 /// State of the stream error flags. 33 /// Sometimes it is not known to the checker what error flags are set. 34 /// This is indicated by setting more than one flag to true. 35 /// This is an optimization to avoid state splits. 36 /// A stream can either be in FEOF or FERROR but not both at the same time. 37 /// Multiple flags are set to handle the corresponding states together. 38 struct StreamErrorState { 39 /// The stream can be in state where none of the error flags set. 40 bool NoError = true; 41 /// The stream can be in state where the EOF indicator is set. 42 bool FEof = false; 43 /// The stream can be in state where the error indicator is set. 44 bool FError = false; 45 46 bool isNoError() const { return NoError && !FEof && !FError; } 47 bool isFEof() const { return !NoError && FEof && !FError; } 48 bool isFError() const { return !NoError && !FEof && FError; } 49 50 bool operator==(const StreamErrorState &ES) const { 51 return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError; 52 } 53 54 StreamErrorState operator|(const StreamErrorState &E) const { 55 return {NoError || E.NoError, FEof || E.FEof, FError || E.FError}; 56 } 57 58 StreamErrorState operator&(const StreamErrorState &E) const { 59 return {NoError && E.NoError, FEof && E.FEof, FError && E.FError}; 60 } 61 62 StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; } 63 64 /// Returns if the StreamErrorState is a valid object. 65 operator bool() const { return NoError || FEof || FError; } 66 67 void Profile(llvm::FoldingSetNodeID &ID) const { 68 ID.AddBoolean(NoError); 69 ID.AddBoolean(FEof); 70 ID.AddBoolean(FError); 71 } 72 }; 73 74 const StreamErrorState ErrorNone{true, false, false}; 75 const StreamErrorState ErrorFEof{false, true, false}; 76 const StreamErrorState ErrorFError{false, false, true}; 77 78 /// Full state information about a stream pointer. 79 struct StreamState { 80 /// The last file operation called in the stream. 81 const FnDescription *LastOperation; 82 83 /// State of a stream symbol. 84 /// FIXME: We need maybe an "escaped" state later. 85 enum KindTy { 86 Opened, /// Stream is opened. 87 Closed, /// Closed stream (an invalid stream pointer after it was closed). 88 OpenFailed /// The last open operation has failed. 89 } State; 90 91 /// State of the error flags. 92 /// Ignored in non-opened stream state but must be NoError. 93 StreamErrorState ErrorState; 94 95 bool isOpened() const { return State == Opened; } 96 bool isClosed() const { return State == Closed; } 97 bool isOpenFailed() const { return State == OpenFailed; } 98 99 bool operator==(const StreamState &X) const { 100 // In not opened state error state should always NoError, so comparison 101 // here is no problem. 102 return LastOperation == X.LastOperation && State == X.State && 103 ErrorState == X.ErrorState; 104 } 105 106 static StreamState getOpened(const FnDescription *L, 107 const StreamErrorState &ES = {}) { 108 return StreamState{L, Opened, ES}; 109 } 110 static StreamState getClosed(const FnDescription *L) { 111 return StreamState{L, Closed, {}}; 112 } 113 static StreamState getOpenFailed(const FnDescription *L) { 114 return StreamState{L, OpenFailed, {}}; 115 } 116 117 void Profile(llvm::FoldingSetNodeID &ID) const { 118 ID.AddPointer(LastOperation); 119 ID.AddInteger(State); 120 ID.AddInteger(ErrorState); 121 } 122 }; 123 124 class StreamChecker; 125 using FnCheck = std::function<void(const StreamChecker *, const FnDescription *, 126 const CallEvent &, CheckerContext &)>; 127 128 using ArgNoTy = unsigned int; 129 static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max(); 130 131 struct FnDescription { 132 FnCheck PreFn; 133 FnCheck EvalFn; 134 ArgNoTy StreamArgNo; 135 }; 136 137 /// Get the value of the stream argument out of the passed call event. 138 /// The call should contain a function that is described by Desc. 139 SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) { 140 assert(Desc && Desc->StreamArgNo != ArgNone && 141 "Try to get a non-existing stream argument."); 142 return Call.getArgSVal(Desc->StreamArgNo); 143 } 144 145 /// Create a conjured symbol return value for a call expression. 146 DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) { 147 assert(CE && "Expecting a call expression."); 148 149 const LocationContext *LCtx = C.getLocationContext(); 150 return C.getSValBuilder() 151 .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()) 152 .castAs<DefinedSVal>(); 153 } 154 155 ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C, 156 const CallExpr *CE) { 157 DefinedSVal RetVal = makeRetVal(C, CE); 158 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 159 State = State->assume(RetVal, true); 160 assert(State && "Assumption on new value should not fail."); 161 return State; 162 } 163 164 ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, 165 CheckerContext &C, const CallExpr *CE) { 166 State = State->BindExpr(CE, C.getLocationContext(), 167 C.getSValBuilder().makeIntVal(Value, false)); 168 return State; 169 } 170 171 class StreamChecker 172 : public Checker<check::PreCall, eval::Call, check::DeadSymbols> { 173 mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence, 174 BT_UseAfterClose, BT_UseAfterOpenFailed, BT_ResourceLeak; 175 176 public: 177 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 178 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 179 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 180 181 /// If true, evaluate special testing stream functions. 182 bool TestMode = false; 183 184 private: 185 CallDescriptionMap<FnDescription> FnDescriptions = { 186 {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 187 {{"freopen", 3}, 188 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, 189 {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 190 {{"fclose", 1}, 191 {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, 192 {{"fread", 4}, {&StreamChecker::preDefault, nullptr, 3}}, 193 {{"fwrite", 4}, {&StreamChecker::preDefault, nullptr, 3}}, 194 {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 195 {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 196 {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 197 {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 198 {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 199 {{"clearerr", 1}, 200 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, 201 {{"feof", 1}, 202 {&StreamChecker::preDefault, 203 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), 204 0}}, 205 {{"ferror", 1}, 206 {&StreamChecker::preDefault, 207 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError), 208 0}}, 209 {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 210 }; 211 212 CallDescriptionMap<FnDescription> FnTestDescriptions = { 213 {{"StreamTesterChecker_make_feof_stream", 1}, 214 {nullptr, 215 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof), 216 0}}, 217 {{"StreamTesterChecker_make_ferror_stream", 1}, 218 {nullptr, 219 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, 220 ErrorFError), 221 0}}, 222 }; 223 224 void evalFopen(const FnDescription *Desc, const CallEvent &Call, 225 CheckerContext &C) const; 226 227 void preFreopen(const FnDescription *Desc, const CallEvent &Call, 228 CheckerContext &C) const; 229 void evalFreopen(const FnDescription *Desc, const CallEvent &Call, 230 CheckerContext &C) const; 231 232 void evalFclose(const FnDescription *Desc, const CallEvent &Call, 233 CheckerContext &C) const; 234 235 void preFseek(const FnDescription *Desc, const CallEvent &Call, 236 CheckerContext &C) const; 237 void evalFseek(const FnDescription *Desc, const CallEvent &Call, 238 CheckerContext &C) const; 239 240 void preDefault(const FnDescription *Desc, const CallEvent &Call, 241 CheckerContext &C) const; 242 243 void evalClearerr(const FnDescription *Desc, const CallEvent &Call, 244 CheckerContext &C) const; 245 246 void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call, 247 CheckerContext &C, 248 const StreamErrorState &ErrorKind) const; 249 250 void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call, 251 CheckerContext &C, 252 const StreamErrorState &ErrorKind) const; 253 254 /// Check that the stream (in StreamVal) is not NULL. 255 /// If it can only be NULL a fatal error is emitted and nullptr returned. 256 /// Otherwise the return value is a new state where the stream is constrained 257 /// to be non-null. 258 ProgramStateRef ensureStreamNonNull(SVal StreamVal, CheckerContext &C, 259 ProgramStateRef State) const; 260 261 /// Check that the stream is the opened state. 262 /// If the stream is known to be not opened an error is generated 263 /// and nullptr returned, otherwise the original state is returned. 264 ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C, 265 ProgramStateRef State) const; 266 267 /// Check the legality of the 'whence' argument of 'fseek'. 268 /// Generate error and return nullptr if it is found to be illegal. 269 /// Otherwise returns the state. 270 /// (State is not changed here because the "whence" value is already known.) 271 ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 272 ProgramStateRef State) const; 273 274 /// Find the description data of the function called by a call event. 275 /// Returns nullptr if no function is recognized. 276 const FnDescription *lookupFn(const CallEvent &Call) const { 277 // Recognize "global C functions" with only integral or pointer arguments 278 // (and matching name) as stream functions. 279 if (!Call.isGlobalCFunction()) 280 return nullptr; 281 for (auto P : Call.parameters()) { 282 QualType T = P->getType(); 283 if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) 284 return nullptr; 285 } 286 287 return FnDescriptions.lookup(Call); 288 } 289 }; 290 291 } // end anonymous namespace 292 293 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 294 295 inline void assertStreamStateOpened(const StreamState *SS) { 296 assert(SS->isOpened() && 297 "Previous create of error node for non-opened stream failed?"); 298 } 299 300 void StreamChecker::checkPreCall(const CallEvent &Call, 301 CheckerContext &C) const { 302 const FnDescription *Desc = lookupFn(Call); 303 if (!Desc || !Desc->PreFn) 304 return; 305 306 Desc->PreFn(this, Desc, Call, C); 307 } 308 309 bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { 310 const FnDescription *Desc = lookupFn(Call); 311 if (!Desc && TestMode) 312 Desc = FnTestDescriptions.lookup(Call); 313 if (!Desc || !Desc->EvalFn) 314 return false; 315 316 Desc->EvalFn(this, Desc, Call, C); 317 318 return C.isDifferent(); 319 } 320 321 void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call, 322 CheckerContext &C) const { 323 ProgramStateRef State = C.getState(); 324 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 325 if (!CE) 326 return; 327 328 DefinedSVal RetVal = makeRetVal(C, CE); 329 SymbolRef RetSym = RetVal.getAsSymbol(); 330 assert(RetSym && "RetVal must be a symbol here."); 331 332 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 333 334 // Bifurcate the state into two: one with a valid FILE* pointer, the other 335 // with a NULL. 336 ProgramStateRef StateNotNull, StateNull; 337 std::tie(StateNotNull, StateNull) = 338 C.getConstraintManager().assumeDual(State, RetVal); 339 340 StateNotNull = 341 StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc)); 342 StateNull = 343 StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc)); 344 345 C.addTransition(StateNotNull); 346 C.addTransition(StateNull); 347 } 348 349 void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call, 350 CheckerContext &C) const { 351 // Do not allow NULL as passed stream pointer but allow a closed stream. 352 ProgramStateRef State = C.getState(); 353 State = ensureStreamNonNull(getStreamArg(Desc, Call), C, State); 354 if (!State) 355 return; 356 357 C.addTransition(State); 358 } 359 360 void StreamChecker::evalFreopen(const FnDescription *Desc, 361 const CallEvent &Call, 362 CheckerContext &C) const { 363 ProgramStateRef State = C.getState(); 364 365 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 366 if (!CE) 367 return; 368 369 Optional<DefinedSVal> StreamVal = 370 getStreamArg(Desc, Call).getAs<DefinedSVal>(); 371 if (!StreamVal) 372 return; 373 374 SymbolRef StreamSym = StreamVal->getAsSymbol(); 375 // Do not care about concrete values for stream ("(FILE *)0x12345"?). 376 // FIXME: Are stdin, stdout, stderr such values? 377 if (!StreamSym) 378 return; 379 380 // Generate state for non-failed case. 381 // Return value is the passed stream pointer. 382 // According to the documentations, the stream is closed first 383 // but any close error is ignored. The state changes to (or remains) opened. 384 ProgramStateRef StateRetNotNull = 385 State->BindExpr(CE, C.getLocationContext(), *StreamVal); 386 // Generate state for NULL return value. 387 // Stream switches to OpenFailed state. 388 ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(), 389 C.getSValBuilder().makeNull()); 390 391 StateRetNotNull = 392 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 393 StateRetNull = 394 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc)); 395 396 C.addTransition(StateRetNotNull); 397 C.addTransition(StateRetNull); 398 } 399 400 void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, 401 CheckerContext &C) const { 402 ProgramStateRef State = C.getState(); 403 SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); 404 if (!Sym) 405 return; 406 407 const StreamState *SS = State->get<StreamMap>(Sym); 408 if (!SS) 409 return; 410 411 assertStreamStateOpened(SS); 412 413 // Close the File Descriptor. 414 // Regardless if the close fails or not, stream becomes "closed" 415 // and can not be used any more. 416 State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc)); 417 418 C.addTransition(State); 419 } 420 421 void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, 422 CheckerContext &C) const { 423 ProgramStateRef State = C.getState(); 424 SVal StreamVal = getStreamArg(Desc, Call); 425 State = ensureStreamNonNull(StreamVal, C, State); 426 if (!State) 427 return; 428 State = ensureStreamOpened(StreamVal, C, State); 429 if (!State) 430 return; 431 State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); 432 if (!State) 433 return; 434 435 C.addTransition(State); 436 } 437 438 void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, 439 CheckerContext &C) const { 440 ProgramStateRef State = C.getState(); 441 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 442 if (!StreamSym) 443 return; 444 445 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 446 if (!CE) 447 return; 448 449 // Ignore the call if the stream is not tracked. 450 if (!State->get<StreamMap>(StreamSym)) 451 return; 452 453 DefinedSVal RetVal = makeRetVal(C, CE); 454 455 // Make expression result. 456 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 457 458 // Bifurcate the state into failed and non-failed. 459 // Return zero on success, nonzero on error. 460 ProgramStateRef StateNotFailed, StateFailed; 461 std::tie(StateFailed, StateNotFailed) = 462 C.getConstraintManager().assumeDual(State, RetVal); 463 464 // Reset the state to opened with no error. 465 StateNotFailed = 466 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 467 // We get error. 468 // It is possible that fseek fails but sets none of the error flags. 469 StateFailed = StateFailed->set<StreamMap>( 470 StreamSym, 471 StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError)); 472 473 C.addTransition(StateNotFailed); 474 C.addTransition(StateFailed); 475 } 476 477 void StreamChecker::evalClearerr(const FnDescription *Desc, 478 const CallEvent &Call, 479 CheckerContext &C) const { 480 ProgramStateRef State = C.getState(); 481 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 482 if (!StreamSym) 483 return; 484 485 const StreamState *SS = State->get<StreamMap>(StreamSym); 486 if (!SS) 487 return; 488 489 assertStreamStateOpened(SS); 490 491 State = State->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 492 C.addTransition(State); 493 } 494 495 void StreamChecker::evalFeofFerror(const FnDescription *Desc, 496 const CallEvent &Call, CheckerContext &C, 497 const StreamErrorState &ErrorKind) const { 498 ProgramStateRef State = C.getState(); 499 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 500 if (!StreamSym) 501 return; 502 503 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 504 if (!CE) 505 return; 506 507 const StreamState *SS = State->get<StreamMap>(StreamSym); 508 if (!SS) 509 return; 510 511 assertStreamStateOpened(SS); 512 513 if (SS->ErrorState & ErrorKind) { 514 // Execution path with error of ErrorKind. 515 // Function returns true. 516 // From now on it is the only one error state. 517 ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE); 518 C.addTransition(TrueState->set<StreamMap>( 519 StreamSym, StreamState::getOpened(Desc, ErrorKind))); 520 } 521 if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) { 522 // Execution path(s) with ErrorKind not set. 523 // Function returns false. 524 // New error state is everything before minus ErrorKind. 525 ProgramStateRef FalseState = bindInt(0, State, C, CE); 526 C.addTransition(FalseState->set<StreamMap>( 527 StreamSym, StreamState::getOpened(Desc, NewES))); 528 } 529 } 530 531 void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, 532 CheckerContext &C) const { 533 ProgramStateRef State = C.getState(); 534 SVal StreamVal = getStreamArg(Desc, Call); 535 State = ensureStreamNonNull(StreamVal, C, State); 536 if (!State) 537 return; 538 State = ensureStreamOpened(StreamVal, C, State); 539 if (!State) 540 return; 541 542 C.addTransition(State); 543 } 544 545 void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, 546 const CallEvent &Call, CheckerContext &C, 547 const StreamErrorState &ErrorKind) const { 548 ProgramStateRef State = C.getState(); 549 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 550 assert(StreamSym && "Operation not permitted on non-symbolic stream value."); 551 const StreamState *SS = State->get<StreamMap>(StreamSym); 552 assert(SS && "Stream should be tracked by the checker."); 553 State = State->set<StreamMap>( 554 StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind)); 555 C.addTransition(State); 556 } 557 558 ProgramStateRef 559 StreamChecker::ensureStreamNonNull(SVal StreamVal, CheckerContext &C, 560 ProgramStateRef State) const { 561 auto Stream = StreamVal.getAs<DefinedSVal>(); 562 if (!Stream) 563 return State; 564 565 ConstraintManager &CM = C.getConstraintManager(); 566 567 ProgramStateRef StateNotNull, StateNull; 568 std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream); 569 570 if (!StateNotNull && StateNull) { 571 if (ExplodedNode *N = C.generateErrorNode(StateNull)) { 572 if (!BT_nullfp) 573 BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer", 574 "Stream pointer might be NULL.")); 575 C.emitReport(std::make_unique<PathSensitiveBugReport>( 576 *BT_nullfp, BT_nullfp->getDescription(), N)); 577 } 578 return nullptr; 579 } 580 581 return StateNotNull; 582 } 583 584 ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, 585 CheckerContext &C, 586 ProgramStateRef State) const { 587 SymbolRef Sym = StreamVal.getAsSymbol(); 588 if (!Sym) 589 return State; 590 591 const StreamState *SS = State->get<StreamMap>(Sym); 592 if (!SS) 593 return State; 594 595 if (SS->isClosed()) { 596 // Using a stream pointer after 'fclose' causes undefined behavior 597 // according to cppreference.com . 598 ExplodedNode *N = C.generateErrorNode(); 599 if (N) { 600 if (!BT_UseAfterClose) 601 BT_UseAfterClose.reset(new BuiltinBug(this, "Closed stream", 602 "Stream might be already closed. " 603 "Causes undefined behaviour.")); 604 C.emitReport(std::make_unique<PathSensitiveBugReport>( 605 *BT_UseAfterClose, BT_UseAfterClose->getDescription(), N)); 606 return nullptr; 607 } 608 609 return State; 610 } 611 612 if (SS->isOpenFailed()) { 613 // Using a stream that has failed to open is likely to cause problems. 614 // This should usually not occur because stream pointer is NULL. 615 // But freopen can cause a state when stream pointer remains non-null but 616 // failed to open. 617 ExplodedNode *N = C.generateErrorNode(); 618 if (N) { 619 if (!BT_UseAfterOpenFailed) 620 BT_UseAfterOpenFailed.reset( 621 new BuiltinBug(this, "Invalid stream", 622 "Stream might be invalid after " 623 "(re-)opening it has failed. " 624 "Can cause undefined behaviour.")); 625 C.emitReport(std::make_unique<PathSensitiveBugReport>( 626 *BT_UseAfterOpenFailed, BT_UseAfterOpenFailed->getDescription(), N)); 627 return nullptr; 628 } 629 return State; 630 } 631 632 return State; 633 } 634 635 ProgramStateRef 636 StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 637 ProgramStateRef State) const { 638 Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>(); 639 if (!CI) 640 return State; 641 642 int64_t X = CI->getValue().getSExtValue(); 643 if (X >= 0 && X <= 2) 644 return State; 645 646 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 647 if (!BT_illegalwhence) 648 BT_illegalwhence.reset( 649 new BuiltinBug(this, "Illegal whence argument", 650 "The whence argument to fseek() should be " 651 "SEEK_SET, SEEK_END, or SEEK_CUR.")); 652 C.emitReport(std::make_unique<PathSensitiveBugReport>( 653 *BT_illegalwhence, BT_illegalwhence->getDescription(), N)); 654 return nullptr; 655 } 656 657 return State; 658 } 659 660 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 661 CheckerContext &C) const { 662 ProgramStateRef State = C.getState(); 663 664 // TODO: Clean up the state. 665 const StreamMapTy &Map = State->get<StreamMap>(); 666 for (const auto &I : Map) { 667 SymbolRef Sym = I.first; 668 const StreamState &SS = I.second; 669 if (!SymReaper.isDead(Sym) || !SS.isOpened()) 670 continue; 671 672 ExplodedNode *N = C.generateErrorNode(); 673 if (!N) 674 continue; 675 676 if (!BT_ResourceLeak) 677 BT_ResourceLeak.reset( 678 new BuiltinBug(this, "Resource Leak", 679 "Opened File never closed. Potential Resource leak.")); 680 C.emitReport(std::make_unique<PathSensitiveBugReport>( 681 *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N)); 682 } 683 } 684 685 void ento::registerStreamChecker(CheckerManager &Mgr) { 686 Mgr.registerChecker<StreamChecker>(); 687 } 688 689 bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) { 690 return true; 691 } 692 693 void ento::registerStreamTesterChecker(CheckerManager &Mgr) { 694 auto *Checker = Mgr.getChecker<StreamChecker>(); 695 Checker->TestMode = true; 696 } 697 698 bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) { 699 return true; 700 } 701