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