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