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