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