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