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 : public Checker<check::PreCall, eval::Call, 197 check::DeadSymbols, check::PointerEscape> { 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 stream 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 ProgramStateRef checkPointerEscape(ProgramStateRef State, 227 const InvalidatedSymbols &Escaped, 228 const CallEvent *Call, 229 PointerEscapeKind Kind) const; 230 231 /// If true, evaluate special testing stream functions. 232 bool TestMode = false; 233 234 private: 235 CallDescriptionMap<FnDescription> FnDescriptions = { 236 {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 237 {{"freopen", 3}, 238 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, 239 {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 240 {{"fclose", 1}, 241 {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, 242 {{"fread", 4}, 243 {&StreamChecker::preFread, 244 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, 245 {{"fwrite", 4}, 246 {&StreamChecker::preFwrite, 247 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, 248 {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 249 {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 250 {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 251 {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 252 {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 253 {{"clearerr", 1}, 254 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, 255 {{"feof", 1}, 256 {&StreamChecker::preDefault, 257 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), 258 0}}, 259 {{"ferror", 1}, 260 {&StreamChecker::preDefault, 261 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError), 262 0}}, 263 {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 264 }; 265 266 CallDescriptionMap<FnDescription> FnTestDescriptions = { 267 {{"StreamTesterChecker_make_feof_stream", 1}, 268 {nullptr, 269 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof), 270 0}}, 271 {{"StreamTesterChecker_make_ferror_stream", 1}, 272 {nullptr, 273 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, 274 ErrorFError), 275 0}}, 276 }; 277 278 void evalFopen(const FnDescription *Desc, const CallEvent &Call, 279 CheckerContext &C) const; 280 281 void preFreopen(const FnDescription *Desc, const CallEvent &Call, 282 CheckerContext &C) const; 283 void evalFreopen(const FnDescription *Desc, const CallEvent &Call, 284 CheckerContext &C) const; 285 286 void evalFclose(const FnDescription *Desc, const CallEvent &Call, 287 CheckerContext &C) const; 288 289 void preFread(const FnDescription *Desc, const CallEvent &Call, 290 CheckerContext &C) const; 291 292 void preFwrite(const FnDescription *Desc, const CallEvent &Call, 293 CheckerContext &C) const; 294 295 void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, 296 CheckerContext &C, bool IsFread) const; 297 298 void preFseek(const FnDescription *Desc, const CallEvent &Call, 299 CheckerContext &C) const; 300 void evalFseek(const FnDescription *Desc, const CallEvent &Call, 301 CheckerContext &C) const; 302 303 void preDefault(const FnDescription *Desc, const CallEvent &Call, 304 CheckerContext &C) const; 305 306 void evalClearerr(const FnDescription *Desc, const CallEvent &Call, 307 CheckerContext &C) const; 308 309 void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call, 310 CheckerContext &C, 311 const StreamErrorState &ErrorKind) const; 312 313 void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call, 314 CheckerContext &C, 315 const StreamErrorState &ErrorKind) const; 316 317 /// Check that the stream (in StreamVal) is not NULL. 318 /// If it can only be NULL a fatal error is emitted and nullptr returned. 319 /// Otherwise the return value is a new state where the stream is constrained 320 /// to be non-null. 321 ProgramStateRef ensureStreamNonNull(SVal StreamVal, CheckerContext &C, 322 ProgramStateRef State) const; 323 324 /// Check that the stream is the opened state. 325 /// If the stream is known to be not opened an error is generated 326 /// and nullptr returned, otherwise the original state is returned. 327 ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C, 328 ProgramStateRef State) const; 329 330 /// Check that the stream has not an invalid ("indeterminate") file position, 331 /// generate warning for it. 332 /// (EOF is not an invalid position.) 333 /// The returned state can be nullptr if a fatal error was generated. 334 /// It can return non-null state if the stream has not an invalid position or 335 /// there is execution path with non-invalid position. 336 ProgramStateRef 337 ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C, 338 ProgramStateRef State) const; 339 340 /// Check the legality of the 'whence' argument of 'fseek'. 341 /// Generate error and return nullptr if it is found to be illegal. 342 /// Otherwise returns the state. 343 /// (State is not changed here because the "whence" value is already known.) 344 ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 345 ProgramStateRef State) const; 346 347 /// Generate warning about stream in EOF state. 348 /// There will be always a state transition into the passed State, 349 /// by the new non-fatal error node or (if failed) a normal transition, 350 /// to ensure uniform handling. 351 void reportFEofWarning(CheckerContext &C, ProgramStateRef State) const; 352 353 /// Find the description data of the function called by a call event. 354 /// Returns nullptr if no function is recognized. 355 const FnDescription *lookupFn(const CallEvent &Call) const { 356 // Recognize "global C functions" with only integral or pointer arguments 357 // (and matching name) as stream functions. 358 if (!Call.isGlobalCFunction()) 359 return nullptr; 360 for (auto P : Call.parameters()) { 361 QualType T = P->getType(); 362 if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) 363 return nullptr; 364 } 365 366 return FnDescriptions.lookup(Call); 367 } 368 369 /// Generate a message for BugReporterVisitor if the stored symbol is 370 /// marked as interesting by the actual bug report. 371 struct NoteFn { 372 const CheckerNameRef CheckerName; 373 SymbolRef StreamSym; 374 std::string Message; 375 376 std::string operator()(PathSensitiveBugReport &BR) const { 377 if (BR.isInteresting(StreamSym) && 378 CheckerName == BR.getBugType().getCheckerName()) 379 return Message; 380 381 return ""; 382 } 383 }; 384 385 const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym, 386 const std::string &Message) const { 387 return C.getNoteTag(NoteFn{getCheckerName(), StreamSym, Message}); 388 } 389 390 /// Searches for the ExplodedNode where the file descriptor was acquired for 391 /// StreamSym. 392 static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, 393 SymbolRef StreamSym, 394 CheckerContext &C); 395 }; 396 397 } // end anonymous namespace 398 399 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 400 401 inline void assertStreamStateOpened(const StreamState *SS) { 402 assert(SS->isOpened() && 403 "Previous create of error node for non-opened stream failed?"); 404 } 405 406 const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, 407 SymbolRef StreamSym, 408 CheckerContext &C) { 409 ProgramStateRef State = N->getState(); 410 // When bug type is resource leak, exploded node N may not have state info 411 // for leaked file descriptor, but predecessor should have it. 412 if (!State->get<StreamMap>(StreamSym)) 413 N = N->getFirstPred(); 414 415 const ExplodedNode *Pred = N; 416 while (N) { 417 State = N->getState(); 418 if (!State->get<StreamMap>(StreamSym)) 419 return Pred; 420 Pred = N; 421 N = N->getFirstPred(); 422 } 423 424 return nullptr; 425 } 426 427 void StreamChecker::checkPreCall(const CallEvent &Call, 428 CheckerContext &C) const { 429 const FnDescription *Desc = lookupFn(Call); 430 if (!Desc || !Desc->PreFn) 431 return; 432 433 Desc->PreFn(this, Desc, Call, C); 434 } 435 436 bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { 437 const FnDescription *Desc = lookupFn(Call); 438 if (!Desc && TestMode) 439 Desc = FnTestDescriptions.lookup(Call); 440 if (!Desc || !Desc->EvalFn) 441 return false; 442 443 Desc->EvalFn(this, Desc, Call, C); 444 445 return C.isDifferent(); 446 } 447 448 void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call, 449 CheckerContext &C) const { 450 ProgramStateRef State = C.getState(); 451 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 452 if (!CE) 453 return; 454 455 DefinedSVal RetVal = makeRetVal(C, CE); 456 SymbolRef RetSym = RetVal.getAsSymbol(); 457 assert(RetSym && "RetVal must be a symbol here."); 458 459 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 460 461 // Bifurcate the state into two: one with a valid FILE* pointer, the other 462 // with a NULL. 463 ProgramStateRef StateNotNull, StateNull; 464 std::tie(StateNotNull, StateNull) = 465 C.getConstraintManager().assumeDual(State, RetVal); 466 467 StateNotNull = 468 StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc)); 469 StateNull = 470 StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc)); 471 472 C.addTransition(StateNotNull, 473 constructNoteTag(C, RetSym, "Stream opened here")); 474 C.addTransition(StateNull); 475 } 476 477 void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call, 478 CheckerContext &C) const { 479 // Do not allow NULL as passed stream pointer but allow a closed stream. 480 ProgramStateRef State = C.getState(); 481 State = ensureStreamNonNull(getStreamArg(Desc, Call), C, State); 482 if (!State) 483 return; 484 485 C.addTransition(State); 486 } 487 488 void StreamChecker::evalFreopen(const FnDescription *Desc, 489 const CallEvent &Call, 490 CheckerContext &C) const { 491 ProgramStateRef State = C.getState(); 492 493 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 494 if (!CE) 495 return; 496 497 Optional<DefinedSVal> StreamVal = 498 getStreamArg(Desc, Call).getAs<DefinedSVal>(); 499 if (!StreamVal) 500 return; 501 502 SymbolRef StreamSym = StreamVal->getAsSymbol(); 503 // Do not care about concrete values for stream ("(FILE *)0x12345"?). 504 // FIXME: Can be stdin, stdout, stderr such values? 505 if (!StreamSym) 506 return; 507 508 // Do not handle untracked stream. It is probably escaped. 509 if (!State->get<StreamMap>(StreamSym)) 510 return; 511 512 // Generate state for non-failed case. 513 // Return value is the passed stream pointer. 514 // According to the documentations, the stream is closed first 515 // but any close error is ignored. The state changes to (or remains) opened. 516 ProgramStateRef StateRetNotNull = 517 State->BindExpr(CE, C.getLocationContext(), *StreamVal); 518 // Generate state for NULL return value. 519 // Stream switches to OpenFailed state. 520 ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(), 521 C.getSValBuilder().makeNull()); 522 523 StateRetNotNull = 524 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 525 StateRetNull = 526 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc)); 527 528 C.addTransition(StateRetNotNull, 529 constructNoteTag(C, StreamSym, "Stream reopened here")); 530 C.addTransition(StateRetNull); 531 } 532 533 void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, 534 CheckerContext &C) const { 535 ProgramStateRef State = C.getState(); 536 SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); 537 if (!Sym) 538 return; 539 540 const StreamState *SS = State->get<StreamMap>(Sym); 541 if (!SS) 542 return; 543 544 assertStreamStateOpened(SS); 545 546 // Close the File Descriptor. 547 // Regardless if the close fails or not, stream becomes "closed" 548 // and can not be used any more. 549 State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc)); 550 551 C.addTransition(State); 552 } 553 554 void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, 555 CheckerContext &C) const { 556 ProgramStateRef State = C.getState(); 557 SVal StreamVal = getStreamArg(Desc, Call); 558 State = ensureStreamNonNull(StreamVal, C, State); 559 if (!State) 560 return; 561 State = ensureStreamOpened(StreamVal, C, State); 562 if (!State) 563 return; 564 State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 565 if (!State) 566 return; 567 568 SymbolRef Sym = StreamVal.getAsSymbol(); 569 if (Sym && State->get<StreamMap>(Sym)) { 570 const StreamState *SS = State->get<StreamMap>(Sym); 571 if (SS->ErrorState & ErrorFEof) 572 reportFEofWarning(C, State); 573 } else { 574 C.addTransition(State); 575 } 576 } 577 578 void StreamChecker::preFwrite(const FnDescription *Desc, const CallEvent &Call, 579 CheckerContext &C) const { 580 ProgramStateRef State = C.getState(); 581 SVal StreamVal = getStreamArg(Desc, Call); 582 State = ensureStreamNonNull(StreamVal, C, State); 583 if (!State) 584 return; 585 State = ensureStreamOpened(StreamVal, C, State); 586 if (!State) 587 return; 588 State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 589 if (!State) 590 return; 591 592 C.addTransition(State); 593 } 594 595 void StreamChecker::evalFreadFwrite(const FnDescription *Desc, 596 const CallEvent &Call, CheckerContext &C, 597 bool IsFread) const { 598 ProgramStateRef State = C.getState(); 599 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 600 if (!StreamSym) 601 return; 602 603 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 604 if (!CE) 605 return; 606 607 Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); 608 if (!SizeVal) 609 return; 610 Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); 611 if (!NMembVal) 612 return; 613 614 const StreamState *SS = State->get<StreamMap>(StreamSym); 615 if (!SS) 616 return; 617 618 assertStreamStateOpened(SS); 619 620 // C'99 standard, §7.19.8.1.3, the return value of fread: 621 // The fread function returns the number of elements successfully read, which 622 // may be less than nmemb if a read error or end-of-file is encountered. If 623 // size or nmemb is zero, fread returns zero and the contents of the array and 624 // the state of the stream remain unchanged. 625 626 if (State->isNull(*SizeVal).isConstrainedTrue() || 627 State->isNull(*NMembVal).isConstrainedTrue()) { 628 // This is the "size or nmemb is zero" case. 629 // Just return 0, do nothing more (not clear the error flags). 630 State = bindInt(0, State, C, CE); 631 C.addTransition(State); 632 return; 633 } 634 635 // Generate a transition for the success state. 636 // If we know the state to be FEOF at fread, do not add a success state. 637 if (!IsFread || (SS->ErrorState != ErrorFEof)) { 638 ProgramStateRef StateNotFailed = 639 State->BindExpr(CE, C.getLocationContext(), *NMembVal); 640 if (StateNotFailed) { 641 StateNotFailed = StateNotFailed->set<StreamMap>( 642 StreamSym, StreamState::getOpened(Desc)); 643 C.addTransition(StateNotFailed); 644 } 645 } 646 647 // Add transition for the failed state. 648 Optional<NonLoc> RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 649 assert(RetVal && "Value should be NonLoc."); 650 ProgramStateRef StateFailed = 651 State->BindExpr(CE, C.getLocationContext(), *RetVal); 652 if (!StateFailed) 653 return; 654 auto Cond = C.getSValBuilder() 655 .evalBinOpNN(State, BO_LT, *RetVal, *NMembVal, 656 C.getASTContext().IntTy) 657 .getAs<DefinedOrUnknownSVal>(); 658 if (!Cond) 659 return; 660 StateFailed = StateFailed->assume(*Cond, true); 661 if (!StateFailed) 662 return; 663 664 StreamErrorState NewES; 665 if (IsFread) 666 NewES = (SS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError; 667 else 668 NewES = ErrorFError; 669 // If a (non-EOF) error occurs, the resulting value of the file position 670 // indicator for the stream is indeterminate. 671 StreamState NewState = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); 672 StateFailed = StateFailed->set<StreamMap>(StreamSym, NewState); 673 C.addTransition(StateFailed); 674 } 675 676 void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, 677 CheckerContext &C) const { 678 ProgramStateRef State = C.getState(); 679 SVal StreamVal = getStreamArg(Desc, Call); 680 State = ensureStreamNonNull(StreamVal, C, State); 681 if (!State) 682 return; 683 State = ensureStreamOpened(StreamVal, C, State); 684 if (!State) 685 return; 686 State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); 687 if (!State) 688 return; 689 690 C.addTransition(State); 691 } 692 693 void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, 694 CheckerContext &C) const { 695 ProgramStateRef State = C.getState(); 696 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 697 if (!StreamSym) 698 return; 699 700 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 701 if (!CE) 702 return; 703 704 // Ignore the call if the stream is not tracked. 705 if (!State->get<StreamMap>(StreamSym)) 706 return; 707 708 DefinedSVal RetVal = makeRetVal(C, CE); 709 710 // Make expression result. 711 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 712 713 // Bifurcate the state into failed and non-failed. 714 // Return zero on success, nonzero on error. 715 ProgramStateRef StateNotFailed, StateFailed; 716 std::tie(StateFailed, StateNotFailed) = 717 C.getConstraintManager().assumeDual(State, RetVal); 718 719 // Reset the state to opened with no error. 720 StateNotFailed = 721 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 722 // We get error. 723 // It is possible that fseek fails but sets none of the error flags. 724 // If fseek failed, assume that the file position becomes indeterminate in any 725 // case. 726 StateFailed = StateFailed->set<StreamMap>( 727 StreamSym, 728 StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true)); 729 730 C.addTransition(StateNotFailed); 731 C.addTransition(StateFailed); 732 } 733 734 void StreamChecker::evalClearerr(const FnDescription *Desc, 735 const CallEvent &Call, 736 CheckerContext &C) const { 737 ProgramStateRef State = C.getState(); 738 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 739 if (!StreamSym) 740 return; 741 742 const StreamState *SS = State->get<StreamMap>(StreamSym); 743 if (!SS) 744 return; 745 746 assertStreamStateOpened(SS); 747 748 // FilePositionIndeterminate is not cleared. 749 State = State->set<StreamMap>( 750 StreamSym, 751 StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate)); 752 C.addTransition(State); 753 } 754 755 void StreamChecker::evalFeofFerror(const FnDescription *Desc, 756 const CallEvent &Call, CheckerContext &C, 757 const StreamErrorState &ErrorKind) const { 758 ProgramStateRef State = C.getState(); 759 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 760 if (!StreamSym) 761 return; 762 763 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 764 if (!CE) 765 return; 766 767 const StreamState *SS = State->get<StreamMap>(StreamSym); 768 if (!SS) 769 return; 770 771 assertStreamStateOpened(SS); 772 773 if (SS->ErrorState & ErrorKind) { 774 // Execution path with error of ErrorKind. 775 // Function returns true. 776 // From now on it is the only one error state. 777 ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE); 778 C.addTransition(TrueState->set<StreamMap>( 779 StreamSym, StreamState::getOpened(Desc, ErrorKind, 780 SS->FilePositionIndeterminate && 781 !ErrorKind.isFEof()))); 782 } 783 if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) { 784 // Execution path(s) with ErrorKind not set. 785 // Function returns false. 786 // New error state is everything before minus ErrorKind. 787 ProgramStateRef FalseState = bindInt(0, State, C, CE); 788 C.addTransition(FalseState->set<StreamMap>( 789 StreamSym, 790 StreamState::getOpened( 791 Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof()))); 792 } 793 } 794 795 void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, 796 CheckerContext &C) const { 797 ProgramStateRef State = C.getState(); 798 SVal StreamVal = getStreamArg(Desc, Call); 799 State = ensureStreamNonNull(StreamVal, C, State); 800 if (!State) 801 return; 802 State = ensureStreamOpened(StreamVal, C, State); 803 if (!State) 804 return; 805 806 C.addTransition(State); 807 } 808 809 void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, 810 const CallEvent &Call, CheckerContext &C, 811 const StreamErrorState &ErrorKind) const { 812 ProgramStateRef State = C.getState(); 813 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 814 assert(StreamSym && "Operation not permitted on non-symbolic stream value."); 815 const StreamState *SS = State->get<StreamMap>(StreamSym); 816 assert(SS && "Stream should be tracked by the checker."); 817 State = State->set<StreamMap>( 818 StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind)); 819 C.addTransition(State); 820 } 821 822 ProgramStateRef 823 StreamChecker::ensureStreamNonNull(SVal StreamVal, CheckerContext &C, 824 ProgramStateRef State) const { 825 auto Stream = StreamVal.getAs<DefinedSVal>(); 826 if (!Stream) 827 return State; 828 829 ConstraintManager &CM = C.getConstraintManager(); 830 831 ProgramStateRef StateNotNull, StateNull; 832 std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream); 833 834 if (!StateNotNull && StateNull) { 835 if (ExplodedNode *N = C.generateErrorNode(StateNull)) { 836 C.emitReport(std::make_unique<PathSensitiveBugReport>( 837 BT_FileNull, BT_FileNull.getDescription(), N)); 838 } 839 return nullptr; 840 } 841 842 return StateNotNull; 843 } 844 845 ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, 846 CheckerContext &C, 847 ProgramStateRef State) const { 848 SymbolRef Sym = StreamVal.getAsSymbol(); 849 if (!Sym) 850 return State; 851 852 const StreamState *SS = State->get<StreamMap>(Sym); 853 if (!SS) 854 return State; 855 856 if (SS->isClosed()) { 857 // Using a stream pointer after 'fclose' causes undefined behavior 858 // according to cppreference.com . 859 ExplodedNode *N = C.generateErrorNode(); 860 if (N) { 861 C.emitReport(std::make_unique<PathSensitiveBugReport>( 862 BT_UseAfterClose, BT_UseAfterClose.getDescription(), N)); 863 return nullptr; 864 } 865 866 return State; 867 } 868 869 if (SS->isOpenFailed()) { 870 // Using a stream that has failed to open is likely to cause problems. 871 // This should usually not occur because stream pointer is NULL. 872 // But freopen can cause a state when stream pointer remains non-null but 873 // failed to open. 874 ExplodedNode *N = C.generateErrorNode(); 875 if (N) { 876 C.emitReport(std::make_unique<PathSensitiveBugReport>( 877 BT_UseAfterOpenFailed, BT_UseAfterOpenFailed.getDescription(), N)); 878 return nullptr; 879 } 880 return State; 881 } 882 883 return State; 884 } 885 886 ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate( 887 SVal StreamVal, CheckerContext &C, ProgramStateRef State) const { 888 SymbolRef Sym = StreamVal.getAsSymbol(); 889 if (!Sym) 890 return State; 891 892 const StreamState *SS = State->get<StreamMap>(Sym); 893 if (!SS) 894 return State; 895 896 assert(SS->isOpened() && "First ensure that stream is opened."); 897 898 if (SS->FilePositionIndeterminate) { 899 if (SS->ErrorState & ErrorFEof) { 900 // The error is unknown but may be FEOF. 901 // Continue analysis with the FEOF error state. 902 // Report warning because the other possible error states. 903 ExplodedNode *N = C.generateNonFatalErrorNode(State); 904 if (!N) 905 return nullptr; 906 907 C.emitReport(std::make_unique<PathSensitiveBugReport>( 908 BT_IndeterminatePosition, BT_IndeterminatePosition.getDescription(), 909 N)); 910 return State->set<StreamMap>( 911 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false)); 912 } 913 914 // Known or unknown error state without FEOF possible. 915 // Stop analysis, report error. 916 ExplodedNode *N = C.generateErrorNode(State); 917 if (N) 918 C.emitReport(std::make_unique<PathSensitiveBugReport>( 919 BT_IndeterminatePosition, BT_IndeterminatePosition.getDescription(), 920 N)); 921 922 return nullptr; 923 } 924 925 return State; 926 } 927 928 ProgramStateRef 929 StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 930 ProgramStateRef State) const { 931 Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>(); 932 if (!CI) 933 return State; 934 935 int64_t X = CI->getValue().getSExtValue(); 936 if (X >= 0 && X <= 2) 937 return State; 938 939 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 940 C.emitReport(std::make_unique<PathSensitiveBugReport>( 941 BT_IllegalWhence, BT_IllegalWhence.getDescription(), N)); 942 return nullptr; 943 } 944 945 return State; 946 } 947 948 void StreamChecker::reportFEofWarning(CheckerContext &C, 949 ProgramStateRef State) const { 950 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 951 C.emitReport(std::make_unique<PathSensitiveBugReport>( 952 BT_StreamEof, BT_StreamEof.getDescription(), N)); 953 return; 954 } 955 C.addTransition(State); 956 } 957 958 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 959 CheckerContext &C) const { 960 ProgramStateRef State = C.getState(); 961 962 // TODO: Clean up the state. 963 const StreamMapTy &Map = State->get<StreamMap>(); 964 for (const auto &I : Map) { 965 SymbolRef Sym = I.first; 966 const StreamState &SS = I.second; 967 if (!SymReaper.isDead(Sym) || !SS.isOpened()) 968 continue; 969 970 ExplodedNode *N = C.generateErrorNode(); 971 if (!N) 972 continue; 973 974 // Do not warn for non-closed stream at program exit. 975 ExplodedNode *Pred = C.getPredecessor(); 976 if (Pred && Pred->getCFGBlock() && 977 Pred->getCFGBlock()->hasNoReturnElement()) 978 continue; 979 980 // Resource leaks can result in multiple warning that describe the same kind 981 // of programming error: 982 // void f() { 983 // FILE *F = fopen("a.txt"); 984 // if (rand()) // state split 985 // return; // warning 986 // } // warning 987 // While this isn't necessarily true (leaking the same stream could result 988 // from a different kinds of errors), the reduction in redundant reports 989 // makes this a worthwhile heuristic. 990 // FIXME: Add a checker option to turn this uniqueing feature off. 991 992 const ExplodedNode *StreamOpenNode = getAcquisitionSite(N, Sym, C); 993 assert(StreamOpenNode && "Could not find place of stream opening."); 994 PathDiagnosticLocation LocUsedForUniqueing = 995 PathDiagnosticLocation::createBegin( 996 StreamOpenNode->getStmtForDiagnostics(), C.getSourceManager(), 997 StreamOpenNode->getLocationContext()); 998 999 std::unique_ptr<PathSensitiveBugReport> R = 1000 std::make_unique<PathSensitiveBugReport>( 1001 BT_ResourceLeak, BT_ResourceLeak.getDescription(), N, 1002 LocUsedForUniqueing, 1003 StreamOpenNode->getLocationContext()->getDecl()); 1004 R->markInteresting(Sym); 1005 C.emitReport(std::move(R)); 1006 } 1007 } 1008 1009 ProgramStateRef StreamChecker::checkPointerEscape( 1010 ProgramStateRef State, const InvalidatedSymbols &Escaped, 1011 const CallEvent *Call, PointerEscapeKind Kind) const { 1012 // Check for file-handling system call that is not handled by the checker. 1013 // FIXME: The checker should be updated to handle all system calls that take 1014 // 'FILE*' argument. These are now ignored. 1015 if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader()) 1016 return State; 1017 1018 for (SymbolRef Sym : Escaped) { 1019 // The symbol escaped. 1020 // From now the stream can be manipulated in unknown way to the checker, 1021 // it is not possible to handle it any more. 1022 // Optimistically, assume that the corresponding file handle will be closed 1023 // somewhere else. 1024 // Remove symbol from state so the following stream calls on this symbol are 1025 // not handled by the checker. 1026 State = State->remove<StreamMap>(Sym); 1027 } 1028 return State; 1029 } 1030 1031 void ento::registerStreamChecker(CheckerManager &Mgr) { 1032 Mgr.registerChecker<StreamChecker>(); 1033 } 1034 1035 bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) { 1036 return true; 1037 } 1038 1039 void ento::registerStreamTesterChecker(CheckerManager &Mgr) { 1040 auto *Checker = Mgr.getChecker<StreamChecker>(); 1041 Checker->TestMode = true; 1042 } 1043 1044 bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) { 1045 return true; 1046 } 1047