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 //===----------------------------------------------------------------------===// 29 // Definition of state data structures. 30 //===----------------------------------------------------------------------===// 31 32 namespace { 33 34 struct FnDescription; 35 36 /// State of the stream error flags. 37 /// Sometimes it is not known to the checker what error flags are set. 38 /// This is indicated by setting more than one flag to true. 39 /// This is an optimization to avoid state splits. 40 /// A stream can either be in FEOF or FERROR but not both at the same time. 41 /// Multiple flags are set to handle the corresponding states together. 42 struct StreamErrorState { 43 /// The stream can be in state where none of the error flags set. 44 bool NoError = true; 45 /// The stream can be in state where the EOF indicator is set. 46 bool FEof = false; 47 /// The stream can be in state where the error indicator is set. 48 bool FError = false; 49 50 bool isNoError() const { return NoError && !FEof && !FError; } 51 bool isFEof() const { return !NoError && FEof && !FError; } 52 bool isFError() const { return !NoError && !FEof && FError; } 53 54 bool operator==(const StreamErrorState &ES) const { 55 return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError; 56 } 57 58 bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); } 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 StreamErrorState &E) const { 65 return {NoError && E.NoError, FEof && E.FEof, FError && E.FError}; 66 } 67 68 StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; } 69 70 /// Returns if the StreamErrorState is a valid object. 71 operator bool() const { return NoError || FEof || FError; } 72 73 void Profile(llvm::FoldingSetNodeID &ID) const { 74 ID.AddBoolean(NoError); 75 ID.AddBoolean(FEof); 76 ID.AddBoolean(FError); 77 } 78 }; 79 80 const StreamErrorState ErrorNone{true, false, false}; 81 const StreamErrorState ErrorFEof{false, true, false}; 82 const StreamErrorState ErrorFError{false, false, true}; 83 84 /// Full state information about a stream pointer. 85 struct StreamState { 86 /// The last file operation called in the stream. 87 const FnDescription *LastOperation; 88 89 /// State of a stream symbol. 90 /// FIXME: We need maybe an "escaped" state later. 91 enum KindTy { 92 Opened, /// Stream is opened. 93 Closed, /// Closed stream (an invalid stream pointer after it was closed). 94 OpenFailed /// The last open operation has failed. 95 } State; 96 97 /// State of the error flags. 98 /// Ignored in non-opened stream state but must be NoError. 99 StreamErrorState const ErrorState; 100 101 /// Indicate if the file has an "indeterminate file position indicator". 102 /// This can be set at a failing read or write or seek operation. 103 /// If it is set no more read or write is allowed. 104 /// This value is not dependent on the stream error flags: 105 /// The error flag may be cleared with `clearerr` but the file position 106 /// remains still indeterminate. 107 /// This value applies to all error states in ErrorState except FEOF. 108 /// An EOF+indeterminate state is the same as EOF state. 109 bool const FilePositionIndeterminate = false; 110 111 StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES, 112 bool IsFilePositionIndeterminate) 113 : LastOperation(L), State(S), ErrorState(ES), 114 FilePositionIndeterminate(IsFilePositionIndeterminate) { 115 assert((!ES.isFEof() || !IsFilePositionIndeterminate) && 116 "FilePositionIndeterminate should be false in FEof case."); 117 assert((State == Opened || ErrorState.isNoError()) && 118 "ErrorState should be None in non-opened stream state."); 119 } 120 121 bool isOpened() const { return State == Opened; } 122 bool isClosed() const { return State == Closed; } 123 bool isOpenFailed() const { return State == OpenFailed; } 124 125 bool operator==(const StreamState &X) const { 126 // In not opened state error state should always NoError, so comparison 127 // here is no problem. 128 return LastOperation == X.LastOperation && State == X.State && 129 ErrorState == X.ErrorState && 130 FilePositionIndeterminate == X.FilePositionIndeterminate; 131 } 132 133 static StreamState getOpened(const FnDescription *L, 134 const StreamErrorState &ES = ErrorNone, 135 bool IsFilePositionIndeterminate = false) { 136 return StreamState{L, Opened, ES, IsFilePositionIndeterminate}; 137 } 138 static StreamState getClosed(const FnDescription *L) { 139 return StreamState{L, Closed, {}, false}; 140 } 141 static StreamState getOpenFailed(const FnDescription *L) { 142 return StreamState{L, OpenFailed, {}, false}; 143 } 144 145 void Profile(llvm::FoldingSetNodeID &ID) const { 146 ID.AddPointer(LastOperation); 147 ID.AddInteger(State); 148 ID.AddInteger(ErrorState); 149 ID.AddBoolean(FilePositionIndeterminate); 150 } 151 }; 152 153 } // namespace 154 155 //===----------------------------------------------------------------------===// 156 // StreamChecker class and utility functions. 157 //===----------------------------------------------------------------------===// 158 159 namespace { 160 161 class StreamChecker; 162 using FnCheck = std::function<void(const StreamChecker *, const FnDescription *, 163 const CallEvent &, CheckerContext &)>; 164 165 using ArgNoTy = unsigned int; 166 static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max(); 167 168 struct FnDescription { 169 FnCheck PreFn; 170 FnCheck EvalFn; 171 ArgNoTy StreamArgNo; 172 }; 173 174 /// Get the value of the stream argument out of the passed call event. 175 /// The call should contain a function that is described by Desc. 176 SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) { 177 assert(Desc && Desc->StreamArgNo != ArgNone && 178 "Try to get a non-existing stream argument."); 179 return Call.getArgSVal(Desc->StreamArgNo); 180 } 181 182 /// Create a conjured symbol return value for a call expression. 183 DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) { 184 assert(CE && "Expecting a call expression."); 185 186 const LocationContext *LCtx = C.getLocationContext(); 187 return C.getSValBuilder() 188 .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()) 189 .castAs<DefinedSVal>(); 190 } 191 192 ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C, 193 const CallExpr *CE) { 194 DefinedSVal RetVal = makeRetVal(C, CE); 195 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 196 State = State->assume(RetVal, true); 197 assert(State && "Assumption on new value should not fail."); 198 return State; 199 } 200 201 ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, 202 CheckerContext &C, const CallExpr *CE) { 203 State = State->BindExpr(CE, C.getLocationContext(), 204 C.getSValBuilder().makeIntVal(Value, false)); 205 return State; 206 } 207 208 class StreamChecker : public Checker<check::PreCall, eval::Call, 209 check::DeadSymbols, check::PointerEscape> { 210 BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"}; 211 BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"}; 212 BugType BT_UseAfterOpenFailed{this, "Invalid stream", 213 "Stream handling error"}; 214 BugType BT_IndeterminatePosition{this, "Invalid stream state", 215 "Stream handling error"}; 216 BugType BT_IllegalWhence{this, "Illegal whence argument", 217 "Stream handling error"}; 218 BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"}; 219 BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error", 220 /*SuppressOnSink =*/true}; 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 const BugType *getBT_StreamEof() const { return &BT_StreamEof; } 235 236 private: 237 CallDescriptionMap<FnDescription> FnDescriptions = { 238 {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 239 {{"freopen", 3}, 240 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, 241 {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 242 {{"fclose", 1}, 243 {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, 244 {{"fread", 4}, 245 {&StreamChecker::preFread, 246 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, 247 {{"fwrite", 4}, 248 {&StreamChecker::preFwrite, 249 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, 250 {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 251 {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 252 {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 253 {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 254 {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 255 {{"clearerr", 1}, 256 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, 257 {{"feof", 1}, 258 {&StreamChecker::preDefault, 259 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), 260 0}}, 261 {{"ferror", 1}, 262 {&StreamChecker::preDefault, 263 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError), 264 0}}, 265 {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 266 }; 267 268 CallDescriptionMap<FnDescription> FnTestDescriptions = { 269 {{"StreamTesterChecker_make_feof_stream", 1}, 270 {nullptr, 271 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof), 272 0}}, 273 {{"StreamTesterChecker_make_ferror_stream", 1}, 274 {nullptr, 275 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, 276 ErrorFError), 277 0}}, 278 }; 279 280 void evalFopen(const FnDescription *Desc, const CallEvent &Call, 281 CheckerContext &C) const; 282 283 void preFreopen(const FnDescription *Desc, const CallEvent &Call, 284 CheckerContext &C) const; 285 void evalFreopen(const FnDescription *Desc, const CallEvent &Call, 286 CheckerContext &C) const; 287 288 void evalFclose(const FnDescription *Desc, const CallEvent &Call, 289 CheckerContext &C) const; 290 291 void preFread(const FnDescription *Desc, const CallEvent &Call, 292 CheckerContext &C) const; 293 294 void preFwrite(const FnDescription *Desc, const CallEvent &Call, 295 CheckerContext &C) const; 296 297 void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, 298 CheckerContext &C, bool IsFread) const; 299 300 void preFseek(const FnDescription *Desc, const CallEvent &Call, 301 CheckerContext &C) const; 302 void evalFseek(const FnDescription *Desc, const CallEvent &Call, 303 CheckerContext &C) const; 304 305 void preDefault(const FnDescription *Desc, const CallEvent &Call, 306 CheckerContext &C) const; 307 308 void evalClearerr(const FnDescription *Desc, const CallEvent &Call, 309 CheckerContext &C) const; 310 311 void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call, 312 CheckerContext &C, 313 const StreamErrorState &ErrorKind) const; 314 315 void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call, 316 CheckerContext &C, 317 const StreamErrorState &ErrorKind) const; 318 319 /// Check that the stream (in StreamVal) is not NULL. 320 /// If it can only be NULL a fatal error is emitted and nullptr returned. 321 /// Otherwise the return value is a new state where the stream is constrained 322 /// to be non-null. 323 ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, 324 CheckerContext &C, 325 ProgramStateRef State) const; 326 327 /// Check that the stream is the opened state. 328 /// If the stream is known to be not opened an error is generated 329 /// and nullptr returned, otherwise the original state is returned. 330 ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C, 331 ProgramStateRef State) const; 332 333 /// Check that the stream has not an invalid ("indeterminate") file position, 334 /// generate warning for it. 335 /// (EOF is not an invalid position.) 336 /// The returned state can be nullptr if a fatal error was generated. 337 /// It can return non-null state if the stream has not an invalid position or 338 /// there is execution path with non-invalid position. 339 ProgramStateRef 340 ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C, 341 ProgramStateRef State) const; 342 343 /// Check the legality of the 'whence' argument of 'fseek'. 344 /// Generate error and return nullptr if it is found to be illegal. 345 /// Otherwise returns the state. 346 /// (State is not changed here because the "whence" value is already known.) 347 ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 348 ProgramStateRef State) const; 349 350 /// Generate warning about stream in EOF state. 351 /// There will be always a state transition into the passed State, 352 /// by the new non-fatal error node or (if failed) a normal transition, 353 /// to ensure uniform handling. 354 void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C, 355 ProgramStateRef State) const; 356 357 /// Emit resource leak warnings for the given symbols. 358 /// Createn a non-fatal error node for these, and returns it (if any warnings 359 /// were generated). Return value is non-null. 360 ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 361 CheckerContext &C, ExplodedNode *Pred) const; 362 363 /// Find the description data of the function called by a call event. 364 /// Returns nullptr if no function is recognized. 365 const FnDescription *lookupFn(const CallEvent &Call) const { 366 // Recognize "global C functions" with only integral or pointer arguments 367 // (and matching name) as stream functions. 368 if (!Call.isGlobalCFunction()) 369 return nullptr; 370 for (auto P : Call.parameters()) { 371 QualType T = P->getType(); 372 if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) 373 return nullptr; 374 } 375 376 return FnDescriptions.lookup(Call); 377 } 378 379 /// Generate a message for BugReporterVisitor if the stored symbol is 380 /// marked as interesting by the actual bug report. 381 // FIXME: Use lambda instead. 382 struct NoteFn { 383 const BugType *BT_ResourceLeak; 384 SymbolRef StreamSym; 385 std::string Message; 386 387 std::string operator()(PathSensitiveBugReport &BR) const { 388 if (BR.isInteresting(StreamSym) && &BR.getBugType() == BT_ResourceLeak) 389 return Message; 390 391 return ""; 392 } 393 }; 394 395 const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym, 396 const std::string &Message) const { 397 return C.getNoteTag(NoteFn{&BT_ResourceLeak, StreamSym, Message}); 398 } 399 400 const NoteTag *constructSetEofNoteTag(CheckerContext &C, 401 SymbolRef StreamSym) const { 402 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) { 403 if (!BR.isInteresting(StreamSym) || 404 &BR.getBugType() != this->getBT_StreamEof()) 405 return ""; 406 407 BR.markNotInteresting(StreamSym); 408 409 return "Assuming stream reaches end-of-file here"; 410 }); 411 } 412 413 /// Searches for the ExplodedNode where the file descriptor was acquired for 414 /// StreamSym. 415 static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, 416 SymbolRef StreamSym, 417 CheckerContext &C); 418 }; 419 420 } // end anonymous namespace 421 422 // This map holds the state of a stream. 423 // The stream is identified with a SymbolRef that is created when a stream 424 // opening function is modeled by the checker. 425 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 426 427 inline void assertStreamStateOpened(const StreamState *SS) { 428 assert(SS->isOpened() && 429 "Previous create of error node for non-opened stream failed?"); 430 } 431 432 const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, 433 SymbolRef StreamSym, 434 CheckerContext &C) { 435 ProgramStateRef State = N->getState(); 436 // When bug type is resource leak, exploded node N may not have state info 437 // for leaked file descriptor, but predecessor should have it. 438 if (!State->get<StreamMap>(StreamSym)) 439 N = N->getFirstPred(); 440 441 const ExplodedNode *Pred = N; 442 while (N) { 443 State = N->getState(); 444 if (!State->get<StreamMap>(StreamSym)) 445 return Pred; 446 Pred = N; 447 N = N->getFirstPred(); 448 } 449 450 return nullptr; 451 } 452 453 //===----------------------------------------------------------------------===// 454 // Methods of StreamChecker. 455 //===----------------------------------------------------------------------===// 456 457 void StreamChecker::checkPreCall(const CallEvent &Call, 458 CheckerContext &C) const { 459 const FnDescription *Desc = lookupFn(Call); 460 if (!Desc || !Desc->PreFn) 461 return; 462 463 Desc->PreFn(this, Desc, Call, C); 464 } 465 466 bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { 467 const FnDescription *Desc = lookupFn(Call); 468 if (!Desc && TestMode) 469 Desc = FnTestDescriptions.lookup(Call); 470 if (!Desc || !Desc->EvalFn) 471 return false; 472 473 Desc->EvalFn(this, Desc, Call, C); 474 475 return C.isDifferent(); 476 } 477 478 void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call, 479 CheckerContext &C) const { 480 ProgramStateRef State = C.getState(); 481 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 482 if (!CE) 483 return; 484 485 DefinedSVal RetVal = makeRetVal(C, CE); 486 SymbolRef RetSym = RetVal.getAsSymbol(); 487 assert(RetSym && "RetVal must be a symbol here."); 488 489 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 490 491 // Bifurcate the state into two: one with a valid FILE* pointer, the other 492 // with a NULL. 493 ProgramStateRef StateNotNull, StateNull; 494 std::tie(StateNotNull, StateNull) = 495 C.getConstraintManager().assumeDual(State, RetVal); 496 497 StateNotNull = 498 StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc)); 499 StateNull = 500 StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc)); 501 502 C.addTransition(StateNotNull, 503 constructNoteTag(C, RetSym, "Stream opened here")); 504 C.addTransition(StateNull); 505 } 506 507 void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call, 508 CheckerContext &C) const { 509 // Do not allow NULL as passed stream pointer but allow a closed stream. 510 ProgramStateRef State = C.getState(); 511 State = ensureStreamNonNull(getStreamArg(Desc, Call), 512 Call.getArgExpr(Desc->StreamArgNo), C, State); 513 if (!State) 514 return; 515 516 C.addTransition(State); 517 } 518 519 void StreamChecker::evalFreopen(const FnDescription *Desc, 520 const CallEvent &Call, 521 CheckerContext &C) const { 522 ProgramStateRef State = C.getState(); 523 524 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 525 if (!CE) 526 return; 527 528 Optional<DefinedSVal> StreamVal = 529 getStreamArg(Desc, Call).getAs<DefinedSVal>(); 530 if (!StreamVal) 531 return; 532 533 SymbolRef StreamSym = StreamVal->getAsSymbol(); 534 // Do not care about concrete values for stream ("(FILE *)0x12345"?). 535 // FIXME: Can be stdin, stdout, stderr such values? 536 if (!StreamSym) 537 return; 538 539 // Do not handle untracked stream. It is probably escaped. 540 if (!State->get<StreamMap>(StreamSym)) 541 return; 542 543 // Generate state for non-failed case. 544 // Return value is the passed stream pointer. 545 // According to the documentations, the stream is closed first 546 // but any close error is ignored. The state changes to (or remains) opened. 547 ProgramStateRef StateRetNotNull = 548 State->BindExpr(CE, C.getLocationContext(), *StreamVal); 549 // Generate state for NULL return value. 550 // Stream switches to OpenFailed state. 551 ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(), 552 C.getSValBuilder().makeNull()); 553 554 StateRetNotNull = 555 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 556 StateRetNull = 557 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc)); 558 559 C.addTransition(StateRetNotNull, 560 constructNoteTag(C, StreamSym, "Stream reopened here")); 561 C.addTransition(StateRetNull); 562 } 563 564 void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, 565 CheckerContext &C) const { 566 ProgramStateRef State = C.getState(); 567 SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); 568 if (!Sym) 569 return; 570 571 const StreamState *SS = State->get<StreamMap>(Sym); 572 if (!SS) 573 return; 574 575 assertStreamStateOpened(SS); 576 577 // Close the File Descriptor. 578 // Regardless if the close fails or not, stream becomes "closed" 579 // and can not be used any more. 580 State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc)); 581 582 C.addTransition(State); 583 } 584 585 void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, 586 CheckerContext &C) const { 587 ProgramStateRef State = C.getState(); 588 SVal StreamVal = getStreamArg(Desc, Call); 589 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 590 State); 591 if (!State) 592 return; 593 State = ensureStreamOpened(StreamVal, C, State); 594 if (!State) 595 return; 596 State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 597 if (!State) 598 return; 599 600 SymbolRef Sym = StreamVal.getAsSymbol(); 601 if (Sym && State->get<StreamMap>(Sym)) { 602 const StreamState *SS = State->get<StreamMap>(Sym); 603 if (SS->ErrorState & ErrorFEof) 604 reportFEofWarning(Sym, C, State); 605 } else { 606 C.addTransition(State); 607 } 608 } 609 610 void StreamChecker::preFwrite(const FnDescription *Desc, const CallEvent &Call, 611 CheckerContext &C) const { 612 ProgramStateRef State = C.getState(); 613 SVal StreamVal = getStreamArg(Desc, Call); 614 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 615 State); 616 if (!State) 617 return; 618 State = ensureStreamOpened(StreamVal, C, State); 619 if (!State) 620 return; 621 State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 622 if (!State) 623 return; 624 625 C.addTransition(State); 626 } 627 628 void StreamChecker::evalFreadFwrite(const FnDescription *Desc, 629 const CallEvent &Call, CheckerContext &C, 630 bool IsFread) const { 631 ProgramStateRef State = C.getState(); 632 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 633 if (!StreamSym) 634 return; 635 636 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 637 if (!CE) 638 return; 639 640 Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); 641 if (!SizeVal) 642 return; 643 Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); 644 if (!NMembVal) 645 return; 646 647 const StreamState *OldSS = State->get<StreamMap>(StreamSym); 648 if (!OldSS) 649 return; 650 651 assertStreamStateOpened(OldSS); 652 653 // C'99 standard, §7.19.8.1.3, the return value of fread: 654 // The fread function returns the number of elements successfully read, which 655 // may be less than nmemb if a read error or end-of-file is encountered. If 656 // size or nmemb is zero, fread returns zero and the contents of the array and 657 // the state of the stream remain unchanged. 658 659 if (State->isNull(*SizeVal).isConstrainedTrue() || 660 State->isNull(*NMembVal).isConstrainedTrue()) { 661 // This is the "size or nmemb is zero" case. 662 // Just return 0, do nothing more (not clear the error flags). 663 State = bindInt(0, State, C, CE); 664 C.addTransition(State); 665 return; 666 } 667 668 // Generate a transition for the success state. 669 // If we know the state to be FEOF at fread, do not add a success state. 670 if (!IsFread || (OldSS->ErrorState != ErrorFEof)) { 671 ProgramStateRef StateNotFailed = 672 State->BindExpr(CE, C.getLocationContext(), *NMembVal); 673 if (StateNotFailed) { 674 StateNotFailed = StateNotFailed->set<StreamMap>( 675 StreamSym, StreamState::getOpened(Desc)); 676 C.addTransition(StateNotFailed); 677 } 678 } 679 680 // Add transition for the failed state. 681 Optional<NonLoc> RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 682 assert(RetVal && "Value should be NonLoc."); 683 ProgramStateRef StateFailed = 684 State->BindExpr(CE, C.getLocationContext(), *RetVal); 685 if (!StateFailed) 686 return; 687 auto Cond = C.getSValBuilder() 688 .evalBinOpNN(State, BO_LT, *RetVal, *NMembVal, 689 C.getASTContext().IntTy) 690 .getAs<DefinedOrUnknownSVal>(); 691 if (!Cond) 692 return; 693 StateFailed = StateFailed->assume(*Cond, true); 694 if (!StateFailed) 695 return; 696 697 StreamErrorState NewES; 698 if (IsFread) 699 NewES = 700 (OldSS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError; 701 else 702 NewES = ErrorFError; 703 // If a (non-EOF) error occurs, the resulting value of the file position 704 // indicator for the stream is indeterminate. 705 StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); 706 StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS); 707 if (IsFread && OldSS->ErrorState != ErrorFEof) 708 C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); 709 else 710 C.addTransition(StateFailed); 711 } 712 713 void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, 714 CheckerContext &C) const { 715 ProgramStateRef State = C.getState(); 716 SVal StreamVal = getStreamArg(Desc, Call); 717 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 718 State); 719 if (!State) 720 return; 721 State = ensureStreamOpened(StreamVal, C, State); 722 if (!State) 723 return; 724 State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); 725 if (!State) 726 return; 727 728 C.addTransition(State); 729 } 730 731 void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, 732 CheckerContext &C) const { 733 ProgramStateRef State = C.getState(); 734 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 735 if (!StreamSym) 736 return; 737 738 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 739 if (!CE) 740 return; 741 742 // Ignore the call if the stream is not tracked. 743 if (!State->get<StreamMap>(StreamSym)) 744 return; 745 746 DefinedSVal RetVal = makeRetVal(C, CE); 747 748 // Make expression result. 749 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 750 751 // Bifurcate the state into failed and non-failed. 752 // Return zero on success, nonzero on error. 753 ProgramStateRef StateNotFailed, StateFailed; 754 std::tie(StateFailed, StateNotFailed) = 755 C.getConstraintManager().assumeDual(State, RetVal); 756 757 // Reset the state to opened with no error. 758 StateNotFailed = 759 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 760 // We get error. 761 // It is possible that fseek fails but sets none of the error flags. 762 // If fseek failed, assume that the file position becomes indeterminate in any 763 // case. 764 StateFailed = StateFailed->set<StreamMap>( 765 StreamSym, 766 StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true)); 767 768 C.addTransition(StateNotFailed); 769 C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); 770 } 771 772 void StreamChecker::evalClearerr(const FnDescription *Desc, 773 const CallEvent &Call, 774 CheckerContext &C) const { 775 ProgramStateRef State = C.getState(); 776 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 777 if (!StreamSym) 778 return; 779 780 const StreamState *SS = State->get<StreamMap>(StreamSym); 781 if (!SS) 782 return; 783 784 assertStreamStateOpened(SS); 785 786 // FilePositionIndeterminate is not cleared. 787 State = State->set<StreamMap>( 788 StreamSym, 789 StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate)); 790 C.addTransition(State); 791 } 792 793 void StreamChecker::evalFeofFerror(const FnDescription *Desc, 794 const CallEvent &Call, CheckerContext &C, 795 const StreamErrorState &ErrorKind) const { 796 ProgramStateRef State = C.getState(); 797 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 798 if (!StreamSym) 799 return; 800 801 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 802 if (!CE) 803 return; 804 805 const StreamState *SS = State->get<StreamMap>(StreamSym); 806 if (!SS) 807 return; 808 809 assertStreamStateOpened(SS); 810 811 if (SS->ErrorState & ErrorKind) { 812 // Execution path with error of ErrorKind. 813 // Function returns true. 814 // From now on it is the only one error state. 815 ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE); 816 C.addTransition(TrueState->set<StreamMap>( 817 StreamSym, StreamState::getOpened(Desc, ErrorKind, 818 SS->FilePositionIndeterminate && 819 !ErrorKind.isFEof()))); 820 } 821 if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) { 822 // Execution path(s) with ErrorKind not set. 823 // Function returns false. 824 // New error state is everything before minus ErrorKind. 825 ProgramStateRef FalseState = bindInt(0, State, C, CE); 826 C.addTransition(FalseState->set<StreamMap>( 827 StreamSym, 828 StreamState::getOpened( 829 Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof()))); 830 } 831 } 832 833 void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, 834 CheckerContext &C) const { 835 ProgramStateRef State = C.getState(); 836 SVal StreamVal = getStreamArg(Desc, Call); 837 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 838 State); 839 if (!State) 840 return; 841 State = ensureStreamOpened(StreamVal, C, State); 842 if (!State) 843 return; 844 845 C.addTransition(State); 846 } 847 848 void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, 849 const CallEvent &Call, CheckerContext &C, 850 const StreamErrorState &ErrorKind) const { 851 ProgramStateRef State = C.getState(); 852 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 853 assert(StreamSym && "Operation not permitted on non-symbolic stream value."); 854 const StreamState *SS = State->get<StreamMap>(StreamSym); 855 assert(SS && "Stream should be tracked by the checker."); 856 State = State->set<StreamMap>( 857 StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind)); 858 C.addTransition(State); 859 } 860 861 ProgramStateRef 862 StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, 863 CheckerContext &C, 864 ProgramStateRef State) const { 865 auto Stream = StreamVal.getAs<DefinedSVal>(); 866 if (!Stream) 867 return State; 868 869 ConstraintManager &CM = C.getConstraintManager(); 870 871 ProgramStateRef StateNotNull, StateNull; 872 std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream); 873 874 if (!StateNotNull && StateNull) { 875 if (ExplodedNode *N = C.generateErrorNode(StateNull)) { 876 auto R = std::make_unique<PathSensitiveBugReport>( 877 BT_FileNull, "Stream pointer might be NULL.", N); 878 if (StreamE) 879 bugreporter::trackExpressionValue(N, StreamE, *R); 880 C.emitReport(std::move(R)); 881 } 882 return nullptr; 883 } 884 885 return StateNotNull; 886 } 887 888 ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, 889 CheckerContext &C, 890 ProgramStateRef State) const { 891 SymbolRef Sym = StreamVal.getAsSymbol(); 892 if (!Sym) 893 return State; 894 895 const StreamState *SS = State->get<StreamMap>(Sym); 896 if (!SS) 897 return State; 898 899 if (SS->isClosed()) { 900 // Using a stream pointer after 'fclose' causes undefined behavior 901 // according to cppreference.com . 902 ExplodedNode *N = C.generateErrorNode(); 903 if (N) { 904 C.emitReport(std::make_unique<PathSensitiveBugReport>( 905 BT_UseAfterClose, 906 "Stream might be already closed. Causes undefined behaviour.", N)); 907 return nullptr; 908 } 909 910 return State; 911 } 912 913 if (SS->isOpenFailed()) { 914 // Using a stream that has failed to open is likely to cause problems. 915 // This should usually not occur because stream pointer is NULL. 916 // But freopen can cause a state when stream pointer remains non-null but 917 // failed to open. 918 ExplodedNode *N = C.generateErrorNode(); 919 if (N) { 920 C.emitReport(std::make_unique<PathSensitiveBugReport>( 921 BT_UseAfterOpenFailed, 922 "Stream might be invalid after " 923 "(re-)opening it has failed. " 924 "Can cause undefined behaviour.", 925 N)); 926 return nullptr; 927 } 928 return State; 929 } 930 931 return State; 932 } 933 934 ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate( 935 SVal StreamVal, CheckerContext &C, ProgramStateRef State) const { 936 static const char *BugMessage = 937 "File position of the stream might be 'indeterminate' " 938 "after a failed operation. " 939 "Can cause undefined behavior."; 940 941 SymbolRef Sym = StreamVal.getAsSymbol(); 942 if (!Sym) 943 return State; 944 945 const StreamState *SS = State->get<StreamMap>(Sym); 946 if (!SS) 947 return State; 948 949 assert(SS->isOpened() && "First ensure that stream is opened."); 950 951 if (SS->FilePositionIndeterminate) { 952 if (SS->ErrorState & ErrorFEof) { 953 // The error is unknown but may be FEOF. 954 // Continue analysis with the FEOF error state. 955 // Report warning because the other possible error states. 956 ExplodedNode *N = C.generateNonFatalErrorNode(State); 957 if (!N) 958 return nullptr; 959 960 C.emitReport(std::make_unique<PathSensitiveBugReport>( 961 BT_IndeterminatePosition, BugMessage, N)); 962 return State->set<StreamMap>( 963 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false)); 964 } 965 966 // Known or unknown error state without FEOF possible. 967 // Stop analysis, report error. 968 ExplodedNode *N = C.generateErrorNode(State); 969 if (N) 970 C.emitReport(std::make_unique<PathSensitiveBugReport>( 971 BT_IndeterminatePosition, BugMessage, N)); 972 973 return nullptr; 974 } 975 976 return State; 977 } 978 979 ProgramStateRef 980 StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 981 ProgramStateRef State) const { 982 Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>(); 983 if (!CI) 984 return State; 985 986 int64_t X = CI->getValue().getSExtValue(); 987 if (X >= 0 && X <= 2) 988 return State; 989 990 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 991 C.emitReport(std::make_unique<PathSensitiveBugReport>( 992 BT_IllegalWhence, 993 "The whence argument to fseek() should be " 994 "SEEK_SET, SEEK_END, or SEEK_CUR.", 995 N)); 996 return nullptr; 997 } 998 999 return State; 1000 } 1001 1002 void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C, 1003 ProgramStateRef State) const { 1004 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 1005 auto R = std::make_unique<PathSensitiveBugReport>( 1006 BT_StreamEof, 1007 "Read function called when stream is in EOF state. " 1008 "Function has no effect.", 1009 N); 1010 R->markInteresting(StreamSym); 1011 C.emitReport(std::move(R)); 1012 return; 1013 } 1014 C.addTransition(State); 1015 } 1016 1017 ExplodedNode * 1018 StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 1019 CheckerContext &C, ExplodedNode *Pred) const { 1020 ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred); 1021 if (!Err) 1022 return Pred; 1023 1024 for (SymbolRef LeakSym : LeakedSyms) { 1025 // Resource leaks can result in multiple warning that describe the same kind 1026 // of programming error: 1027 // void f() { 1028 // FILE *F = fopen("a.txt"); 1029 // if (rand()) // state split 1030 // return; // warning 1031 // } // warning 1032 // While this isn't necessarily true (leaking the same stream could result 1033 // from a different kinds of errors), the reduction in redundant reports 1034 // makes this a worthwhile heuristic. 1035 // FIXME: Add a checker option to turn this uniqueing feature off. 1036 const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C); 1037 assert(StreamOpenNode && "Could not find place of stream opening."); 1038 PathDiagnosticLocation LocUsedForUniqueing = 1039 PathDiagnosticLocation::createBegin( 1040 StreamOpenNode->getStmtForDiagnostics(), C.getSourceManager(), 1041 StreamOpenNode->getLocationContext()); 1042 1043 std::unique_ptr<PathSensitiveBugReport> R = 1044 std::make_unique<PathSensitiveBugReport>( 1045 BT_ResourceLeak, 1046 "Opened stream never closed. Potential resource leak.", Err, 1047 LocUsedForUniqueing, 1048 StreamOpenNode->getLocationContext()->getDecl()); 1049 R->markInteresting(LeakSym); 1050 C.emitReport(std::move(R)); 1051 } 1052 1053 return Err; 1054 } 1055 1056 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 1057 CheckerContext &C) const { 1058 ProgramStateRef State = C.getState(); 1059 1060 llvm::SmallVector<SymbolRef, 2> LeakedSyms; 1061 1062 const StreamMapTy &Map = State->get<StreamMap>(); 1063 for (const auto &I : Map) { 1064 SymbolRef Sym = I.first; 1065 const StreamState &SS = I.second; 1066 if (!SymReaper.isDead(Sym)) 1067 continue; 1068 if (SS.isOpened()) 1069 LeakedSyms.push_back(Sym); 1070 State = State->remove<StreamMap>(Sym); 1071 } 1072 1073 ExplodedNode *N = C.getPredecessor(); 1074 if (!LeakedSyms.empty()) 1075 N = reportLeaks(LeakedSyms, C, N); 1076 1077 C.addTransition(State, N); 1078 } 1079 1080 ProgramStateRef StreamChecker::checkPointerEscape( 1081 ProgramStateRef State, const InvalidatedSymbols &Escaped, 1082 const CallEvent *Call, PointerEscapeKind Kind) const { 1083 // Check for file-handling system call that is not handled by the checker. 1084 // FIXME: The checker should be updated to handle all system calls that take 1085 // 'FILE*' argument. These are now ignored. 1086 if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader()) 1087 return State; 1088 1089 for (SymbolRef Sym : Escaped) { 1090 // The symbol escaped. 1091 // From now the stream can be manipulated in unknown way to the checker, 1092 // it is not possible to handle it any more. 1093 // Optimistically, assume that the corresponding file handle will be closed 1094 // somewhere else. 1095 // Remove symbol from state so the following stream calls on this symbol are 1096 // not handled by the checker. 1097 State = State->remove<StreamMap>(Sym); 1098 } 1099 return State; 1100 } 1101 1102 //===----------------------------------------------------------------------===// 1103 // Checker registration. 1104 //===----------------------------------------------------------------------===// 1105 1106 void ento::registerStreamChecker(CheckerManager &Mgr) { 1107 Mgr.registerChecker<StreamChecker>(); 1108 } 1109 1110 bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) { 1111 return true; 1112 } 1113 1114 void ento::registerStreamTesterChecker(CheckerManager &Mgr) { 1115 auto *Checker = Mgr.getChecker<StreamChecker>(); 1116 Checker->TestMode = true; 1117 } 1118 1119 bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) { 1120 return true; 1121 }