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 "llvm/ADT/Sequence.h" 25 #include <functional> 26 #include <optional> 27 28 using namespace clang; 29 using namespace ento; 30 using namespace std::placeholders; 31 32 //===----------------------------------------------------------------------===// 33 // Definition of state data structures. 34 //===----------------------------------------------------------------------===// 35 36 namespace { 37 38 struct FnDescription; 39 40 /// State of the stream error flags. 41 /// Sometimes it is not known to the checker what error flags are set. 42 /// This is indicated by setting more than one flag to true. 43 /// This is an optimization to avoid state splits. 44 /// A stream can either be in FEOF or FERROR but not both at the same time. 45 /// Multiple flags are set to handle the corresponding states together. 46 struct StreamErrorState { 47 /// The stream can be in state where none of the error flags set. 48 bool NoError = true; 49 /// The stream can be in state where the EOF indicator is set. 50 bool FEof = false; 51 /// The stream can be in state where the error indicator is set. 52 bool FError = false; 53 54 bool isNoError() const { return NoError && !FEof && !FError; } 55 bool isFEof() const { return !NoError && FEof && !FError; } 56 bool isFError() const { return !NoError && !FEof && FError; } 57 58 bool operator==(const StreamErrorState &ES) const { 59 return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError; 60 } 61 62 bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); } 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 StreamErrorState &E) const { 69 return {NoError && E.NoError, FEof && E.FEof, FError && E.FError}; 70 } 71 72 StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; } 73 74 /// Returns if the StreamErrorState is a valid object. 75 operator bool() const { return NoError || FEof || FError; } 76 77 void Profile(llvm::FoldingSetNodeID &ID) const { 78 ID.AddBoolean(NoError); 79 ID.AddBoolean(FEof); 80 ID.AddBoolean(FError); 81 } 82 }; 83 84 const StreamErrorState ErrorNone{true, false, false}; 85 const StreamErrorState ErrorFEof{false, true, false}; 86 const StreamErrorState ErrorFError{false, false, true}; 87 88 /// Full state information about a stream pointer. 89 struct StreamState { 90 /// The last file operation called in the stream. 91 /// Can be nullptr. 92 const FnDescription *LastOperation; 93 94 /// State of a stream symbol. 95 enum KindTy { 96 Opened, /// Stream is opened. 97 Closed, /// Closed stream (an invalid stream pointer after it was closed). 98 OpenFailed /// The last open operation has failed. 99 } State; 100 101 /// State of the error flags. 102 /// Ignored in non-opened stream state but must be NoError. 103 StreamErrorState const ErrorState; 104 105 /// Indicate if the file has an "indeterminate file position indicator". 106 /// This can be set at a failing read or write or seek operation. 107 /// If it is set no more read or write is allowed. 108 /// This value is not dependent on the stream error flags: 109 /// The error flag may be cleared with `clearerr` but the file position 110 /// remains still indeterminate. 111 /// This value applies to all error states in ErrorState except FEOF. 112 /// An EOF+indeterminate state is the same as EOF state. 113 bool const FilePositionIndeterminate = false; 114 115 StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES, 116 bool IsFilePositionIndeterminate) 117 : LastOperation(L), State(S), ErrorState(ES), 118 FilePositionIndeterminate(IsFilePositionIndeterminate) { 119 assert((!ES.isFEof() || !IsFilePositionIndeterminate) && 120 "FilePositionIndeterminate should be false in FEof case."); 121 assert((State == Opened || ErrorState.isNoError()) && 122 "ErrorState should be None in non-opened stream state."); 123 } 124 125 bool isOpened() const { return State == Opened; } 126 bool isClosed() const { return State == Closed; } 127 bool isOpenFailed() const { return State == OpenFailed; } 128 129 bool operator==(const StreamState &X) const { 130 // In not opened state error state should always NoError, so comparison 131 // here is no problem. 132 return LastOperation == X.LastOperation && State == X.State && 133 ErrorState == X.ErrorState && 134 FilePositionIndeterminate == X.FilePositionIndeterminate; 135 } 136 137 static StreamState getOpened(const FnDescription *L, 138 const StreamErrorState &ES = ErrorNone, 139 bool IsFilePositionIndeterminate = false) { 140 return StreamState{L, Opened, ES, IsFilePositionIndeterminate}; 141 } 142 static StreamState getClosed(const FnDescription *L) { 143 return StreamState{L, Closed, {}, false}; 144 } 145 static StreamState getOpenFailed(const FnDescription *L) { 146 return StreamState{L, OpenFailed, {}, false}; 147 } 148 149 void Profile(llvm::FoldingSetNodeID &ID) const { 150 ID.AddPointer(LastOperation); 151 ID.AddInteger(State); 152 ErrorState.Profile(ID); 153 ID.AddBoolean(FilePositionIndeterminate); 154 } 155 }; 156 157 } // namespace 158 159 // This map holds the state of a stream. 160 // The stream is identified with a SymbolRef that is created when a stream 161 // opening function is modeled by the checker. 162 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 163 164 //===----------------------------------------------------------------------===// 165 // StreamChecker class and utility functions. 166 //===----------------------------------------------------------------------===// 167 168 namespace { 169 170 class StreamChecker; 171 using FnCheck = std::function<void(const StreamChecker *, const FnDescription *, 172 const CallEvent &, CheckerContext &)>; 173 174 using ArgNoTy = unsigned int; 175 static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max(); 176 177 const char *FeofNote = "Assuming stream reaches end-of-file here"; 178 const char *FerrorNote = "Assuming this stream operation fails"; 179 180 struct FnDescription { 181 FnCheck PreFn; 182 FnCheck EvalFn; 183 ArgNoTy StreamArgNo; 184 }; 185 186 /// Get the value of the stream argument out of the passed call event. 187 /// The call should contain a function that is described by Desc. 188 SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) { 189 assert(Desc && Desc->StreamArgNo != ArgNone && 190 "Try to get a non-existing stream argument."); 191 return Call.getArgSVal(Desc->StreamArgNo); 192 } 193 194 /// Create a conjured symbol return value for a call expression. 195 DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) { 196 assert(CE && "Expecting a call expression."); 197 198 const LocationContext *LCtx = C.getLocationContext(); 199 return C.getSValBuilder() 200 .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()) 201 .castAs<DefinedSVal>(); 202 } 203 204 ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C, 205 const CallExpr *CE) { 206 DefinedSVal RetVal = makeRetVal(C, CE); 207 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 208 State = State->assume(RetVal, true); 209 assert(State && "Assumption on new value should not fail."); 210 return State; 211 } 212 213 ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, 214 CheckerContext &C, const CallExpr *CE) { 215 State = State->BindExpr(CE, C.getLocationContext(), 216 C.getSValBuilder().makeIntVal(Value, CE->getType())); 217 return State; 218 } 219 220 inline void assertStreamStateOpened(const StreamState *SS) { 221 assert(SS->isOpened() && "Stream is expected to be opened"); 222 } 223 224 class StreamChecker : public Checker<check::PreCall, eval::Call, 225 check::DeadSymbols, check::PointerEscape> { 226 BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"}; 227 BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"}; 228 BugType BT_UseAfterOpenFailed{this, "Invalid stream", 229 "Stream handling error"}; 230 BugType BT_IndeterminatePosition{this, "Invalid stream state", 231 "Stream handling error"}; 232 BugType BT_IllegalWhence{this, "Illegal whence argument", 233 "Stream handling error"}; 234 BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"}; 235 BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error", 236 /*SuppressOnSink =*/true}; 237 238 public: 239 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 240 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 241 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 242 ProgramStateRef checkPointerEscape(ProgramStateRef State, 243 const InvalidatedSymbols &Escaped, 244 const CallEvent *Call, 245 PointerEscapeKind Kind) const; 246 247 const BugType *getBT_StreamEof() const { return &BT_StreamEof; } 248 const BugType *getBT_IndeterminatePosition() const { 249 return &BT_IndeterminatePosition; 250 } 251 252 const NoteTag *constructSetEofNoteTag(CheckerContext &C, 253 SymbolRef StreamSym) const { 254 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) { 255 if (!BR.isInteresting(StreamSym) || 256 &BR.getBugType() != this->getBT_StreamEof()) 257 return ""; 258 259 BR.markNotInteresting(StreamSym); 260 261 return FeofNote; 262 }); 263 } 264 265 const NoteTag *constructSetErrorNoteTag(CheckerContext &C, 266 SymbolRef StreamSym) const { 267 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) { 268 if (!BR.isInteresting(StreamSym) || 269 &BR.getBugType() != this->getBT_IndeterminatePosition()) 270 return ""; 271 272 BR.markNotInteresting(StreamSym); 273 274 return FerrorNote; 275 }); 276 } 277 278 const NoteTag *constructSetEofOrErrorNoteTag(CheckerContext &C, 279 SymbolRef StreamSym) const { 280 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) { 281 if (!BR.isInteresting(StreamSym)) 282 return ""; 283 284 if (&BR.getBugType() == this->getBT_StreamEof()) { 285 BR.markNotInteresting(StreamSym); 286 return FeofNote; 287 } 288 if (&BR.getBugType() == this->getBT_IndeterminatePosition()) { 289 BR.markNotInteresting(StreamSym); 290 return FerrorNote; 291 } 292 293 return ""; 294 }); 295 } 296 297 /// If true, evaluate special testing stream functions. 298 bool TestMode = false; 299 300 /// If true, generate failure branches for cases that are often not checked. 301 bool PedanticMode = false; 302 303 private: 304 CallDescriptionMap<FnDescription> FnDescriptions = { 305 {{CDM::CLibrary, {"fopen"}, 2}, 306 {nullptr, &StreamChecker::evalFopen, ArgNone}}, 307 {{CDM::CLibrary, {"fdopen"}, 2}, 308 {nullptr, &StreamChecker::evalFopen, ArgNone}}, 309 {{CDM::CLibrary, {"freopen"}, 3}, 310 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, 311 {{CDM::CLibrary, {"tmpfile"}, 0}, 312 {nullptr, &StreamChecker::evalFopen, ArgNone}}, 313 {{CDM::CLibrary, {"fclose"}, 1}, 314 {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, 315 {{CDM::CLibrary, {"fread"}, 4}, 316 {&StreamChecker::preRead, 317 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, 318 {{CDM::CLibrary, {"fwrite"}, 4}, 319 {&StreamChecker::preWrite, 320 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, 321 {{CDM::CLibrary, {"fgetc"}, 1}, 322 {&StreamChecker::preRead, 323 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}}, 324 {{CDM::CLibrary, {"fgets"}, 3}, 325 {&StreamChecker::preRead, 326 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}}, 327 {{CDM::CLibrary, {"getc"}, 1}, 328 {&StreamChecker::preRead, 329 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}}, 330 {{CDM::CLibrary, {"fputc"}, 2}, 331 {&StreamChecker::preWrite, 332 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}}, 333 {{CDM::CLibrary, {"fputs"}, 2}, 334 {&StreamChecker::preWrite, 335 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}}, 336 {{CDM::CLibrary, {"putc"}, 2}, 337 {&StreamChecker::preWrite, 338 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}}, 339 {{CDM::CLibrary, {"fprintf"}}, 340 {&StreamChecker::preWrite, 341 std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}}, 342 {{CDM::CLibrary, {"vfprintf"}, 3}, 343 {&StreamChecker::preWrite, 344 std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}}, 345 {{CDM::CLibrary, {"fscanf"}}, 346 {&StreamChecker::preRead, 347 std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}}, 348 {{CDM::CLibrary, {"vfscanf"}, 3}, 349 {&StreamChecker::preRead, 350 std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}}, 351 {{CDM::CLibrary, {"ungetc"}, 2}, 352 {&StreamChecker::preWrite, 353 std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}}, 354 {{CDM::CLibrary, {"getdelim"}, 4}, 355 {&StreamChecker::preRead, 356 std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 3}}, 357 {{CDM::CLibrary, {"getline"}, 3}, 358 {&StreamChecker::preRead, 359 std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 2}}, 360 {{CDM::CLibrary, {"fseek"}, 3}, 361 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 362 {{CDM::CLibrary, {"fseeko"}, 3}, 363 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 364 {{CDM::CLibrary, {"ftell"}, 1}, 365 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}}, 366 {{CDM::CLibrary, {"ftello"}, 1}, 367 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}}, 368 {{CDM::CLibrary, {"fflush"}, 1}, 369 {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}}, 370 {{CDM::CLibrary, {"rewind"}, 1}, 371 {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}}, 372 {{CDM::CLibrary, {"fgetpos"}, 2}, 373 {&StreamChecker::preWrite, &StreamChecker::evalFgetpos, 0}}, 374 {{CDM::CLibrary, {"fsetpos"}, 2}, 375 {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}}, 376 {{CDM::CLibrary, {"clearerr"}, 1}, 377 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, 378 {{CDM::CLibrary, {"feof"}, 1}, 379 {&StreamChecker::preDefault, 380 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), 381 0}}, 382 {{CDM::CLibrary, {"ferror"}, 1}, 383 {&StreamChecker::preDefault, 384 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError), 385 0}}, 386 {{CDM::CLibrary, {"fileno"}, 1}, 387 {&StreamChecker::preDefault, &StreamChecker::evalFileno, 0}}, 388 }; 389 390 CallDescriptionMap<FnDescription> FnTestDescriptions = { 391 {{CDM::SimpleFunc, {"StreamTesterChecker_make_feof_stream"}, 1}, 392 {nullptr, 393 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof, 394 false), 395 0}}, 396 {{CDM::SimpleFunc, {"StreamTesterChecker_make_ferror_stream"}, 1}, 397 {nullptr, 398 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, 399 ErrorFError, false), 400 0}}, 401 {{CDM::SimpleFunc, 402 {"StreamTesterChecker_make_ferror_indeterminate_stream"}, 403 1}, 404 {nullptr, 405 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, 406 ErrorFError, true), 407 0}}, 408 }; 409 410 /// Expanded value of EOF, empty before initialization. 411 mutable std::optional<int> EofVal; 412 /// Expanded value of SEEK_SET, 0 if not found. 413 mutable int SeekSetVal = 0; 414 /// Expanded value of SEEK_CUR, 1 if not found. 415 mutable int SeekCurVal = 1; 416 /// Expanded value of SEEK_END, 2 if not found. 417 mutable int SeekEndVal = 2; 418 /// The built-in va_list type is platform-specific 419 mutable QualType VaListType; 420 421 void evalFopen(const FnDescription *Desc, const CallEvent &Call, 422 CheckerContext &C) const; 423 424 void preFreopen(const FnDescription *Desc, const CallEvent &Call, 425 CheckerContext &C) const; 426 void evalFreopen(const FnDescription *Desc, const CallEvent &Call, 427 CheckerContext &C) const; 428 429 void evalFclose(const FnDescription *Desc, const CallEvent &Call, 430 CheckerContext &C) const; 431 432 void preRead(const FnDescription *Desc, const CallEvent &Call, 433 CheckerContext &C) const; 434 435 void preWrite(const FnDescription *Desc, const CallEvent &Call, 436 CheckerContext &C) const; 437 438 void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, 439 CheckerContext &C, bool IsFread) const; 440 441 void evalFgetx(const FnDescription *Desc, const CallEvent &Call, 442 CheckerContext &C, bool SingleChar) const; 443 444 void evalFputx(const FnDescription *Desc, const CallEvent &Call, 445 CheckerContext &C, bool IsSingleChar) const; 446 447 void evalFprintf(const FnDescription *Desc, const CallEvent &Call, 448 CheckerContext &C) const; 449 450 void evalFscanf(const FnDescription *Desc, const CallEvent &Call, 451 CheckerContext &C) const; 452 453 void evalUngetc(const FnDescription *Desc, const CallEvent &Call, 454 CheckerContext &C) const; 455 456 void evalGetdelim(const FnDescription *Desc, const CallEvent &Call, 457 CheckerContext &C) const; 458 459 void preFseek(const FnDescription *Desc, const CallEvent &Call, 460 CheckerContext &C) const; 461 void evalFseek(const FnDescription *Desc, const CallEvent &Call, 462 CheckerContext &C) const; 463 464 void evalFgetpos(const FnDescription *Desc, const CallEvent &Call, 465 CheckerContext &C) const; 466 467 void evalFsetpos(const FnDescription *Desc, const CallEvent &Call, 468 CheckerContext &C) const; 469 470 void evalFtell(const FnDescription *Desc, const CallEvent &Call, 471 CheckerContext &C) const; 472 473 void evalRewind(const FnDescription *Desc, const CallEvent &Call, 474 CheckerContext &C) const; 475 476 void preDefault(const FnDescription *Desc, const CallEvent &Call, 477 CheckerContext &C) const; 478 479 void evalClearerr(const FnDescription *Desc, const CallEvent &Call, 480 CheckerContext &C) const; 481 482 void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call, 483 CheckerContext &C, 484 const StreamErrorState &ErrorKind) const; 485 486 void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call, 487 CheckerContext &C, const StreamErrorState &ErrorKind, 488 bool Indeterminate) const; 489 490 void preFflush(const FnDescription *Desc, const CallEvent &Call, 491 CheckerContext &C) const; 492 493 void evalFflush(const FnDescription *Desc, const CallEvent &Call, 494 CheckerContext &C) const; 495 496 void evalFileno(const FnDescription *Desc, const CallEvent &Call, 497 CheckerContext &C) const; 498 499 /// Check that the stream (in StreamVal) is not NULL. 500 /// If it can only be NULL a fatal error is emitted and nullptr returned. 501 /// Otherwise the return value is a new state where the stream is constrained 502 /// to be non-null. 503 ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, 504 CheckerContext &C, 505 ProgramStateRef State) const; 506 507 /// Check that the stream is the opened state. 508 /// If the stream is known to be not opened an error is generated 509 /// and nullptr returned, otherwise the original state is returned. 510 ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C, 511 ProgramStateRef State) const; 512 513 /// Check that the stream has not an invalid ("indeterminate") file position, 514 /// generate warning for it. 515 /// (EOF is not an invalid position.) 516 /// The returned state can be nullptr if a fatal error was generated. 517 /// It can return non-null state if the stream has not an invalid position or 518 /// there is execution path with non-invalid position. 519 ProgramStateRef 520 ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C, 521 ProgramStateRef State) const; 522 523 /// Check the legality of the 'whence' argument of 'fseek'. 524 /// Generate error and return nullptr if it is found to be illegal. 525 /// Otherwise returns the state. 526 /// (State is not changed here because the "whence" value is already known.) 527 ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 528 ProgramStateRef State) const; 529 530 /// Generate warning about stream in EOF state. 531 /// There will be always a state transition into the passed State, 532 /// by the new non-fatal error node or (if failed) a normal transition, 533 /// to ensure uniform handling. 534 void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C, 535 ProgramStateRef State) const; 536 537 /// Emit resource leak warnings for the given symbols. 538 /// Createn a non-fatal error node for these, and returns it (if any warnings 539 /// were generated). Return value is non-null. 540 ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 541 CheckerContext &C, ExplodedNode *Pred) const; 542 543 /// Find the description data of the function called by a call event. 544 /// Returns nullptr if no function is recognized. 545 const FnDescription *lookupFn(const CallEvent &Call) const { 546 // Recognize "global C functions" with only integral or pointer arguments 547 // (and matching name) as stream functions. 548 for (auto *P : Call.parameters()) { 549 QualType T = P->getType(); 550 if (!T->isIntegralOrEnumerationType() && !T->isPointerType() && 551 T.getCanonicalType() != VaListType) 552 return nullptr; 553 } 554 555 return FnDescriptions.lookup(Call); 556 } 557 558 /// Generate a message for BugReporterVisitor if the stored symbol is 559 /// marked as interesting by the actual bug report. 560 const NoteTag *constructLeakNoteTag(CheckerContext &C, SymbolRef StreamSym, 561 const std::string &Message) const { 562 return C.getNoteTag([this, StreamSym, 563 Message](PathSensitiveBugReport &BR) -> std::string { 564 if (BR.isInteresting(StreamSym) && &BR.getBugType() == &BT_ResourceLeak) 565 return Message; 566 return ""; 567 }); 568 } 569 570 void initMacroValues(CheckerContext &C) const { 571 if (EofVal) 572 return; 573 574 if (const std::optional<int> OptInt = 575 tryExpandAsInteger("EOF", C.getPreprocessor())) 576 EofVal = *OptInt; 577 else 578 EofVal = -1; 579 if (const std::optional<int> OptInt = 580 tryExpandAsInteger("SEEK_SET", C.getPreprocessor())) 581 SeekSetVal = *OptInt; 582 if (const std::optional<int> OptInt = 583 tryExpandAsInteger("SEEK_END", C.getPreprocessor())) 584 SeekEndVal = *OptInt; 585 if (const std::optional<int> OptInt = 586 tryExpandAsInteger("SEEK_CUR", C.getPreprocessor())) 587 SeekCurVal = *OptInt; 588 } 589 590 void initVaListType(CheckerContext &C) const { 591 VaListType = C.getASTContext().getBuiltinVaListType().getCanonicalType(); 592 } 593 594 /// Searches for the ExplodedNode where the file descriptor was acquired for 595 /// StreamSym. 596 static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, 597 SymbolRef StreamSym, 598 CheckerContext &C); 599 }; 600 601 struct StreamOperationEvaluator { 602 SValBuilder &SVB; 603 const ASTContext &ACtx; 604 605 SymbolRef StreamSym = nullptr; 606 const StreamState *SS = nullptr; 607 const CallExpr *CE = nullptr; 608 StreamErrorState NewES; 609 610 StreamOperationEvaluator(CheckerContext &C) 611 : SVB(C.getSValBuilder()), ACtx(C.getASTContext()) { 612 ; 613 } 614 615 bool Init(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C, 616 ProgramStateRef State) { 617 StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 618 if (!StreamSym) 619 return false; 620 SS = State->get<StreamMap>(StreamSym); 621 if (!SS) 622 return false; 623 NewES = SS->ErrorState; 624 CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 625 if (!CE) 626 return false; 627 628 assertStreamStateOpened(SS); 629 630 return true; 631 } 632 633 bool isStreamEof() const { return SS->ErrorState == ErrorFEof; } 634 635 NonLoc getZeroVal(const CallEvent &Call) { 636 return *SVB.makeZeroVal(Call.getResultType()).getAs<NonLoc>(); 637 } 638 639 ProgramStateRef setStreamState(ProgramStateRef State, 640 const StreamState &NewSS) { 641 NewES = NewSS.ErrorState; 642 return State->set<StreamMap>(StreamSym, NewSS); 643 } 644 645 ProgramStateRef makeAndBindRetVal(ProgramStateRef State, CheckerContext &C) { 646 NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 647 return State->BindExpr(CE, C.getLocationContext(), RetVal); 648 } 649 650 ProgramStateRef bindReturnValue(ProgramStateRef State, CheckerContext &C, 651 uint64_t Val) { 652 return State->BindExpr(CE, C.getLocationContext(), 653 SVB.makeIntVal(Val, CE->getCallReturnType(ACtx))); 654 } 655 656 ProgramStateRef bindReturnValue(ProgramStateRef State, CheckerContext &C, 657 SVal Val) { 658 return State->BindExpr(CE, C.getLocationContext(), Val); 659 } 660 661 ProgramStateRef bindNullReturnValue(ProgramStateRef State, 662 CheckerContext &C) { 663 return State->BindExpr(CE, C.getLocationContext(), 664 C.getSValBuilder().makeNullWithType(CE->getType())); 665 } 666 667 ProgramStateRef assumeBinOpNN(ProgramStateRef State, 668 BinaryOperator::Opcode Op, NonLoc LHS, 669 NonLoc RHS) { 670 auto Cond = SVB.evalBinOpNN(State, Op, LHS, RHS, SVB.getConditionType()) 671 .getAs<DefinedOrUnknownSVal>(); 672 if (!Cond) 673 return nullptr; 674 return State->assume(*Cond, true); 675 } 676 677 ConstraintManager::ProgramStatePair 678 makeRetValAndAssumeDual(ProgramStateRef State, CheckerContext &C) { 679 DefinedSVal RetVal = makeRetVal(C, CE); 680 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 681 return C.getConstraintManager().assumeDual(State, RetVal); 682 } 683 684 const NoteTag *getFailureNoteTag(const StreamChecker *Ch, CheckerContext &C) { 685 bool SetFeof = NewES.FEof && !SS->ErrorState.FEof; 686 bool SetFerror = NewES.FError && !SS->ErrorState.FError; 687 if (SetFeof && !SetFerror) 688 return Ch->constructSetEofNoteTag(C, StreamSym); 689 if (!SetFeof && SetFerror) 690 return Ch->constructSetErrorNoteTag(C, StreamSym); 691 if (SetFeof && SetFerror) 692 return Ch->constructSetEofOrErrorNoteTag(C, StreamSym); 693 return nullptr; 694 } 695 }; 696 697 } // end anonymous namespace 698 699 const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, 700 SymbolRef StreamSym, 701 CheckerContext &C) { 702 ProgramStateRef State = N->getState(); 703 // When bug type is resource leak, exploded node N may not have state info 704 // for leaked file descriptor, but predecessor should have it. 705 if (!State->get<StreamMap>(StreamSym)) 706 N = N->getFirstPred(); 707 708 const ExplodedNode *Pred = N; 709 while (N) { 710 State = N->getState(); 711 if (!State->get<StreamMap>(StreamSym)) 712 return Pred; 713 Pred = N; 714 N = N->getFirstPred(); 715 } 716 717 return nullptr; 718 } 719 720 static std::optional<int64_t> getKnownValue(ProgramStateRef State, SVal V) { 721 SValBuilder &SVB = State->getStateManager().getSValBuilder(); 722 if (const llvm::APSInt *Int = SVB.getKnownValue(State, V)) 723 return Int->tryExtValue(); 724 return std::nullopt; 725 } 726 727 /// Invalidate only the requested elements instead of the whole buffer. 728 /// This is basically a refinement of the more generic 'escapeArgs' or 729 /// the plain old 'invalidateRegions'. 730 static ProgramStateRef 731 escapeByStartIndexAndCount(ProgramStateRef State, const CallEvent &Call, 732 unsigned BlockCount, const SubRegion *Buffer, 733 QualType ElemType, int64_t StartIndex, 734 int64_t ElementCount) { 735 constexpr auto DoNotInvalidateSuperRegion = 736 RegionAndSymbolInvalidationTraits::InvalidationKinds:: 737 TK_DoNotInvalidateSuperRegion; 738 739 const LocationContext *LCtx = Call.getLocationContext(); 740 const ASTContext &Ctx = State->getStateManager().getContext(); 741 SValBuilder &SVB = State->getStateManager().getSValBuilder(); 742 auto &RegionManager = Buffer->getMemRegionManager(); 743 744 SmallVector<SVal> EscapingVals; 745 EscapingVals.reserve(ElementCount); 746 747 RegionAndSymbolInvalidationTraits ITraits; 748 for (auto Idx : llvm::seq(StartIndex, StartIndex + ElementCount)) { 749 NonLoc Index = SVB.makeArrayIndex(Idx); 750 const auto *Element = 751 RegionManager.getElementRegion(ElemType, Index, Buffer, Ctx); 752 EscapingVals.push_back(loc::MemRegionVal(Element)); 753 ITraits.setTrait(Element, DoNotInvalidateSuperRegion); 754 } 755 return State->invalidateRegions( 756 EscapingVals, Call.getOriginExpr(), BlockCount, LCtx, 757 /*CausesPointerEscape=*/false, 758 /*InvalidatedSymbols=*/nullptr, &Call, &ITraits); 759 } 760 761 static ProgramStateRef escapeArgs(ProgramStateRef State, CheckerContext &C, 762 const CallEvent &Call, 763 ArrayRef<unsigned int> EscapingArgs) { 764 auto GetArgSVal = [&Call](int Idx) { return Call.getArgSVal(Idx); }; 765 auto EscapingVals = to_vector(map_range(EscapingArgs, GetArgSVal)); 766 State = State->invalidateRegions(EscapingVals, Call.getOriginExpr(), 767 C.blockCount(), C.getLocationContext(), 768 /*CausesPointerEscape=*/false, 769 /*InvalidatedSymbols=*/nullptr); 770 return State; 771 } 772 773 //===----------------------------------------------------------------------===// 774 // Methods of StreamChecker. 775 //===----------------------------------------------------------------------===// 776 777 void StreamChecker::checkPreCall(const CallEvent &Call, 778 CheckerContext &C) const { 779 initMacroValues(C); 780 initVaListType(C); 781 782 const FnDescription *Desc = lookupFn(Call); 783 if (!Desc || !Desc->PreFn) 784 return; 785 786 Desc->PreFn(this, Desc, Call, C); 787 } 788 789 bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { 790 const FnDescription *Desc = lookupFn(Call); 791 if (!Desc && TestMode) 792 Desc = FnTestDescriptions.lookup(Call); 793 if (!Desc || !Desc->EvalFn) 794 return false; 795 796 Desc->EvalFn(this, Desc, Call, C); 797 798 return C.isDifferent(); 799 } 800 801 void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call, 802 CheckerContext &C) const { 803 ProgramStateRef State = C.getState(); 804 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 805 if (!CE) 806 return; 807 808 DefinedSVal RetVal = makeRetVal(C, CE); 809 SymbolRef RetSym = RetVal.getAsSymbol(); 810 assert(RetSym && "RetVal must be a symbol here."); 811 812 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 813 814 // Bifurcate the state into two: one with a valid FILE* pointer, the other 815 // with a NULL. 816 ProgramStateRef StateNotNull, StateNull; 817 std::tie(StateNotNull, StateNull) = 818 C.getConstraintManager().assumeDual(State, RetVal); 819 820 StateNotNull = 821 StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc)); 822 StateNull = 823 StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc)); 824 825 C.addTransition(StateNotNull, 826 constructLeakNoteTag(C, RetSym, "Stream opened here")); 827 C.addTransition(StateNull); 828 } 829 830 void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call, 831 CheckerContext &C) const { 832 // Do not allow NULL as passed stream pointer but allow a closed stream. 833 ProgramStateRef State = C.getState(); 834 State = ensureStreamNonNull(getStreamArg(Desc, Call), 835 Call.getArgExpr(Desc->StreamArgNo), C, State); 836 if (!State) 837 return; 838 839 C.addTransition(State); 840 } 841 842 void StreamChecker::evalFreopen(const FnDescription *Desc, 843 const CallEvent &Call, 844 CheckerContext &C) const { 845 ProgramStateRef State = C.getState(); 846 847 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 848 if (!CE) 849 return; 850 851 std::optional<DefinedSVal> StreamVal = 852 getStreamArg(Desc, Call).getAs<DefinedSVal>(); 853 if (!StreamVal) 854 return; 855 856 SymbolRef StreamSym = StreamVal->getAsSymbol(); 857 // Do not care about concrete values for stream ("(FILE *)0x12345"?). 858 // FIXME: Can be stdin, stdout, stderr such values? 859 if (!StreamSym) 860 return; 861 862 // Do not handle untracked stream. It is probably escaped. 863 if (!State->get<StreamMap>(StreamSym)) 864 return; 865 866 // Generate state for non-failed case. 867 // Return value is the passed stream pointer. 868 // According to the documentations, the stream is closed first 869 // but any close error is ignored. The state changes to (or remains) opened. 870 ProgramStateRef StateRetNotNull = 871 State->BindExpr(CE, C.getLocationContext(), *StreamVal); 872 // Generate state for NULL return value. 873 // Stream switches to OpenFailed state. 874 ProgramStateRef StateRetNull = 875 State->BindExpr(CE, C.getLocationContext(), 876 C.getSValBuilder().makeNullWithType(CE->getType())); 877 878 StateRetNotNull = 879 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 880 StateRetNull = 881 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc)); 882 883 C.addTransition(StateRetNotNull, 884 constructLeakNoteTag(C, StreamSym, "Stream reopened here")); 885 C.addTransition(StateRetNull); 886 } 887 888 void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, 889 CheckerContext &C) const { 890 ProgramStateRef State = C.getState(); 891 StreamOperationEvaluator E(C); 892 if (!E.Init(Desc, Call, C, State)) 893 return; 894 895 // Close the File Descriptor. 896 // Regardless if the close fails or not, stream becomes "closed" 897 // and can not be used any more. 898 State = E.setStreamState(State, StreamState::getClosed(Desc)); 899 900 // Return 0 on success, EOF on failure. 901 C.addTransition(E.bindReturnValue(State, C, 0)); 902 C.addTransition(E.bindReturnValue(State, C, *EofVal)); 903 } 904 905 void StreamChecker::preRead(const FnDescription *Desc, const CallEvent &Call, 906 CheckerContext &C) const { 907 ProgramStateRef State = C.getState(); 908 SVal StreamVal = getStreamArg(Desc, Call); 909 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 910 State); 911 if (!State) 912 return; 913 State = ensureStreamOpened(StreamVal, C, State); 914 if (!State) 915 return; 916 State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 917 if (!State) 918 return; 919 920 SymbolRef Sym = StreamVal.getAsSymbol(); 921 if (Sym && State->get<StreamMap>(Sym)) { 922 const StreamState *SS = State->get<StreamMap>(Sym); 923 if (SS->ErrorState & ErrorFEof) 924 reportFEofWarning(Sym, C, State); 925 } else { 926 C.addTransition(State); 927 } 928 } 929 930 void StreamChecker::preWrite(const FnDescription *Desc, const CallEvent &Call, 931 CheckerContext &C) const { 932 ProgramStateRef State = C.getState(); 933 SVal StreamVal = getStreamArg(Desc, Call); 934 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 935 State); 936 if (!State) 937 return; 938 State = ensureStreamOpened(StreamVal, C, State); 939 if (!State) 940 return; 941 State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 942 if (!State) 943 return; 944 945 C.addTransition(State); 946 } 947 948 static std::optional<QualType> getPointeeType(const MemRegion *R) { 949 if (!R) 950 return std::nullopt; 951 if (const auto *ER = dyn_cast<ElementRegion>(R)) 952 return ER->getElementType(); 953 if (const auto *TR = dyn_cast<TypedValueRegion>(R)) 954 return TR->getValueType(); 955 if (const auto *SR = dyn_cast<SymbolicRegion>(R)) 956 return SR->getPointeeStaticType(); 957 return std::nullopt; 958 } 959 960 static std::optional<NonLoc> getStartIndex(SValBuilder &SVB, 961 const MemRegion *R) { 962 if (!R) 963 return std::nullopt; 964 965 auto Zero = [&SVB] { 966 BasicValueFactory &BVF = SVB.getBasicValueFactory(); 967 return nonloc::ConcreteInt(BVF.getIntValue(0, /*isUnsigned=*/false)); 968 }; 969 970 if (const auto *ER = dyn_cast<ElementRegion>(R)) 971 return ER->getIndex(); 972 if (isa<TypedValueRegion>(R)) 973 return Zero(); 974 if (isa<SymbolicRegion>(R)) 975 return Zero(); 976 return std::nullopt; 977 } 978 979 static ProgramStateRef 980 tryToInvalidateFReadBufferByElements(ProgramStateRef State, CheckerContext &C, 981 const CallEvent &Call, NonLoc SizeVal, 982 NonLoc NMembVal) { 983 // Try to invalidate the individual elements. 984 const auto *Buffer = 985 dyn_cast_or_null<SubRegion>(Call.getArgSVal(0).getAsRegion()); 986 987 std::optional<QualType> ElemTy = getPointeeType(Buffer); 988 std::optional<SVal> StartElementIndex = 989 getStartIndex(C.getSValBuilder(), Buffer); 990 991 // Drop the outermost ElementRegion to get the buffer. 992 if (const auto *ER = dyn_cast_or_null<ElementRegion>(Buffer)) 993 Buffer = dyn_cast<SubRegion>(ER->getSuperRegion()); 994 995 std::optional<int64_t> CountVal = getKnownValue(State, NMembVal); 996 std::optional<int64_t> Size = getKnownValue(State, SizeVal); 997 std::optional<int64_t> StartIndexVal = 998 getKnownValue(State, StartElementIndex.value_or(UnknownVal())); 999 1000 if (ElemTy && CountVal && Size && StartIndexVal) { 1001 int64_t NumBytesRead = Size.value() * CountVal.value(); 1002 int64_t ElemSizeInChars = 1003 C.getASTContext().getTypeSizeInChars(*ElemTy).getQuantity(); 1004 bool IncompleteLastElement = (NumBytesRead % ElemSizeInChars) != 0; 1005 int64_t NumCompleteOrIncompleteElementsRead = 1006 NumBytesRead / ElemSizeInChars + IncompleteLastElement; 1007 1008 constexpr int MaxInvalidatedElementsLimit = 64; 1009 if (NumCompleteOrIncompleteElementsRead <= MaxInvalidatedElementsLimit) { 1010 return escapeByStartIndexAndCount(State, Call, C.blockCount(), Buffer, 1011 *ElemTy, *StartIndexVal, 1012 NumCompleteOrIncompleteElementsRead); 1013 } 1014 } 1015 return nullptr; 1016 } 1017 1018 void StreamChecker::evalFreadFwrite(const FnDescription *Desc, 1019 const CallEvent &Call, CheckerContext &C, 1020 bool IsFread) const { 1021 ProgramStateRef State = C.getState(); 1022 StreamOperationEvaluator E(C); 1023 if (!E.Init(Desc, Call, C, State)) 1024 return; 1025 1026 std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); 1027 if (!SizeVal) 1028 return; 1029 std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); 1030 if (!NMembVal) 1031 return; 1032 1033 // C'99 standard, §7.19.8.1.3, the return value of fread: 1034 // The fread function returns the number of elements successfully read, which 1035 // may be less than nmemb if a read error or end-of-file is encountered. If 1036 // size or nmemb is zero, fread returns zero and the contents of the array and 1037 // the state of the stream remain unchanged. 1038 if (State->isNull(*SizeVal).isConstrainedTrue() || 1039 State->isNull(*NMembVal).isConstrainedTrue()) { 1040 // This is the "size or nmemb is zero" case. 1041 // Just return 0, do nothing more (not clear the error flags). 1042 C.addTransition(E.bindReturnValue(State, C, 0)); 1043 return; 1044 } 1045 1046 // At read, invalidate the buffer in any case of error or success, 1047 // except if EOF was already present. 1048 if (IsFread && !E.isStreamEof()) { 1049 // Try to invalidate the individual elements. 1050 // Otherwise just fall back to invalidating the whole buffer. 1051 ProgramStateRef InvalidatedState = tryToInvalidateFReadBufferByElements( 1052 State, C, Call, *SizeVal, *NMembVal); 1053 State = 1054 InvalidatedState ? InvalidatedState : escapeArgs(State, C, Call, {0}); 1055 } 1056 1057 // Generate a transition for the success state. 1058 // If we know the state to be FEOF at fread, do not add a success state. 1059 if (!IsFread || !E.isStreamEof()) { 1060 ProgramStateRef StateNotFailed = 1061 State->BindExpr(E.CE, C.getLocationContext(), *NMembVal); 1062 StateNotFailed = 1063 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 1064 C.addTransition(StateNotFailed); 1065 } 1066 1067 // Add transition for the failed state. 1068 // At write, add failure case only if "pedantic mode" is on. 1069 if (!IsFread && !PedanticMode) 1070 return; 1071 1072 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1073 ProgramStateRef StateFailed = 1074 State->BindExpr(E.CE, C.getLocationContext(), RetVal); 1075 StateFailed = E.assumeBinOpNN(StateFailed, BO_LT, RetVal, *NMembVal); 1076 if (!StateFailed) 1077 return; 1078 1079 StreamErrorState NewES; 1080 if (IsFread) 1081 NewES = E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError; 1082 else 1083 NewES = ErrorFError; 1084 // If a (non-EOF) error occurs, the resulting value of the file position 1085 // indicator for the stream is indeterminate. 1086 StateFailed = E.setStreamState( 1087 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof())); 1088 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1089 } 1090 1091 void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call, 1092 CheckerContext &C, bool SingleChar) const { 1093 // `fgetc` returns the read character on success, otherwise returns EOF. 1094 // `fgets` returns the read buffer address on success, otherwise returns NULL. 1095 1096 ProgramStateRef State = C.getState(); 1097 StreamOperationEvaluator E(C); 1098 if (!E.Init(Desc, Call, C, State)) 1099 return; 1100 1101 if (!E.isStreamEof()) { 1102 // If there was already EOF, assume that read buffer is not changed. 1103 // Otherwise it may change at success or failure. 1104 State = escapeArgs(State, C, Call, {0}); 1105 if (SingleChar) { 1106 // Generate a transition for the success state of `fgetc`. 1107 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1108 ProgramStateRef StateNotFailed = 1109 State->BindExpr(E.CE, C.getLocationContext(), RetVal); 1110 // The returned 'unsigned char' of `fgetc` is converted to 'int', 1111 // so we need to check if it is in range [0, 255]. 1112 StateNotFailed = StateNotFailed->assumeInclusiveRange( 1113 RetVal, 1114 E.SVB.getBasicValueFactory().getValue(0, E.ACtx.UnsignedCharTy), 1115 E.SVB.getBasicValueFactory().getMaxValue(E.ACtx.UnsignedCharTy), 1116 true); 1117 if (!StateNotFailed) 1118 return; 1119 C.addTransition(StateNotFailed); 1120 } else { 1121 // Generate a transition for the success state of `fgets`. 1122 std::optional<DefinedSVal> GetBuf = 1123 Call.getArgSVal(0).getAs<DefinedSVal>(); 1124 if (!GetBuf) 1125 return; 1126 ProgramStateRef StateNotFailed = 1127 State->BindExpr(E.CE, C.getLocationContext(), *GetBuf); 1128 StateNotFailed = 1129 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 1130 C.addTransition(StateNotFailed); 1131 } 1132 } 1133 1134 // Add transition for the failed state. 1135 ProgramStateRef StateFailed; 1136 if (SingleChar) 1137 StateFailed = E.bindReturnValue(State, C, *EofVal); 1138 else 1139 StateFailed = E.bindNullReturnValue(State, C); 1140 1141 // If a (non-EOF) error occurs, the resulting value of the file position 1142 // indicator for the stream is indeterminate. 1143 StreamErrorState NewES = 1144 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError; 1145 StateFailed = E.setStreamState( 1146 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof())); 1147 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1148 } 1149 1150 void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, 1151 CheckerContext &C, bool IsSingleChar) const { 1152 // `fputc` returns the written character on success, otherwise returns EOF. 1153 // `fputs` returns a nonnegative value on success, otherwise returns EOF. 1154 1155 ProgramStateRef State = C.getState(); 1156 StreamOperationEvaluator E(C); 1157 if (!E.Init(Desc, Call, C, State)) 1158 return; 1159 1160 if (IsSingleChar) { 1161 // Generate a transition for the success state of `fputc`. 1162 std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>(); 1163 if (!PutVal) 1164 return; 1165 ProgramStateRef StateNotFailed = 1166 State->BindExpr(E.CE, C.getLocationContext(), *PutVal); 1167 StateNotFailed = 1168 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 1169 C.addTransition(StateNotFailed); 1170 } else { 1171 // Generate a transition for the success state of `fputs`. 1172 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1173 ProgramStateRef StateNotFailed = 1174 State->BindExpr(E.CE, C.getLocationContext(), RetVal); 1175 StateNotFailed = 1176 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call)); 1177 if (!StateNotFailed) 1178 return; 1179 StateNotFailed = 1180 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 1181 C.addTransition(StateNotFailed); 1182 } 1183 1184 if (!PedanticMode) 1185 return; 1186 1187 // Add transition for the failed state. The resulting value of the file 1188 // position indicator for the stream is indeterminate. 1189 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal); 1190 StateFailed = E.setStreamState( 1191 StateFailed, StreamState::getOpened(Desc, ErrorFError, true)); 1192 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1193 } 1194 1195 void StreamChecker::evalFprintf(const FnDescription *Desc, 1196 const CallEvent &Call, 1197 CheckerContext &C) const { 1198 if (Call.getNumArgs() < 2) 1199 return; 1200 1201 ProgramStateRef State = C.getState(); 1202 StreamOperationEvaluator E(C); 1203 if (!E.Init(Desc, Call, C, State)) 1204 return; 1205 1206 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1207 State = State->BindExpr(E.CE, C.getLocationContext(), RetVal); 1208 auto Cond = 1209 E.SVB 1210 .evalBinOp(State, BO_GE, RetVal, E.SVB.makeZeroVal(E.ACtx.IntTy), 1211 E.SVB.getConditionType()) 1212 .getAs<DefinedOrUnknownSVal>(); 1213 if (!Cond) 1214 return; 1215 ProgramStateRef StateNotFailed, StateFailed; 1216 std::tie(StateNotFailed, StateFailed) = State->assume(*Cond); 1217 1218 StateNotFailed = 1219 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 1220 C.addTransition(StateNotFailed); 1221 1222 if (!PedanticMode) 1223 return; 1224 1225 // Add transition for the failed state. The resulting value of the file 1226 // position indicator for the stream is indeterminate. 1227 StateFailed = E.setStreamState( 1228 StateFailed, StreamState::getOpened(Desc, ErrorFError, true)); 1229 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1230 } 1231 1232 void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call, 1233 CheckerContext &C) const { 1234 if (Call.getNumArgs() < 2) 1235 return; 1236 1237 ProgramStateRef State = C.getState(); 1238 StreamOperationEvaluator E(C); 1239 if (!E.Init(Desc, Call, C, State)) 1240 return; 1241 1242 // Add the success state. 1243 // In this context "success" means there is not an EOF or other read error 1244 // before any item is matched in 'fscanf'. But there may be match failure, 1245 // therefore return value can be 0 or greater. 1246 // It is not specified what happens if some items (not all) are matched and 1247 // then EOF or read error happens. Now this case is handled like a "success" 1248 // case, and no error flags are set on the stream. This is probably not 1249 // accurate, and the POSIX documentation does not tell more. 1250 if (!E.isStreamEof()) { 1251 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1252 ProgramStateRef StateNotFailed = 1253 State->BindExpr(E.CE, C.getLocationContext(), RetVal); 1254 StateNotFailed = 1255 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call)); 1256 if (!StateNotFailed) 1257 return; 1258 1259 if (auto const *Callee = Call.getCalleeIdentifier(); 1260 !Callee || Callee->getName() != "vfscanf") { 1261 SmallVector<unsigned int> EscArgs; 1262 for (auto EscArg : llvm::seq(2u, Call.getNumArgs())) 1263 EscArgs.push_back(EscArg); 1264 StateNotFailed = escapeArgs(StateNotFailed, C, Call, EscArgs); 1265 } 1266 1267 if (StateNotFailed) 1268 C.addTransition(StateNotFailed); 1269 } 1270 1271 // Add transition for the failed state. 1272 // Error occurs if nothing is matched yet and reading the input fails. 1273 // Error can be EOF, or other error. At "other error" FERROR or 'errno' can 1274 // be set but it is not further specified if all are required to be set. 1275 // Documentation does not mention, but file position will be set to 1276 // indeterminate similarly as at 'fread'. 1277 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal); 1278 StreamErrorState NewES = 1279 E.isStreamEof() ? ErrorFEof : ErrorNone | ErrorFEof | ErrorFError; 1280 StateFailed = E.setStreamState( 1281 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof())); 1282 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1283 } 1284 1285 void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent &Call, 1286 CheckerContext &C) const { 1287 ProgramStateRef State = C.getState(); 1288 StreamOperationEvaluator E(C); 1289 if (!E.Init(Desc, Call, C, State)) 1290 return; 1291 1292 // Generate a transition for the success state. 1293 std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>(); 1294 if (!PutVal) 1295 return; 1296 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, *PutVal); 1297 StateNotFailed = 1298 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 1299 C.addTransition(StateNotFailed); 1300 1301 // Add transition for the failed state. 1302 // Failure of 'ungetc' does not result in feof or ferror state. 1303 // If the PutVal has value of EofVal the function should "fail", but this is 1304 // the same transition as the success state. 1305 // In this case only one state transition is added by the analyzer (the two 1306 // new states may be similar). 1307 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal); 1308 StateFailed = E.setStreamState(StateFailed, StreamState::getOpened(Desc)); 1309 C.addTransition(StateFailed); 1310 } 1311 1312 void StreamChecker::evalGetdelim(const FnDescription *Desc, 1313 const CallEvent &Call, 1314 CheckerContext &C) const { 1315 ProgramStateRef State = C.getState(); 1316 StreamOperationEvaluator E(C); 1317 if (!E.Init(Desc, Call, C, State)) 1318 return; 1319 1320 // Upon successful completion, the getline() and getdelim() functions shall 1321 // return the number of bytes written into the buffer. 1322 // If the end-of-file indicator for the stream is set, the function shall 1323 // return -1. 1324 // If an error occurs, the function shall return -1 and set 'errno'. 1325 1326 if (!E.isStreamEof()) { 1327 // Escape buffer and size (may change by the call). 1328 // May happen even at error (partial read?). 1329 State = escapeArgs(State, C, Call, {0, 1}); 1330 1331 // Add transition for the successful state. 1332 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1333 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, RetVal); 1334 StateNotFailed = 1335 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call)); 1336 1337 // On success, a buffer is allocated. 1338 auto NewLinePtr = getPointeeVal(Call.getArgSVal(0), State); 1339 if (NewLinePtr && isa<DefinedOrUnknownSVal>(*NewLinePtr)) 1340 StateNotFailed = StateNotFailed->assume( 1341 NewLinePtr->castAs<DefinedOrUnknownSVal>(), true); 1342 1343 // The buffer size `*n` must be enough to hold the whole line, and 1344 // greater than the return value, since it has to account for '\0'. 1345 SVal SizePtrSval = Call.getArgSVal(1); 1346 auto NVal = getPointeeVal(SizePtrSval, State); 1347 if (NVal && isa<NonLoc>(*NVal)) { 1348 StateNotFailed = E.assumeBinOpNN(StateNotFailed, BO_GT, 1349 NVal->castAs<NonLoc>(), RetVal); 1350 StateNotFailed = E.bindReturnValue(StateNotFailed, C, RetVal); 1351 } 1352 if (!StateNotFailed) 1353 return; 1354 C.addTransition(StateNotFailed); 1355 } 1356 1357 // Add transition for the failed state. 1358 // If a (non-EOF) error occurs, the resulting value of the file position 1359 // indicator for the stream is indeterminate. 1360 ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1); 1361 StreamErrorState NewES = 1362 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError; 1363 StateFailed = E.setStreamState( 1364 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof())); 1365 // On failure, the content of the buffer is undefined. 1366 if (auto NewLinePtr = getPointeeVal(Call.getArgSVal(0), State)) 1367 StateFailed = StateFailed->bindLoc(*NewLinePtr, UndefinedVal(), 1368 C.getLocationContext()); 1369 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1370 } 1371 1372 void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, 1373 CheckerContext &C) const { 1374 ProgramStateRef State = C.getState(); 1375 SVal StreamVal = getStreamArg(Desc, Call); 1376 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 1377 State); 1378 if (!State) 1379 return; 1380 State = ensureStreamOpened(StreamVal, C, State); 1381 if (!State) 1382 return; 1383 State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); 1384 if (!State) 1385 return; 1386 1387 C.addTransition(State); 1388 } 1389 1390 void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, 1391 CheckerContext &C) const { 1392 ProgramStateRef State = C.getState(); 1393 StreamOperationEvaluator E(C); 1394 if (!E.Init(Desc, Call, C, State)) 1395 return; 1396 1397 // Add success state. 1398 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, 0); 1399 // No failure: Reset the state to opened with no error. 1400 StateNotFailed = 1401 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc)); 1402 C.addTransition(StateNotFailed); 1403 1404 if (!PedanticMode) 1405 return; 1406 1407 // Add failure state. 1408 // At error it is possible that fseek fails but sets none of the error flags. 1409 // If fseek failed, assume that the file position becomes indeterminate in any 1410 // case. 1411 // It is allowed to set the position beyond the end of the file. EOF error 1412 // should not occur. 1413 ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1); 1414 StateFailed = E.setStreamState( 1415 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true)); 1416 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1417 } 1418 1419 void StreamChecker::evalFgetpos(const FnDescription *Desc, 1420 const CallEvent &Call, 1421 CheckerContext &C) const { 1422 ProgramStateRef State = C.getState(); 1423 StreamOperationEvaluator E(C); 1424 if (!E.Init(Desc, Call, C, State)) 1425 return; 1426 1427 ProgramStateRef StateNotFailed, StateFailed; 1428 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C); 1429 StateNotFailed = escapeArgs(StateNotFailed, C, Call, {1}); 1430 1431 // This function does not affect the stream state. 1432 // Still we add success and failure state with the appropriate return value. 1433 // StdLibraryFunctionsChecker can change these states (set the 'errno' state). 1434 C.addTransition(StateNotFailed); 1435 C.addTransition(StateFailed); 1436 } 1437 1438 void StreamChecker::evalFsetpos(const FnDescription *Desc, 1439 const CallEvent &Call, 1440 CheckerContext &C) const { 1441 ProgramStateRef State = C.getState(); 1442 StreamOperationEvaluator E(C); 1443 if (!E.Init(Desc, Call, C, State)) 1444 return; 1445 1446 ProgramStateRef StateNotFailed, StateFailed; 1447 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C); 1448 1449 StateNotFailed = E.setStreamState( 1450 StateNotFailed, StreamState::getOpened(Desc, ErrorNone, false)); 1451 C.addTransition(StateNotFailed); 1452 1453 if (!PedanticMode) 1454 return; 1455 1456 // At failure ferror could be set. 1457 // The standards do not tell what happens with the file position at failure. 1458 // But we can assume that it is dangerous to make a next I/O operation after 1459 // the position was not set correctly (similar to 'fseek'). 1460 StateFailed = E.setStreamState( 1461 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true)); 1462 1463 C.addTransition(StateFailed, E.getFailureNoteTag(this, C)); 1464 } 1465 1466 void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call, 1467 CheckerContext &C) const { 1468 ProgramStateRef State = C.getState(); 1469 StreamOperationEvaluator E(C); 1470 if (!E.Init(Desc, Call, C, State)) 1471 return; 1472 1473 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1474 ProgramStateRef StateNotFailed = 1475 State->BindExpr(E.CE, C.getLocationContext(), RetVal); 1476 StateNotFailed = 1477 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call)); 1478 if (!StateNotFailed) 1479 return; 1480 1481 ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1); 1482 1483 // This function does not affect the stream state. 1484 // Still we add success and failure state with the appropriate return value. 1485 // StdLibraryFunctionsChecker can change these states (set the 'errno' state). 1486 C.addTransition(StateNotFailed); 1487 C.addTransition(StateFailed); 1488 } 1489 1490 void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call, 1491 CheckerContext &C) const { 1492 ProgramStateRef State = C.getState(); 1493 StreamOperationEvaluator E(C); 1494 if (!E.Init(Desc, Call, C, State)) 1495 return; 1496 1497 State = 1498 E.setStreamState(State, StreamState::getOpened(Desc, ErrorNone, false)); 1499 C.addTransition(State); 1500 } 1501 1502 void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call, 1503 CheckerContext &C) const { 1504 ProgramStateRef State = C.getState(); 1505 SVal StreamVal = getStreamArg(Desc, Call); 1506 std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>(); 1507 if (!Stream) 1508 return; 1509 1510 ProgramStateRef StateNotNull, StateNull; 1511 std::tie(StateNotNull, StateNull) = 1512 C.getConstraintManager().assumeDual(State, *Stream); 1513 if (StateNotNull && !StateNull) 1514 ensureStreamOpened(StreamVal, C, StateNotNull); 1515 } 1516 1517 void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, 1518 CheckerContext &C) const { 1519 ProgramStateRef State = C.getState(); 1520 SVal StreamVal = getStreamArg(Desc, Call); 1521 std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>(); 1522 if (!Stream) 1523 return; 1524 1525 // Skip if the stream can be both NULL and non-NULL. 1526 ProgramStateRef StateNotNull, StateNull; 1527 std::tie(StateNotNull, StateNull) = 1528 C.getConstraintManager().assumeDual(State, *Stream); 1529 if (StateNotNull && StateNull) 1530 return; 1531 if (StateNotNull && !StateNull) 1532 State = StateNotNull; 1533 else 1534 State = StateNull; 1535 1536 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 1537 if (!CE) 1538 return; 1539 1540 // `fflush` returns EOF on failure, otherwise returns 0. 1541 ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); 1542 ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); 1543 1544 // Clear error states if `fflush` returns 0, but retain their EOF flags. 1545 auto ClearErrorInNotFailed = [&StateNotFailed, Desc](SymbolRef Sym, 1546 const StreamState *SS) { 1547 if (SS->ErrorState & ErrorFError) { 1548 StreamErrorState NewES = 1549 (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone; 1550 StreamState NewSS = StreamState::getOpened(Desc, NewES, false); 1551 StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS); 1552 } 1553 }; 1554 1555 if (StateNotNull && !StateNull) { 1556 // Skip if the input stream's state is unknown, open-failed or closed. 1557 if (SymbolRef StreamSym = StreamVal.getAsSymbol()) { 1558 const StreamState *SS = State->get<StreamMap>(StreamSym); 1559 if (SS) { 1560 assert(SS->isOpened() && "Stream is expected to be opened"); 1561 ClearErrorInNotFailed(StreamSym, SS); 1562 } else 1563 return; 1564 } 1565 } else { 1566 // Clear error states for all streams. 1567 const StreamMapTy &Map = StateNotFailed->get<StreamMap>(); 1568 for (const auto &I : Map) { 1569 SymbolRef Sym = I.first; 1570 const StreamState &SS = I.second; 1571 if (SS.isOpened()) 1572 ClearErrorInNotFailed(Sym, &SS); 1573 } 1574 } 1575 1576 C.addTransition(StateNotFailed); 1577 C.addTransition(StateFailed); 1578 } 1579 1580 void StreamChecker::evalClearerr(const FnDescription *Desc, 1581 const CallEvent &Call, 1582 CheckerContext &C) const { 1583 ProgramStateRef State = C.getState(); 1584 StreamOperationEvaluator E(C); 1585 if (!E.Init(Desc, Call, C, State)) 1586 return; 1587 1588 // FilePositionIndeterminate is not cleared. 1589 State = E.setStreamState( 1590 State, 1591 StreamState::getOpened(Desc, ErrorNone, E.SS->FilePositionIndeterminate)); 1592 C.addTransition(State); 1593 } 1594 1595 void StreamChecker::evalFeofFerror(const FnDescription *Desc, 1596 const CallEvent &Call, CheckerContext &C, 1597 const StreamErrorState &ErrorKind) const { 1598 ProgramStateRef State = C.getState(); 1599 StreamOperationEvaluator E(C); 1600 if (!E.Init(Desc, Call, C, State)) 1601 return; 1602 1603 if (E.SS->ErrorState & ErrorKind) { 1604 // Execution path with error of ErrorKind. 1605 // Function returns true. 1606 // From now on it is the only one error state. 1607 ProgramStateRef TrueState = bindAndAssumeTrue(State, C, E.CE); 1608 C.addTransition(E.setStreamState( 1609 TrueState, StreamState::getOpened(Desc, ErrorKind, 1610 E.SS->FilePositionIndeterminate && 1611 !ErrorKind.isFEof()))); 1612 } 1613 if (StreamErrorState NewES = E.SS->ErrorState & (~ErrorKind)) { 1614 // Execution path(s) with ErrorKind not set. 1615 // Function returns false. 1616 // New error state is everything before minus ErrorKind. 1617 ProgramStateRef FalseState = E.bindReturnValue(State, C, 0); 1618 C.addTransition(E.setStreamState( 1619 FalseState, 1620 StreamState::getOpened( 1621 Desc, NewES, E.SS->FilePositionIndeterminate && !NewES.isFEof()))); 1622 } 1623 } 1624 1625 void StreamChecker::evalFileno(const FnDescription *Desc, const CallEvent &Call, 1626 CheckerContext &C) const { 1627 // Fileno should fail only if the passed pointer is invalid. 1628 // Some of the preconditions are checked already in preDefault. 1629 // Here we can assume that the operation does not fail, because if we 1630 // introduced a separate branch where fileno() returns -1, then it would cause 1631 // many unexpected and unwanted warnings in situations where fileno() is 1632 // called on valid streams. 1633 // The stream error states are not modified by 'fileno', and 'errno' is also 1634 // left unchanged (so this evalCall does not invalidate it, but we have a 1635 // custom evalCall instead of the default that would invalidate it). 1636 ProgramStateRef State = C.getState(); 1637 StreamOperationEvaluator E(C); 1638 if (!E.Init(Desc, Call, C, State)) 1639 return; 1640 1641 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>(); 1642 State = State->BindExpr(E.CE, C.getLocationContext(), RetVal); 1643 State = E.assumeBinOpNN(State, BO_GE, RetVal, E.getZeroVal(Call)); 1644 if (!State) 1645 return; 1646 1647 C.addTransition(State); 1648 } 1649 1650 void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, 1651 CheckerContext &C) const { 1652 ProgramStateRef State = C.getState(); 1653 SVal StreamVal = getStreamArg(Desc, Call); 1654 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, 1655 State); 1656 if (!State) 1657 return; 1658 State = ensureStreamOpened(StreamVal, C, State); 1659 if (!State) 1660 return; 1661 1662 C.addTransition(State); 1663 } 1664 1665 void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, 1666 const CallEvent &Call, CheckerContext &C, 1667 const StreamErrorState &ErrorKind, 1668 bool Indeterminate) const { 1669 ProgramStateRef State = C.getState(); 1670 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 1671 assert(StreamSym && "Operation not permitted on non-symbolic stream value."); 1672 const StreamState *SS = State->get<StreamMap>(StreamSym); 1673 assert(SS && "Stream should be tracked by the checker."); 1674 State = State->set<StreamMap>( 1675 StreamSym, 1676 StreamState::getOpened(SS->LastOperation, ErrorKind, Indeterminate)); 1677 C.addTransition(State); 1678 } 1679 1680 ProgramStateRef 1681 StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, 1682 CheckerContext &C, 1683 ProgramStateRef State) const { 1684 auto Stream = StreamVal.getAs<DefinedSVal>(); 1685 if (!Stream) 1686 return State; 1687 1688 ConstraintManager &CM = C.getConstraintManager(); 1689 1690 ProgramStateRef StateNotNull, StateNull; 1691 std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream); 1692 1693 if (!StateNotNull && StateNull) { 1694 if (ExplodedNode *N = C.generateErrorNode(StateNull)) { 1695 auto R = std::make_unique<PathSensitiveBugReport>( 1696 BT_FileNull, "Stream pointer might be NULL.", N); 1697 if (StreamE) 1698 bugreporter::trackExpressionValue(N, StreamE, *R); 1699 C.emitReport(std::move(R)); 1700 } 1701 return nullptr; 1702 } 1703 1704 return StateNotNull; 1705 } 1706 1707 ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, 1708 CheckerContext &C, 1709 ProgramStateRef State) const { 1710 SymbolRef Sym = StreamVal.getAsSymbol(); 1711 if (!Sym) 1712 return State; 1713 1714 const StreamState *SS = State->get<StreamMap>(Sym); 1715 if (!SS) 1716 return State; 1717 1718 if (SS->isClosed()) { 1719 // Using a stream pointer after 'fclose' causes undefined behavior 1720 // according to cppreference.com . 1721 ExplodedNode *N = C.generateErrorNode(); 1722 if (N) { 1723 C.emitReport(std::make_unique<PathSensitiveBugReport>( 1724 BT_UseAfterClose, 1725 "Stream might be already closed. Causes undefined behaviour.", N)); 1726 return nullptr; 1727 } 1728 1729 return State; 1730 } 1731 1732 if (SS->isOpenFailed()) { 1733 // Using a stream that has failed to open is likely to cause problems. 1734 // This should usually not occur because stream pointer is NULL. 1735 // But freopen can cause a state when stream pointer remains non-null but 1736 // failed to open. 1737 ExplodedNode *N = C.generateErrorNode(); 1738 if (N) { 1739 C.emitReport(std::make_unique<PathSensitiveBugReport>( 1740 BT_UseAfterOpenFailed, 1741 "Stream might be invalid after " 1742 "(re-)opening it has failed. " 1743 "Can cause undefined behaviour.", 1744 N)); 1745 return nullptr; 1746 } 1747 } 1748 1749 return State; 1750 } 1751 1752 ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate( 1753 SVal StreamVal, CheckerContext &C, ProgramStateRef State) const { 1754 static const char *BugMessage = 1755 "File position of the stream might be 'indeterminate' " 1756 "after a failed operation. " 1757 "Can cause undefined behavior."; 1758 1759 SymbolRef Sym = StreamVal.getAsSymbol(); 1760 if (!Sym) 1761 return State; 1762 1763 const StreamState *SS = State->get<StreamMap>(Sym); 1764 if (!SS) 1765 return State; 1766 1767 assert(SS->isOpened() && "First ensure that stream is opened."); 1768 1769 if (SS->FilePositionIndeterminate) { 1770 if (SS->ErrorState & ErrorFEof) { 1771 // The error is unknown but may be FEOF. 1772 // Continue analysis with the FEOF error state. 1773 // Report warning because the other possible error states. 1774 ExplodedNode *N = C.generateNonFatalErrorNode(State); 1775 if (!N) 1776 return nullptr; 1777 1778 auto R = std::make_unique<PathSensitiveBugReport>( 1779 BT_IndeterminatePosition, BugMessage, N); 1780 R->markInteresting(Sym); 1781 C.emitReport(std::move(R)); 1782 return State->set<StreamMap>( 1783 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false)); 1784 } 1785 1786 // Known or unknown error state without FEOF possible. 1787 // Stop analysis, report error. 1788 if (ExplodedNode *N = C.generateErrorNode(State)) { 1789 auto R = std::make_unique<PathSensitiveBugReport>( 1790 BT_IndeterminatePosition, BugMessage, N); 1791 R->markInteresting(Sym); 1792 C.emitReport(std::move(R)); 1793 } 1794 1795 return nullptr; 1796 } 1797 1798 return State; 1799 } 1800 1801 ProgramStateRef 1802 StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 1803 ProgramStateRef State) const { 1804 std::optional<nonloc::ConcreteInt> CI = 1805 WhenceVal.getAs<nonloc::ConcreteInt>(); 1806 if (!CI) 1807 return State; 1808 1809 int64_t X = CI->getValue().getSExtValue(); 1810 if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal) 1811 return State; 1812 1813 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 1814 C.emitReport(std::make_unique<PathSensitiveBugReport>( 1815 BT_IllegalWhence, 1816 "The whence argument to fseek() should be " 1817 "SEEK_SET, SEEK_END, or SEEK_CUR.", 1818 N)); 1819 return nullptr; 1820 } 1821 1822 return State; 1823 } 1824 1825 void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C, 1826 ProgramStateRef State) const { 1827 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 1828 auto R = std::make_unique<PathSensitiveBugReport>( 1829 BT_StreamEof, 1830 "Read function called when stream is in EOF state. " 1831 "Function has no effect.", 1832 N); 1833 R->markInteresting(StreamSym); 1834 C.emitReport(std::move(R)); 1835 return; 1836 } 1837 C.addTransition(State); 1838 } 1839 1840 ExplodedNode * 1841 StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 1842 CheckerContext &C, ExplodedNode *Pred) const { 1843 ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred); 1844 if (!Err) 1845 return Pred; 1846 1847 for (SymbolRef LeakSym : LeakedSyms) { 1848 // Resource leaks can result in multiple warning that describe the same kind 1849 // of programming error: 1850 // void f() { 1851 // FILE *F = fopen("a.txt"); 1852 // if (rand()) // state split 1853 // return; // warning 1854 // } // warning 1855 // While this isn't necessarily true (leaking the same stream could result 1856 // from a different kinds of errors), the reduction in redundant reports 1857 // makes this a worthwhile heuristic. 1858 // FIXME: Add a checker option to turn this uniqueing feature off. 1859 const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C); 1860 assert(StreamOpenNode && "Could not find place of stream opening."); 1861 1862 PathDiagnosticLocation LocUsedForUniqueing; 1863 if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics()) 1864 LocUsedForUniqueing = PathDiagnosticLocation::createBegin( 1865 StreamStmt, C.getSourceManager(), 1866 StreamOpenNode->getLocationContext()); 1867 1868 std::unique_ptr<PathSensitiveBugReport> R = 1869 std::make_unique<PathSensitiveBugReport>( 1870 BT_ResourceLeak, 1871 "Opened stream never closed. Potential resource leak.", Err, 1872 LocUsedForUniqueing, 1873 StreamOpenNode->getLocationContext()->getDecl()); 1874 R->markInteresting(LeakSym); 1875 C.emitReport(std::move(R)); 1876 } 1877 1878 return Err; 1879 } 1880 1881 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 1882 CheckerContext &C) const { 1883 ProgramStateRef State = C.getState(); 1884 1885 llvm::SmallVector<SymbolRef, 2> LeakedSyms; 1886 1887 const StreamMapTy &Map = State->get<StreamMap>(); 1888 for (const auto &I : Map) { 1889 SymbolRef Sym = I.first; 1890 const StreamState &SS = I.second; 1891 if (!SymReaper.isDead(Sym)) 1892 continue; 1893 if (SS.isOpened()) 1894 LeakedSyms.push_back(Sym); 1895 State = State->remove<StreamMap>(Sym); 1896 } 1897 1898 ExplodedNode *N = C.getPredecessor(); 1899 if (!LeakedSyms.empty()) 1900 N = reportLeaks(LeakedSyms, C, N); 1901 1902 C.addTransition(State, N); 1903 } 1904 1905 ProgramStateRef StreamChecker::checkPointerEscape( 1906 ProgramStateRef State, const InvalidatedSymbols &Escaped, 1907 const CallEvent *Call, PointerEscapeKind Kind) const { 1908 // Check for file-handling system call that is not handled by the checker. 1909 // FIXME: The checker should be updated to handle all system calls that take 1910 // 'FILE*' argument. These are now ignored. 1911 if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader()) 1912 return State; 1913 1914 for (SymbolRef Sym : Escaped) { 1915 // The symbol escaped. 1916 // From now the stream can be manipulated in unknown way to the checker, 1917 // it is not possible to handle it any more. 1918 // Optimistically, assume that the corresponding file handle will be closed 1919 // somewhere else. 1920 // Remove symbol from state so the following stream calls on this symbol are 1921 // not handled by the checker. 1922 State = State->remove<StreamMap>(Sym); 1923 } 1924 return State; 1925 } 1926 1927 //===----------------------------------------------------------------------===// 1928 // Checker registration. 1929 //===----------------------------------------------------------------------===// 1930 1931 void ento::registerStreamChecker(CheckerManager &Mgr) { 1932 auto *Checker = Mgr.registerChecker<StreamChecker>(); 1933 Checker->PedanticMode = 1934 Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "Pedantic"); 1935 } 1936 1937 bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) { 1938 return true; 1939 } 1940 1941 void ento::registerStreamTesterChecker(CheckerManager &Mgr) { 1942 auto *Checker = Mgr.getChecker<StreamChecker>(); 1943 Checker->TestMode = true; 1944 } 1945 1946 bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) { 1947 return true; 1948 } 1949