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