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