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