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