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