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/CallDescription.h" 18 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 22 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 23 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 24 #include <functional> 25 #include <optional> 26 27 using namespace clang; 28 using namespace ento; 29 using namespace std::placeholders; 30 31 //===----------------------------------------------------------------------===// 32 // Definition of state data structures. 33 //===----------------------------------------------------------------------===// 34 35 namespace { 36 37 struct FnDescription; 38 39 /// State of the stream error flags. 40 /// Sometimes it is not known to the checker what error flags are set. 41 /// This is indicated by setting more than one flag to true. 42 /// This is an optimization to avoid state splits. 43 /// A stream can either be in FEOF or FERROR but not both at the same time. 44 /// Multiple flags are set to handle the corresponding states together. 45 struct StreamErrorState { 46 /// The stream can be in state where none of the error flags set. 47 bool NoError = true; 48 /// The stream can be in state where the EOF indicator is set. 49 bool FEof = false; 50 /// The stream can be in state where the error indicator is set. 51 bool FError = false; 52 53 bool isNoError() const { return NoError && !FEof && !FError; } 54 bool isFEof() const { return !NoError && FEof && !FError; } 55 bool isFError() const { return !NoError && !FEof && FError; } 56 57 bool operator==(const StreamErrorState &ES) const { 58 return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError; 59 } 60 61 bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); } 62 63 StreamErrorState operator|(const StreamErrorState &E) const { 64 return {NoError || E.NoError, FEof || E.FEof, FError || E.FError}; 65 } 66 67 StreamErrorState operator&(const StreamErrorState &E) const { 68 return {NoError && E.NoError, FEof && E.FEof, FError && E.FError}; 69 } 70 71 StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; } 72 73 /// Returns if the StreamErrorState is a valid object. 74 operator bool() const { return NoError || FEof || FError; } 75 76 void Profile(llvm::FoldingSetNodeID &ID) const { 77 ID.AddBoolean(NoError); 78 ID.AddBoolean(FEof); 79 ID.AddBoolean(FError); 80 } 81 }; 82 83 const StreamErrorState ErrorNone{true, false, false}; 84 const StreamErrorState ErrorFEof{false, true, false}; 85 const StreamErrorState ErrorFError{false, false, true}; 86 87 /// Full state information about a stream pointer. 88 struct StreamState { 89 /// The last file operation called in the stream. 90 /// Can be nullptr. 91 const FnDescription *LastOperation; 92 93 /// State of a stream symbol. 94 enum KindTy { 95 Opened, /// Stream is opened. 96 Closed, /// Closed stream (an invalid stream pointer after it was closed). 97 OpenFailed /// The last open operation has failed. 98 } State; 99 100 /// State of the error flags. 101 /// Ignored in non-opened stream state but must be NoError. 102 StreamErrorState const ErrorState; 103 104 /// Indicate if the file has an "indeterminate file position indicator". 105 /// This can be set at a failing read or write or seek operation. 106 /// If it is set no more read or write is allowed. 107 /// This value is not dependent on the stream error flags: 108 /// The error flag may be cleared with `clearerr` but the file position 109 /// remains still indeterminate. 110 /// This value applies to all error states in ErrorState except FEOF. 111 /// An EOF+indeterminate state is the same as EOF state. 112 bool const FilePositionIndeterminate = false; 113 114 StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES, 115 bool IsFilePositionIndeterminate) 116 : LastOperation(L), State(S), ErrorState(ES), 117 FilePositionIndeterminate(IsFilePositionIndeterminate) { 118 assert((!ES.isFEof() || !IsFilePositionIndeterminate) && 119 "FilePositionIndeterminate should be false in FEof case."); 120 assert((State == Opened || ErrorState.isNoError()) && 121 "ErrorState should be None in non-opened stream state."); 122 } 123 124 bool isOpened() const { return State == Opened; } 125 bool isClosed() const { return State == Closed; } 126 bool isOpenFailed() const { return State == OpenFailed; } 127 128 bool operator==(const StreamState &X) const { 129 // In not opened state error state should always NoError, so comparison 130 // here is no problem. 131 return LastOperation == X.LastOperation && State == X.State && 132 ErrorState == X.ErrorState && 133 FilePositionIndeterminate == X.FilePositionIndeterminate; 134 } 135 136 static StreamState getOpened(const FnDescription *L, 137 const StreamErrorState &ES = ErrorNone, 138 bool IsFilePositionIndeterminate = false) { 139 return StreamState{L, Opened, ES, IsFilePositionIndeterminate}; 140 } 141 static StreamState getClosed(const FnDescription *L) { 142 return StreamState{L, Closed, {}, false}; 143 } 144 static StreamState getOpenFailed(const FnDescription *L) { 145 return StreamState{L, OpenFailed, {}, false}; 146 } 147 148 void Profile(llvm::FoldingSetNodeID &ID) const { 149 ID.AddPointer(LastOperation); 150 ID.AddInteger(State); 151 ErrorState.Profile(ID); 152 ID.AddBoolean(FilePositionIndeterminate); 153 } 154 }; 155 156 } // namespace 157 158 //===----------------------------------------------------------------------===// 159 // StreamChecker class and utility functions. 160 //===----------------------------------------------------------------------===// 161 162 namespace { 163 164 class StreamChecker; 165 using FnCheck = std::function<void(const StreamChecker *, const FnDescription *, 166 const CallEvent &, CheckerContext &)>; 167 168 using ArgNoTy = unsigned int; 169 static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max(); 170 171 struct FnDescription { 172 FnCheck PreFn; 173 FnCheck EvalFn; 174 ArgNoTy StreamArgNo; 175 }; 176 177 /// Get the value of the stream argument out of the passed call event. 178 /// The call should contain a function that is described by Desc. 179 SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) { 180 assert(Desc && Desc->StreamArgNo != ArgNone && 181 "Try to get a non-existing stream argument."); 182 return Call.getArgSVal(Desc->StreamArgNo); 183 } 184 185 /// Create a conjured symbol return value for a call expression. 186 DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) { 187 assert(CE && "Expecting a call expression."); 188 189 const LocationContext *LCtx = C.getLocationContext(); 190 return C.getSValBuilder() 191 .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()) 192 .castAs<DefinedSVal>(); 193 } 194 195 ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C, 196 const CallExpr *CE) { 197 DefinedSVal RetVal = makeRetVal(C, CE); 198 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 199 State = State->assume(RetVal, true); 200 assert(State && "Assumption on new value should not fail."); 201 return State; 202 } 203 204 ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, 205 CheckerContext &C, const CallExpr *CE) { 206 State = State->BindExpr(CE, C.getLocationContext(), 207 C.getSValBuilder().makeIntVal(Value, CE->getType())); 208 return State; 209 } 210 211 class StreamChecker : public Checker<check::PreCall, eval::Call, 212 check::DeadSymbols, check::PointerEscape> { 213 BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"}; 214 BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"}; 215 BugType BT_UseAfterOpenFailed{this, "Invalid stream", 216 "Stream handling error"}; 217 BugType BT_IndeterminatePosition{this, "Invalid stream state", 218 "Stream handling error"}; 219 BugType BT_IllegalWhence{this, "Illegal whence argument", 220 "Stream handling error"}; 221 BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"}; 222 BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error", 223 /*SuppressOnSink =*/true}; 224 225 public: 226 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 227 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 228 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 229 ProgramStateRef checkPointerEscape(ProgramStateRef State, 230 const InvalidatedSymbols &Escaped, 231 const CallEvent *Call, 232 PointerEscapeKind Kind) const; 233 234 /// If true, evaluate special testing stream functions. 235 bool TestMode = false; 236 237 const BugType *getBT_StreamEof() const { return &BT_StreamEof; } 238 239 private: 240 CallDescriptionMap<FnDescription> FnDescriptions = { 241 {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 242 {{{"fdopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 243 {{{"freopen"}, 3}, 244 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, 245 {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 246 {{{"fclose"}, 1}, 247 {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, 248 {{{"fread"}, 4}, 249 {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), 250 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, 251 {{{"fwrite"}, 4}, 252 {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), 253 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, 254 {{{"fgetc"}, 1}, 255 {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), 256 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}}, 257 {{{"fgets"}, 3}, 258 {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), 259 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}}, 260 {{{"fputc"}, 2}, 261 {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), 262 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}}, 263 {{{"fputs"}, 2}, 264 {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), 265 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}}, 266 {{{"ungetc"}, 2}, 267 {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), 268 std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}}, 269 {{{"fseek"}, 3}, 270 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 271 {{{"fseeko"}, 3}, 272 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 273 {{{"ftell"}, 1}, 274 {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}}, 275 {{{"ftello"}, 1}, 276 {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}}, 277 {{{"fflush"}, 1}, 278 {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}}, 279 {{{"rewind"}, 1}, 280 {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}}, 281 {{{"fgetpos"}, 2}, 282 {&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}}, 283 {{{"fsetpos"}, 2}, 284 {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}}, 285 {{{"clearerr"}, 1}, 286 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, 287 {{{"feof"}, 1}, 288 {&StreamChecker::preDefault, 289 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), 290 0}}, 291 {{{"ferror"}, 1}, 292 {&StreamChecker::preDefault, 293 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError), 294 0}}, 295 {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}}, 296 }; 297 298 CallDescriptionMap<FnDescription> FnTestDescriptions = { 299 {{{"StreamTesterChecker_make_feof_stream"}, 1}, 300 {nullptr, 301 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof), 302 0}}, 303 {{{"StreamTesterChecker_make_ferror_stream"}, 1}, 304 {nullptr, 305 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, 306 ErrorFError), 307 0}}, 308 }; 309 310 /// Expanded value of EOF, empty before initialization. 311 mutable std::optional<int> EofVal; 312 /// Expanded value of SEEK_SET, 0 if not found. 313 mutable int SeekSetVal = 0; 314 /// Expanded value of SEEK_CUR, 1 if not found. 315 mutable int SeekCurVal = 1; 316 /// Expanded value of SEEK_END, 2 if not found. 317 mutable int SeekEndVal = 2; 318 319 void evalFopen(const FnDescription *Desc, const CallEvent &Call, 320 CheckerContext &C) const; 321 322 void preFreopen(const FnDescription *Desc, const CallEvent &Call, 323 CheckerContext &C) const; 324 void evalFreopen(const FnDescription *Desc, const CallEvent &Call, 325 CheckerContext &C) const; 326 327 void evalFclose(const FnDescription *Desc, const CallEvent &Call, 328 CheckerContext &C) const; 329 330 void preReadWrite(const FnDescription *Desc, const CallEvent &Call, 331 CheckerContext &C, bool IsRead) const; 332 333 void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, 334 CheckerContext &C, bool IsFread) const; 335 336 void evalFgetx(const FnDescription *Desc, const CallEvent &Call, 337 CheckerContext &C, bool SingleChar) const; 338 339 void evalFputx(const FnDescription *Desc, const CallEvent &Call, 340 CheckerContext &C, bool IsSingleChar) const; 341 342 void evalUngetc(const FnDescription *Desc, const CallEvent &Call, 343 CheckerContext &C) const; 344 345 void preFseek(const FnDescription *Desc, const CallEvent &Call, 346 CheckerContext &C) const; 347 void evalFseek(const FnDescription *Desc, const CallEvent &Call, 348 CheckerContext &C) const; 349 350 void evalFgetpos(const FnDescription *Desc, const CallEvent &Call, 351 CheckerContext &C) const; 352 353 void evalFsetpos(const FnDescription *Desc, const CallEvent &Call, 354 CheckerContext &C) const; 355 356 void evalFtell(const FnDescription *Desc, const CallEvent &Call, 357 CheckerContext &C) const; 358 359 void evalRewind(const FnDescription *Desc, const CallEvent &Call, 360 CheckerContext &C) const; 361 362 void preDefault(const FnDescription *Desc, const CallEvent &Call, 363 CheckerContext &C) const; 364 365 void evalClearerr(const FnDescription *Desc, const CallEvent &Call, 366 CheckerContext &C) const; 367 368 void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call, 369 CheckerContext &C, 370 const StreamErrorState &ErrorKind) const; 371 372 void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call, 373 CheckerContext &C, 374 const StreamErrorState &ErrorKind) const; 375 376 void preFflush(const FnDescription *Desc, const CallEvent &Call, 377 CheckerContext &C) const; 378 379 void evalFflush(const FnDescription *Desc, const CallEvent &Call, 380 CheckerContext &C) const; 381 382 /// Check that the stream (in StreamVal) is not NULL. 383 /// If it can only be NULL a fatal error is emitted and nullptr returned. 384 /// Otherwise the return value is a new state where the stream is constrained 385 /// to be non-null. 386 ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, 387 CheckerContext &C, 388 ProgramStateRef State) const; 389 390 /// Check that the stream is the opened state. 391 /// If the stream is known to be not opened an error is generated 392 /// and nullptr returned, otherwise the original state is returned. 393 ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C, 394 ProgramStateRef State) const; 395 396 /// Check that the stream has not an invalid ("indeterminate") file position, 397 /// generate warning for it. 398 /// (EOF is not an invalid position.) 399 /// The returned state can be nullptr if a fatal error was generated. 400 /// It can return non-null state if the stream has not an invalid position or 401 /// there is execution path with non-invalid position. 402 ProgramStateRef 403 ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C, 404 ProgramStateRef State) const; 405 406 /// Check the legality of the 'whence' argument of 'fseek'. 407 /// Generate error and return nullptr if it is found to be illegal. 408 /// Otherwise returns the state. 409 /// (State is not changed here because the "whence" value is already known.) 410 ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 411 ProgramStateRef State) const; 412 413 /// Generate warning about stream in EOF state. 414 /// There will be always a state transition into the passed State, 415 /// by the new non-fatal error node or (if failed) a normal transition, 416 /// to ensure uniform handling. 417 void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C, 418 ProgramStateRef State) const; 419 420 /// Emit resource leak warnings for the given symbols. 421 /// Createn a non-fatal error node for these, and returns it (if any warnings 422 /// were generated). Return value is non-null. 423 ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 424 CheckerContext &C, ExplodedNode *Pred) const; 425 426 /// Find the description data of the function called by a call event. 427 /// Returns nullptr if no function is recognized. 428 const FnDescription *lookupFn(const CallEvent &Call) const { 429 // Recognize "global C functions" with only integral or pointer arguments 430 // (and matching name) as stream functions. 431 if (!Call.isGlobalCFunction()) 432 return nullptr; 433 for (auto *P : Call.parameters()) { 434 QualType T = P->getType(); 435 if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) 436 return nullptr; 437 } 438 439 return FnDescriptions.lookup(Call); 440 } 441 442 /// Generate a message for BugReporterVisitor if the stored symbol is 443 /// marked as interesting by the actual bug report. 444 const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym, 445 const std::string &Message) const { 446 return C.getNoteTag([this, StreamSym, 447 Message](PathSensitiveBugReport &BR) -> std::string { 448 if (BR.isInteresting(StreamSym) && &BR.getBugType() == &BT_ResourceLeak) 449 return Message; 450 return ""; 451 }); 452 } 453 454 const NoteTag *constructSetEofNoteTag(CheckerContext &C, 455 SymbolRef StreamSym) const { 456 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) { 457 if (!BR.isInteresting(StreamSym) || 458 &BR.getBugType() != this->getBT_StreamEof()) 459 return ""; 460 461 BR.markNotInteresting(StreamSym); 462 463 return "Assuming stream reaches end-of-file here"; 464 }); 465 } 466 467 void initMacroValues(CheckerContext &C) const { 468 if (EofVal) 469 return; 470 471 if (const std::optional<int> OptInt = 472 tryExpandAsInteger("EOF", C.getPreprocessor())) 473 EofVal = *OptInt; 474 else 475 EofVal = -1; 476 if (const std::optional<int> OptInt = 477 tryExpandAsInteger("SEEK_SET", C.getPreprocessor())) 478 SeekSetVal = *OptInt; 479 if (const std::optional<int> OptInt = 480 tryExpandAsInteger("SEEK_END", C.getPreprocessor())) 481 SeekEndVal = *OptInt; 482 if (const std::optional<int> OptInt = 483 tryExpandAsInteger("SEEK_CUR", C.getPreprocessor())) 484 SeekCurVal = *OptInt; 485 } 486 487 /// Searches for the ExplodedNode where the file descriptor was acquired for 488 /// StreamSym. 489 static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, 490 SymbolRef StreamSym, 491 CheckerContext &C); 492 }; 493 494 } // end anonymous namespace 495 496 // This map holds the state of a stream. 497 // The stream is identified with a SymbolRef that is created when a stream 498 // opening function is modeled by the checker. 499 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 500 501 inline void assertStreamStateOpened(const StreamState *SS) { 502 assert(SS->isOpened() && "Stream is expected to be opened"); 503 } 504 505 const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, 506 SymbolRef StreamSym, 507 CheckerContext &C) { 508 ProgramStateRef State = N->getState(); 509 // When bug type is resource leak, exploded node N may not have state info 510 // for leaked file descriptor, but predecessor should have it. 511 if (!State->get<StreamMap>(StreamSym)) 512 N = N->getFirstPred(); 513 514 const ExplodedNode *Pred = N; 515 while (N) { 516 State = N->getState(); 517 if (!State->get<StreamMap>(StreamSym)) 518 return Pred; 519 Pred = N; 520 N = N->getFirstPred(); 521 } 522 523 return nullptr; 524 } 525 526 //===----------------------------------------------------------------------===// 527 // Methods of StreamChecker. 528 //===----------------------------------------------------------------------===// 529 530 void StreamChecker::checkPreCall(const CallEvent &Call, 531 CheckerContext &C) const { 532 initMacroValues(C); 533 534 const FnDescription *Desc = lookupFn(Call); 535 if (!Desc || !Desc->PreFn) 536 return; 537 538 Desc->PreFn(this, Desc, Call, C); 539 } 540 541 bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { 542 const FnDescription *Desc = lookupFn(Call); 543 if (!Desc && TestMode) 544 Desc = FnTestDescriptions.lookup(Call); 545 if (!Desc || !Desc->EvalFn) 546 return false; 547 548 Desc->EvalFn(this, Desc, Call, C); 549 550 return C.isDifferent(); 551 } 552 553 void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call, 554 CheckerContext &C) const { 555 ProgramStateRef State = C.getState(); 556 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 557 if (!CE) 558 return; 559 560 DefinedSVal RetVal = makeRetVal(C, CE); 561 SymbolRef RetSym = RetVal.getAsSymbol(); 562 assert(RetSym && "RetVal must be a symbol here."); 563 564 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 565 566 // Bifurcate the state into two: one with a valid FILE* pointer, the other 567 // with a NULL. 568 ProgramStateRef StateNotNull, StateNull; 569 std::tie(StateNotNull, StateNull) = 570 C.getConstraintManager().assumeDual(State, RetVal); 571 572 StateNotNull = 573 StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc)); 574 StateNull = 575 StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc)); 576 577 C.addTransition(StateNotNull, 578 constructNoteTag(C, RetSym, "Stream opened here")); 579 C.addTransition(StateNull); 580 } 581 582 void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call, 583 CheckerContext &C) const { 584 // Do not allow NULL as passed stream pointer but allow a closed stream. 585 ProgramStateRef State = C.getState(); 586 State = ensureStreamNonNull(getStreamArg(Desc, Call), 587 Call.getArgExpr(Desc->StreamArgNo), C, State); 588 if (!State) 589 return; 590 591 C.addTransition(State); 592 } 593 594 void StreamChecker::evalFreopen(const FnDescription *Desc, 595 const CallEvent &Call, 596 CheckerContext &C) const { 597 ProgramStateRef State = C.getState(); 598 599 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 600 if (!CE) 601 return; 602 603 std::optional<DefinedSVal> StreamVal = 604 getStreamArg(Desc, Call).getAs<DefinedSVal>(); 605 if (!StreamVal) 606 return; 607 608 SymbolRef StreamSym = StreamVal->getAsSymbol(); 609 // Do not care about concrete values for stream ("(FILE *)0x12345"?). 610 // FIXME: Can be stdin, stdout, stderr such values? 611 if (!StreamSym) 612 return; 613 614 // Do not handle untracked stream. It is probably escaped. 615 if (!State->get<StreamMap>(StreamSym)) 616 return; 617 618 // Generate state for non-failed case. 619 // Return value is the passed stream pointer. 620 // According to the documentations, the stream is closed first 621 // but any close error is ignored. The state changes to (or remains) opened. 622 ProgramStateRef StateRetNotNull = 623 State->BindExpr(CE, C.getLocationContext(), *StreamVal); 624 // Generate state for NULL return value. 625 // Stream switches to OpenFailed state. 626 ProgramStateRef StateRetNull = 627 State->BindExpr(CE, C.getLocationContext(), 628 C.getSValBuilder().makeNullWithType(CE->getType())); 629 630 StateRetNotNull = 631 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 632 StateRetNull = 633 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc)); 634 635 C.addTransition(StateRetNotNull, 636 constructNoteTag(C, StreamSym, "Stream reopened here")); 637 C.addTransition(StateRetNull); 638 } 639 640 void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, 641 CheckerContext &C) const { 642 ProgramStateRef State = C.getState(); 643 SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); 644 if (!Sym) 645 return; 646 647 const StreamState *SS = State->get<StreamMap>(Sym); 648 if (!SS) 649 return; 650 651 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 652 if (!CE) 653 return; 654 655 assertStreamStateOpened(SS); 656 657 // Close the File Descriptor. 658 // Regardless if the close fails or not, stream becomes "closed" 659 // and can not be used any more. 660 State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc)); 661 662 // Return 0 on success, EOF on failure. 663 SValBuilder &SVB = C.getSValBuilder(); 664 ProgramStateRef StateSuccess = State->BindExpr( 665 CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy)); 666 ProgramStateRef StateFailure = 667 State->BindExpr(CE, C.getLocationContext(), 668 SVB.makeIntVal(*EofVal, C.getASTContext().IntTy)); 669 670 C.addTransition(StateSuccess); 671 C.addTransition(StateFailure); 672 } 673 674 void StreamChecker::preReadWrite(const FnDescription *Desc, 675 const CallEvent &Call, CheckerContext &C, 676 bool IsRead) const { 677 ProgramStateRef State = C.getState(); 678 SVal StreamVal = getStreamArg(Desc, Call); 679 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 680 State); 681 if (!State) 682 return; 683 State = ensureStreamOpened(StreamVal, C, State); 684 if (!State) 685 return; 686 State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 687 if (!State) 688 return; 689 690 if (!IsRead) { 691 C.addTransition(State); 692 return; 693 } 694 695 SymbolRef Sym = StreamVal.getAsSymbol(); 696 if (Sym && State->get<StreamMap>(Sym)) { 697 const StreamState *SS = State->get<StreamMap>(Sym); 698 if (SS->ErrorState & ErrorFEof) 699 reportFEofWarning(Sym, C, State); 700 } else { 701 C.addTransition(State); 702 } 703 } 704 705 void StreamChecker::evalFreadFwrite(const FnDescription *Desc, 706 const CallEvent &Call, CheckerContext &C, 707 bool IsFread) const { 708 ProgramStateRef State = C.getState(); 709 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 710 if (!StreamSym) 711 return; 712 713 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 714 if (!CE) 715 return; 716 717 std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); 718 if (!SizeVal) 719 return; 720 std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); 721 if (!NMembVal) 722 return; 723 724 const StreamState *OldSS = State->get<StreamMap>(StreamSym); 725 if (!OldSS) 726 return; 727 728 assertStreamStateOpened(OldSS); 729 730 // C'99 standard, §7.19.8.1.3, the return value of fread: 731 // The fread function returns the number of elements successfully read, which 732 // may be less than nmemb if a read error or end-of-file is encountered. If 733 // size or nmemb is zero, fread returns zero and the contents of the array and 734 // the state of the stream remain unchanged. 735 736 if (State->isNull(*SizeVal).isConstrainedTrue() || 737 State->isNull(*NMembVal).isConstrainedTrue()) { 738 // This is the "size or nmemb is zero" case. 739 // Just return 0, do nothing more (not clear the error flags). 740 State = bindInt(0, State, C, CE); 741 C.addTransition(State); 742 return; 743 } 744 745 // Generate a transition for the success state. 746 // If we know the state to be FEOF at fread, do not add a success state. 747 if (!IsFread || (OldSS->ErrorState != ErrorFEof)) { 748 ProgramStateRef StateNotFailed = 749 State->BindExpr(CE, C.getLocationContext(), *NMembVal); 750 StateNotFailed = 751 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 752 C.addTransition(StateNotFailed); 753 } 754 755 // Add transition for the failed state. 756 NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 757 ProgramStateRef StateFailed = 758 State->BindExpr(CE, C.getLocationContext(), RetVal); 759 SValBuilder &SVB = C.getSValBuilder(); 760 auto Cond = 761 SVB.evalBinOpNN(State, BO_LT, RetVal, *NMembVal, SVB.getConditionType()) 762 .getAs<DefinedOrUnknownSVal>(); 763 if (!Cond) 764 return; 765 StateFailed = StateFailed->assume(*Cond, true); 766 if (!StateFailed) 767 return; 768 769 StreamErrorState NewES; 770 if (IsFread) 771 NewES = 772 (OldSS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError; 773 else 774 NewES = ErrorFError; 775 // If a (non-EOF) error occurs, the resulting value of the file position 776 // indicator for the stream is indeterminate. 777 StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); 778 StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS); 779 if (IsFread && OldSS->ErrorState != ErrorFEof) 780 C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); 781 else 782 C.addTransition(StateFailed); 783 } 784 785 void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call, 786 CheckerContext &C, bool SingleChar) const { 787 ProgramStateRef State = C.getState(); 788 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 789 if (!StreamSym) 790 return; 791 792 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 793 if (!CE) 794 return; 795 796 const StreamState *OldSS = State->get<StreamMap>(StreamSym); 797 if (!OldSS) 798 return; 799 800 assertStreamStateOpened(OldSS); 801 802 // `fgetc` returns the read character on success, otherwise returns EOF. 803 // `fgets` returns the read buffer address on success, otherwise returns NULL. 804 805 if (OldSS->ErrorState != ErrorFEof) { 806 if (SingleChar) { 807 // Generate a transition for the success state of `fgetc`. 808 NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 809 ProgramStateRef StateNotFailed = 810 State->BindExpr(CE, C.getLocationContext(), RetVal); 811 SValBuilder &SVB = C.getSValBuilder(); 812 ASTContext &ASTC = C.getASTContext(); 813 // The returned 'unsigned char' of `fgetc` is converted to 'int', 814 // so we need to check if it is in range [0, 255]. 815 auto CondLow = 816 SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), 817 SVB.getConditionType()) 818 .getAs<DefinedOrUnknownSVal>(); 819 auto CondHigh = 820 SVB.evalBinOp(State, BO_LE, RetVal, 821 SVB.makeIntVal(SVB.getBasicValueFactory() 822 .getMaxValue(ASTC.UnsignedCharTy) 823 .getLimitedValue(), 824 ASTC.IntTy), 825 SVB.getConditionType()) 826 .getAs<DefinedOrUnknownSVal>(); 827 if (!CondLow || !CondHigh) 828 return; 829 StateNotFailed = StateNotFailed->assume(*CondLow, true); 830 if (!StateNotFailed) 831 return; 832 StateNotFailed = StateNotFailed->assume(*CondHigh, true); 833 if (!StateNotFailed) 834 return; 835 C.addTransition(StateNotFailed); 836 } else { 837 // Generate a transition for the success state of `fgets`. 838 std::optional<DefinedSVal> GetBuf = 839 Call.getArgSVal(0).getAs<DefinedSVal>(); 840 if (!GetBuf) 841 return; 842 ProgramStateRef StateNotFailed = 843 State->BindExpr(CE, C.getLocationContext(), *GetBuf); 844 StateNotFailed = StateNotFailed->set<StreamMap>( 845 StreamSym, StreamState::getOpened(Desc)); 846 C.addTransition(StateNotFailed); 847 } 848 } 849 850 // Add transition for the failed state. 851 ProgramStateRef StateFailed; 852 if (SingleChar) 853 StateFailed = bindInt(*EofVal, State, C, CE); 854 else 855 StateFailed = 856 State->BindExpr(CE, C.getLocationContext(), 857 C.getSValBuilder().makeNullWithType(CE->getType())); 858 859 // If a (non-EOF) error occurs, the resulting value of the file position 860 // indicator for the stream is indeterminate. 861 StreamErrorState NewES = 862 OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; 863 StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); 864 StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS); 865 if (OldSS->ErrorState != ErrorFEof) 866 C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); 867 else 868 C.addTransition(StateFailed); 869 } 870 871 void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, 872 CheckerContext &C, bool IsSingleChar) const { 873 ProgramStateRef State = C.getState(); 874 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 875 if (!StreamSym) 876 return; 877 878 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 879 if (!CE) 880 return; 881 882 const StreamState *OldSS = State->get<StreamMap>(StreamSym); 883 if (!OldSS) 884 return; 885 886 assertStreamStateOpened(OldSS); 887 888 // `fputc` returns the written character on success, otherwise returns EOF. 889 // `fputs` returns a non negative value on sucecess, otherwise returns EOF. 890 891 if (IsSingleChar) { 892 // Generate a transition for the success state of `fputc`. 893 std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>(); 894 if (!PutVal) 895 return; 896 ProgramStateRef StateNotFailed = 897 State->BindExpr(CE, C.getLocationContext(), *PutVal); 898 StateNotFailed = 899 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 900 C.addTransition(StateNotFailed); 901 } else { 902 // Generate a transition for the success state of `fputs`. 903 NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 904 ProgramStateRef StateNotFailed = 905 State->BindExpr(CE, C.getLocationContext(), RetVal); 906 SValBuilder &SVB = C.getSValBuilder(); 907 auto &ASTC = C.getASTContext(); 908 auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), 909 SVB.getConditionType()) 910 .getAs<DefinedOrUnknownSVal>(); 911 if (!Cond) 912 return; 913 StateNotFailed = StateNotFailed->assume(*Cond, true); 914 if (!StateNotFailed) 915 return; 916 StateNotFailed = 917 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 918 C.addTransition(StateNotFailed); 919 } 920 921 // Add transition for the failed state. The resulting value of the file 922 // position indicator for the stream is indeterminate. 923 ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); 924 StreamState NewSS = StreamState::getOpened(Desc, ErrorFError, true); 925 StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS); 926 C.addTransition(StateFailed); 927 } 928 929 void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent &Call, 930 CheckerContext &C) const { 931 ProgramStateRef State = C.getState(); 932 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 933 if (!StreamSym) 934 return; 935 936 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 937 if (!CE) 938 return; 939 940 const StreamState *OldSS = State->get<StreamMap>(StreamSym); 941 if (!OldSS) 942 return; 943 944 assertStreamStateOpened(OldSS); 945 946 // Generate a transition for the success state. 947 std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>(); 948 if (!PutVal) 949 return; 950 ProgramStateRef StateNotFailed = 951 State->BindExpr(CE, C.getLocationContext(), *PutVal); 952 StateNotFailed = 953 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 954 C.addTransition(StateNotFailed); 955 956 // Add transition for the failed state. 957 // Failure of 'ungetc' does not result in feof or ferror state. 958 // If the PutVal has value of EofVal the function should "fail", but this is 959 // the same transition as the success state. 960 // In this case only one state transition is added by the analyzer (the two 961 // new states may be similar). 962 ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); 963 StateFailed = 964 StateFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 965 C.addTransition(StateFailed); 966 } 967 968 void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, 969 CheckerContext &C) const { 970 ProgramStateRef State = C.getState(); 971 SVal StreamVal = getStreamArg(Desc, Call); 972 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 973 State); 974 if (!State) 975 return; 976 State = ensureStreamOpened(StreamVal, C, State); 977 if (!State) 978 return; 979 State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); 980 if (!State) 981 return; 982 983 C.addTransition(State); 984 } 985 986 void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, 987 CheckerContext &C) const { 988 ProgramStateRef State = C.getState(); 989 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 990 if (!StreamSym) 991 return; 992 993 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 994 if (!CE) 995 return; 996 997 // Ignore the call if the stream is not tracked. 998 if (!State->get<StreamMap>(StreamSym)) 999 return; 1000 1001 const llvm::APSInt *PosV = 1002 C.getSValBuilder().getKnownValue(State, Call.getArgSVal(1)); 1003 const llvm::APSInt *WhenceV = 1004 C.getSValBuilder().getKnownValue(State, Call.getArgSVal(2)); 1005 1006 DefinedSVal RetVal = makeRetVal(C, CE); 1007 1008 // Make expression result. 1009 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 1010 1011 // Bifurcate the state into failed and non-failed. 1012 // Return zero on success, nonzero on error. 1013 ProgramStateRef StateNotFailed, StateFailed; 1014 std::tie(StateFailed, StateNotFailed) = 1015 C.getConstraintManager().assumeDual(State, RetVal); 1016 1017 // Reset the state to opened with no error. 1018 StateNotFailed = 1019 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 1020 // We get error. 1021 // It is possible that fseek fails but sets none of the error flags. 1022 // If fseek failed, assume that the file position becomes indeterminate in any 1023 // case. 1024 StreamErrorState NewErrS = ErrorNone | ErrorFError; 1025 // Setting the position to start of file never produces EOF error. 1026 if (!(PosV && *PosV == 0 && WhenceV && *WhenceV == SeekSetVal)) 1027 NewErrS = NewErrS | ErrorFEof; 1028 StateFailed = StateFailed->set<StreamMap>( 1029 StreamSym, StreamState::getOpened(Desc, NewErrS, true)); 1030 1031 C.addTransition(StateNotFailed); 1032 C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); 1033 } 1034 1035 void StreamChecker::evalFgetpos(const FnDescription *Desc, 1036 const CallEvent &Call, 1037 CheckerContext &C) const { 1038 ProgramStateRef State = C.getState(); 1039 SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); 1040 if (!Sym) 1041 return; 1042 1043 // Do not evaluate if stream is not found. 1044 if (!State->get<StreamMap>(Sym)) 1045 return; 1046 1047 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1048 if (!CE) 1049 return; 1050 1051 DefinedSVal RetVal = makeRetVal(C, CE); 1052 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 1053 ProgramStateRef StateNotFailed, StateFailed; 1054 std::tie(StateFailed, StateNotFailed) = 1055 C.getConstraintManager().assumeDual(State, RetVal); 1056 1057 // This function does not affect the stream state. 1058 // Still we add success and failure state with the appropriate return value. 1059 // StdLibraryFunctionsChecker can change these states (set the 'errno' state). 1060 C.addTransition(StateNotFailed); 1061 C.addTransition(StateFailed); 1062 } 1063 1064 void StreamChecker::evalFsetpos(const FnDescription *Desc, 1065 const CallEvent &Call, 1066 CheckerContext &C) const { 1067 ProgramStateRef State = C.getState(); 1068 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 1069 if (!StreamSym) 1070 return; 1071 1072 const StreamState *SS = State->get<StreamMap>(StreamSym); 1073 if (!SS) 1074 return; 1075 1076 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1077 if (!CE) 1078 return; 1079 1080 assertStreamStateOpened(SS); 1081 1082 DefinedSVal RetVal = makeRetVal(C, CE); 1083 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 1084 ProgramStateRef StateNotFailed, StateFailed; 1085 std::tie(StateFailed, StateNotFailed) = 1086 C.getConstraintManager().assumeDual(State, RetVal); 1087 1088 StateNotFailed = StateNotFailed->set<StreamMap>( 1089 StreamSym, StreamState::getOpened(Desc, ErrorNone, false)); 1090 1091 // At failure ferror could be set. 1092 // The standards do not tell what happens with the file position at failure. 1093 // But we can assume that it is dangerous to make a next I/O operation after 1094 // the position was not set correctly (similar to 'fseek'). 1095 StateFailed = StateFailed->set<StreamMap>( 1096 StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true)); 1097 1098 C.addTransition(StateNotFailed); 1099 C.addTransition(StateFailed); 1100 } 1101 1102 void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call, 1103 CheckerContext &C) const { 1104 ProgramStateRef State = C.getState(); 1105 SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); 1106 if (!Sym) 1107 return; 1108 1109 if (!State->get<StreamMap>(Sym)) 1110 return; 1111 1112 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1113 if (!CE) 1114 return; 1115 1116 SValBuilder &SVB = C.getSValBuilder(); 1117 NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 1118 ProgramStateRef StateNotFailed = 1119 State->BindExpr(CE, C.getLocationContext(), RetVal); 1120 auto Cond = 1121 SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(Call.getResultType()), 1122 SVB.getConditionType()) 1123 .getAs<DefinedOrUnknownSVal>(); 1124 if (!Cond) 1125 return; 1126 StateNotFailed = StateNotFailed->assume(*Cond, true); 1127 if (!StateNotFailed) 1128 return; 1129 1130 ProgramStateRef StateFailed = State->BindExpr( 1131 CE, C.getLocationContext(), SVB.makeIntVal(-1, Call.getResultType())); 1132 1133 // This function does not affect the stream state. 1134 // Still we add success and failure state with the appropriate return value. 1135 // StdLibraryFunctionsChecker can change these states (set the 'errno' state). 1136 C.addTransition(StateNotFailed); 1137 C.addTransition(StateFailed); 1138 } 1139 1140 void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call, 1141 CheckerContext &C) const { 1142 ProgramStateRef State = C.getState(); 1143 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 1144 if (!StreamSym) 1145 return; 1146 1147 const StreamState *SS = State->get<StreamMap>(StreamSym); 1148 if (!SS) 1149 return; 1150 1151 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1152 if (!CE) 1153 return; 1154 1155 assertStreamStateOpened(SS); 1156 1157 State = State->set<StreamMap>(StreamSym, 1158 StreamState::getOpened(Desc, ErrorNone, false)); 1159 1160 C.addTransition(State); 1161 } 1162 1163 void StreamChecker::evalClearerr(const FnDescription *Desc, 1164 const CallEvent &Call, 1165 CheckerContext &C) const { 1166 ProgramStateRef State = C.getState(); 1167 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 1168 if (!StreamSym) 1169 return; 1170 1171 const StreamState *SS = State->get<StreamMap>(StreamSym); 1172 if (!SS) 1173 return; 1174 1175 assertStreamStateOpened(SS); 1176 1177 // FilePositionIndeterminate is not cleared. 1178 State = State->set<StreamMap>( 1179 StreamSym, 1180 StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate)); 1181 C.addTransition(State); 1182 } 1183 1184 void StreamChecker::evalFeofFerror(const FnDescription *Desc, 1185 const CallEvent &Call, CheckerContext &C, 1186 const StreamErrorState &ErrorKind) const { 1187 ProgramStateRef State = C.getState(); 1188 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 1189 if (!StreamSym) 1190 return; 1191 1192 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1193 if (!CE) 1194 return; 1195 1196 const StreamState *SS = State->get<StreamMap>(StreamSym); 1197 if (!SS) 1198 return; 1199 1200 assertStreamStateOpened(SS); 1201 1202 if (SS->ErrorState & ErrorKind) { 1203 // Execution path with error of ErrorKind. 1204 // Function returns true. 1205 // From now on it is the only one error state. 1206 ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE); 1207 C.addTransition(TrueState->set<StreamMap>( 1208 StreamSym, StreamState::getOpened(Desc, ErrorKind, 1209 SS->FilePositionIndeterminate && 1210 !ErrorKind.isFEof()))); 1211 } 1212 if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) { 1213 // Execution path(s) with ErrorKind not set. 1214 // Function returns false. 1215 // New error state is everything before minus ErrorKind. 1216 ProgramStateRef FalseState = bindInt(0, State, C, CE); 1217 C.addTransition(FalseState->set<StreamMap>( 1218 StreamSym, 1219 StreamState::getOpened( 1220 Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof()))); 1221 } 1222 } 1223 1224 void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, 1225 CheckerContext &C) const { 1226 ProgramStateRef State = C.getState(); 1227 SVal StreamVal = getStreamArg(Desc, Call); 1228 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 1229 State); 1230 if (!State) 1231 return; 1232 State = ensureStreamOpened(StreamVal, C, State); 1233 if (!State) 1234 return; 1235 1236 C.addTransition(State); 1237 } 1238 1239 void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, 1240 const CallEvent &Call, CheckerContext &C, 1241 const StreamErrorState &ErrorKind) const { 1242 ProgramStateRef State = C.getState(); 1243 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 1244 assert(StreamSym && "Operation not permitted on non-symbolic stream value."); 1245 const StreamState *SS = State->get<StreamMap>(StreamSym); 1246 assert(SS && "Stream should be tracked by the checker."); 1247 State = State->set<StreamMap>( 1248 StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind)); 1249 C.addTransition(State); 1250 } 1251 1252 void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call, 1253 CheckerContext &C) const { 1254 ProgramStateRef State = C.getState(); 1255 SVal StreamVal = getStreamArg(Desc, Call); 1256 std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>(); 1257 if (!Stream) 1258 return; 1259 1260 ProgramStateRef StateNotNull, StateNull; 1261 std::tie(StateNotNull, StateNull) = 1262 C.getConstraintManager().assumeDual(State, *Stream); 1263 if (StateNotNull && !StateNull) 1264 ensureStreamOpened(StreamVal, C, StateNotNull); 1265 } 1266 1267 void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, 1268 CheckerContext &C) const { 1269 ProgramStateRef State = C.getState(); 1270 SVal StreamVal = getStreamArg(Desc, Call); 1271 std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>(); 1272 if (!Stream) 1273 return; 1274 1275 // Skip if the stream can be both NULL and non-NULL. 1276 ProgramStateRef StateNotNull, StateNull; 1277 std::tie(StateNotNull, StateNull) = 1278 C.getConstraintManager().assumeDual(State, *Stream); 1279 if (StateNotNull && StateNull) 1280 return; 1281 if (StateNotNull && !StateNull) 1282 State = StateNotNull; 1283 else 1284 State = StateNull; 1285 1286 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1287 if (!CE) 1288 return; 1289 1290 // `fflush` returns EOF on failure, otherwise returns 0. 1291 ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); 1292 ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); 1293 1294 // Clear error states if `fflush` returns 0, but retain their EOF flags. 1295 auto ClearErrorInNotFailed = [&StateNotFailed, Desc](SymbolRef Sym, 1296 const StreamState *SS) { 1297 if (SS->ErrorState & ErrorFError) { 1298 StreamErrorState NewES = 1299 (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone; 1300 StreamState NewSS = StreamState::getOpened(Desc, NewES, false); 1301 StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS); 1302 } 1303 }; 1304 1305 if (StateNotNull && !StateNull) { 1306 // Skip if the input stream's state is unknown, open-failed or closed. 1307 if (SymbolRef StreamSym = StreamVal.getAsSymbol()) { 1308 const StreamState *SS = State->get<StreamMap>(StreamSym); 1309 if (SS) { 1310 assert(SS->isOpened() && "Stream is expected to be opened"); 1311 ClearErrorInNotFailed(StreamSym, SS); 1312 } else 1313 return; 1314 } 1315 } else { 1316 // Clear error states for all streams. 1317 const StreamMapTy &Map = StateNotFailed->get<StreamMap>(); 1318 for (const auto &I : Map) { 1319 SymbolRef Sym = I.first; 1320 const StreamState &SS = I.second; 1321 if (SS.isOpened()) 1322 ClearErrorInNotFailed(Sym, &SS); 1323 } 1324 } 1325 1326 C.addTransition(StateNotFailed); 1327 C.addTransition(StateFailed); 1328 } 1329 1330 ProgramStateRef 1331 StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, 1332 CheckerContext &C, 1333 ProgramStateRef State) const { 1334 auto Stream = StreamVal.getAs<DefinedSVal>(); 1335 if (!Stream) 1336 return State; 1337 1338 ConstraintManager &CM = C.getConstraintManager(); 1339 1340 ProgramStateRef StateNotNull, StateNull; 1341 std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream); 1342 1343 if (!StateNotNull && StateNull) { 1344 if (ExplodedNode *N = C.generateErrorNode(StateNull)) { 1345 auto R = std::make_unique<PathSensitiveBugReport>( 1346 BT_FileNull, "Stream pointer might be NULL.", N); 1347 if (StreamE) 1348 bugreporter::trackExpressionValue(N, StreamE, *R); 1349 C.emitReport(std::move(R)); 1350 } 1351 return nullptr; 1352 } 1353 1354 return StateNotNull; 1355 } 1356 1357 ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, 1358 CheckerContext &C, 1359 ProgramStateRef State) const { 1360 SymbolRef Sym = StreamVal.getAsSymbol(); 1361 if (!Sym) 1362 return State; 1363 1364 const StreamState *SS = State->get<StreamMap>(Sym); 1365 if (!SS) 1366 return State; 1367 1368 if (SS->isClosed()) { 1369 // Using a stream pointer after 'fclose' causes undefined behavior 1370 // according to cppreference.com . 1371 ExplodedNode *N = C.generateErrorNode(); 1372 if (N) { 1373 C.emitReport(std::make_unique<PathSensitiveBugReport>( 1374 BT_UseAfterClose, 1375 "Stream might be already closed. Causes undefined behaviour.", N)); 1376 return nullptr; 1377 } 1378 1379 return State; 1380 } 1381 1382 if (SS->isOpenFailed()) { 1383 // Using a stream that has failed to open is likely to cause problems. 1384 // This should usually not occur because stream pointer is NULL. 1385 // But freopen can cause a state when stream pointer remains non-null but 1386 // failed to open. 1387 ExplodedNode *N = C.generateErrorNode(); 1388 if (N) { 1389 C.emitReport(std::make_unique<PathSensitiveBugReport>( 1390 BT_UseAfterOpenFailed, 1391 "Stream might be invalid after " 1392 "(re-)opening it has failed. " 1393 "Can cause undefined behaviour.", 1394 N)); 1395 return nullptr; 1396 } 1397 } 1398 1399 return State; 1400 } 1401 1402 ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate( 1403 SVal StreamVal, CheckerContext &C, ProgramStateRef State) const { 1404 static const char *BugMessage = 1405 "File position of the stream might be 'indeterminate' " 1406 "after a failed operation. " 1407 "Can cause undefined behavior."; 1408 1409 SymbolRef Sym = StreamVal.getAsSymbol(); 1410 if (!Sym) 1411 return State; 1412 1413 const StreamState *SS = State->get<StreamMap>(Sym); 1414 if (!SS) 1415 return State; 1416 1417 assert(SS->isOpened() && "First ensure that stream is opened."); 1418 1419 if (SS->FilePositionIndeterminate) { 1420 if (SS->ErrorState & ErrorFEof) { 1421 // The error is unknown but may be FEOF. 1422 // Continue analysis with the FEOF error state. 1423 // Report warning because the other possible error states. 1424 ExplodedNode *N = C.generateNonFatalErrorNode(State); 1425 if (!N) 1426 return nullptr; 1427 1428 C.emitReport(std::make_unique<PathSensitiveBugReport>( 1429 BT_IndeterminatePosition, BugMessage, N)); 1430 return State->set<StreamMap>( 1431 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false)); 1432 } 1433 1434 // Known or unknown error state without FEOF possible. 1435 // Stop analysis, report error. 1436 ExplodedNode *N = C.generateErrorNode(State); 1437 if (N) 1438 C.emitReport(std::make_unique<PathSensitiveBugReport>( 1439 BT_IndeterminatePosition, BugMessage, N)); 1440 1441 return nullptr; 1442 } 1443 1444 return State; 1445 } 1446 1447 ProgramStateRef 1448 StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 1449 ProgramStateRef State) const { 1450 std::optional<nonloc::ConcreteInt> CI = 1451 WhenceVal.getAs<nonloc::ConcreteInt>(); 1452 if (!CI) 1453 return State; 1454 1455 int64_t X = CI->getValue().getSExtValue(); 1456 if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal) 1457 return State; 1458 1459 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 1460 C.emitReport(std::make_unique<PathSensitiveBugReport>( 1461 BT_IllegalWhence, 1462 "The whence argument to fseek() should be " 1463 "SEEK_SET, SEEK_END, or SEEK_CUR.", 1464 N)); 1465 return nullptr; 1466 } 1467 1468 return State; 1469 } 1470 1471 void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C, 1472 ProgramStateRef State) const { 1473 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 1474 auto R = std::make_unique<PathSensitiveBugReport>( 1475 BT_StreamEof, 1476 "Read function called when stream is in EOF state. " 1477 "Function has no effect.", 1478 N); 1479 R->markInteresting(StreamSym); 1480 C.emitReport(std::move(R)); 1481 return; 1482 } 1483 C.addTransition(State); 1484 } 1485 1486 ExplodedNode * 1487 StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 1488 CheckerContext &C, ExplodedNode *Pred) const { 1489 ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred); 1490 if (!Err) 1491 return Pred; 1492 1493 for (SymbolRef LeakSym : LeakedSyms) { 1494 // Resource leaks can result in multiple warning that describe the same kind 1495 // of programming error: 1496 // void f() { 1497 // FILE *F = fopen("a.txt"); 1498 // if (rand()) // state split 1499 // return; // warning 1500 // } // warning 1501 // While this isn't necessarily true (leaking the same stream could result 1502 // from a different kinds of errors), the reduction in redundant reports 1503 // makes this a worthwhile heuristic. 1504 // FIXME: Add a checker option to turn this uniqueing feature off. 1505 const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C); 1506 assert(StreamOpenNode && "Could not find place of stream opening."); 1507 1508 PathDiagnosticLocation LocUsedForUniqueing; 1509 if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics()) 1510 LocUsedForUniqueing = PathDiagnosticLocation::createBegin( 1511 StreamStmt, C.getSourceManager(), 1512 StreamOpenNode->getLocationContext()); 1513 1514 std::unique_ptr<PathSensitiveBugReport> R = 1515 std::make_unique<PathSensitiveBugReport>( 1516 BT_ResourceLeak, 1517 "Opened stream never closed. Potential resource leak.", Err, 1518 LocUsedForUniqueing, 1519 StreamOpenNode->getLocationContext()->getDecl()); 1520 R->markInteresting(LeakSym); 1521 C.emitReport(std::move(R)); 1522 } 1523 1524 return Err; 1525 } 1526 1527 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 1528 CheckerContext &C) const { 1529 ProgramStateRef State = C.getState(); 1530 1531 llvm::SmallVector<SymbolRef, 2> LeakedSyms; 1532 1533 const StreamMapTy &Map = State->get<StreamMap>(); 1534 for (const auto &I : Map) { 1535 SymbolRef Sym = I.first; 1536 const StreamState &SS = I.second; 1537 if (!SymReaper.isDead(Sym)) 1538 continue; 1539 if (SS.isOpened()) 1540 LeakedSyms.push_back(Sym); 1541 State = State->remove<StreamMap>(Sym); 1542 } 1543 1544 ExplodedNode *N = C.getPredecessor(); 1545 if (!LeakedSyms.empty()) 1546 N = reportLeaks(LeakedSyms, C, N); 1547 1548 C.addTransition(State, N); 1549 } 1550 1551 ProgramStateRef StreamChecker::checkPointerEscape( 1552 ProgramStateRef State, const InvalidatedSymbols &Escaped, 1553 const CallEvent *Call, PointerEscapeKind Kind) const { 1554 // Check for file-handling system call that is not handled by the checker. 1555 // FIXME: The checker should be updated to handle all system calls that take 1556 // 'FILE*' argument. These are now ignored. 1557 if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader()) 1558 return State; 1559 1560 for (SymbolRef Sym : Escaped) { 1561 // The symbol escaped. 1562 // From now the stream can be manipulated in unknown way to the checker, 1563 // it is not possible to handle it any more. 1564 // Optimistically, assume that the corresponding file handle will be closed 1565 // somewhere else. 1566 // Remove symbol from state so the following stream calls on this symbol are 1567 // not handled by the checker. 1568 State = State->remove<StreamMap>(Sym); 1569 } 1570 return State; 1571 } 1572 1573 //===----------------------------------------------------------------------===// 1574 // Checker registration. 1575 //===----------------------------------------------------------------------===// 1576 1577 void ento::registerStreamChecker(CheckerManager &Mgr) { 1578 Mgr.registerChecker<StreamChecker>(); 1579 } 1580 1581 bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) { 1582 return true; 1583 } 1584 1585 void ento::registerStreamTesterChecker(CheckerManager &Mgr) { 1586 auto *Checker = Mgr.getChecker<StreamChecker>(); 1587 Checker->TestMode = true; 1588 } 1589 1590 bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) { 1591 return true; 1592 } 1593