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