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/CallEvent.h" 18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 22 23 using namespace clang; 24 using namespace ento; 25 26 namespace { 27 28 /// Full state information about a stream pointer. 29 struct StreamState { 30 /// State of a stream symbol. 31 /// FIXME: We need maybe an "escaped" state later. 32 enum KindTy { 33 Opened, /// Stream is opened. 34 Closed, /// Closed stream (an invalid stream pointer after it was closed). 35 OpenFailed /// The last open operation has failed. 36 } State; 37 38 /// The error state of a stream. 39 /// Valid only if the stream is opened. 40 /// It is assumed that feof and ferror flags are never true at the same time. 41 enum ErrorKindTy { 42 /// No error flag is set (or stream is not open). 43 NoError, 44 /// EOF condition (`feof` is true). 45 FEof, 46 /// Other generic (non-EOF) error (`ferror` is true). 47 FError, 48 } ErrorState = NoError; 49 50 bool isOpened() const { return State == Opened; } 51 bool isClosed() const { return State == Closed; } 52 bool isOpenFailed() const { return State == OpenFailed; } 53 54 bool isNoError() const { 55 assert(State == Opened && "Error undefined for closed stream."); 56 return ErrorState == NoError; 57 } 58 bool isFEof() const { 59 assert(State == Opened && "Error undefined for closed stream."); 60 return ErrorState == FEof; 61 } 62 bool isFError() const { 63 assert(State == Opened && "Error undefined for closed stream."); 64 return ErrorState == FError; 65 } 66 67 bool operator==(const StreamState &X) const { 68 // In not opened state error should always NoError. 69 return State == X.State && ErrorState == X.ErrorState; 70 } 71 72 static StreamState getOpened() { return StreamState{Opened}; } 73 static StreamState getClosed() { return StreamState{Closed}; } 74 static StreamState getOpenFailed() { return StreamState{OpenFailed}; } 75 static StreamState getOpenedWithFEof() { return StreamState{Opened, FEof}; } 76 static StreamState getOpenedWithFError() { 77 return StreamState{Opened, FError}; 78 } 79 80 void Profile(llvm::FoldingSetNodeID &ID) const { 81 ID.AddInteger(State); 82 ID.AddInteger(ErrorState); 83 } 84 }; 85 86 class StreamChecker; 87 struct FnDescription; 88 using FnCheck = std::function<void(const StreamChecker *, const FnDescription *, 89 const CallEvent &, CheckerContext &)>; 90 91 using ArgNoTy = unsigned int; 92 static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max(); 93 94 struct FnDescription { 95 FnCheck PreFn; 96 FnCheck EvalFn; 97 ArgNoTy StreamArgNo; 98 }; 99 100 /// Get the value of the stream argument out of the passed call event. 101 /// The call should contain a function that is described by Desc. 102 SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) { 103 assert(Desc && Desc->StreamArgNo != ArgNone && 104 "Try to get a non-existing stream argument."); 105 return Call.getArgSVal(Desc->StreamArgNo); 106 } 107 108 /// Create a conjured symbol return value for a call expression. 109 DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) { 110 assert(CE && "Expecting a call expression."); 111 112 const LocationContext *LCtx = C.getLocationContext(); 113 return C.getSValBuilder() 114 .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()) 115 .castAs<DefinedSVal>(); 116 } 117 118 class StreamChecker 119 : public Checker<check::PreCall, eval::Call, check::DeadSymbols> { 120 mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence, 121 BT_UseAfterClose, BT_UseAfterOpenFailed, BT_ResourceLeak; 122 123 public: 124 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 125 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 126 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 127 128 /// If true, evaluate special testing stream functions. 129 bool TestMode = false; 130 131 private: 132 CallDescriptionMap<FnDescription> FnDescriptions = { 133 {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 134 {{"freopen", 3}, 135 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, 136 {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 137 {{"fclose", 1}, 138 {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, 139 {{"fread", 4}, {&StreamChecker::preDefault, nullptr, 3}}, 140 {{"fwrite", 4}, {&StreamChecker::preDefault, nullptr, 3}}, 141 {{"fseek", 3}, {&StreamChecker::preFseek, nullptr, 0}}, 142 {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 143 {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 144 {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 145 {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 146 {{"clearerr", 1}, 147 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, 148 {{"feof", 1}, 149 {&StreamChecker::preDefault, 150 &StreamChecker::evalFeofFerror<&StreamState::isFEof>, 0}}, 151 {{"ferror", 1}, 152 {&StreamChecker::preDefault, 153 &StreamChecker::evalFeofFerror<&StreamState::isFError>, 0}}, 154 {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 155 }; 156 157 CallDescriptionMap<FnDescription> FnTestDescriptions = { 158 {{"StreamTesterChecker_make_feof_stream", 1}, 159 {nullptr, 160 &StreamChecker::evalSetFeofFerror<&StreamState::getOpenedWithFEof>, 0}}, 161 {{"StreamTesterChecker_make_ferror_stream", 1}, 162 {nullptr, 163 &StreamChecker::evalSetFeofFerror<&StreamState::getOpenedWithFError>, 164 0}}, 165 }; 166 167 void evalFopen(const FnDescription *Desc, const CallEvent &Call, 168 CheckerContext &C) const; 169 170 void preFreopen(const FnDescription *Desc, const CallEvent &Call, 171 CheckerContext &C) const; 172 void evalFreopen(const FnDescription *Desc, const CallEvent &Call, 173 CheckerContext &C) const; 174 175 void evalFclose(const FnDescription *Desc, const CallEvent &Call, 176 CheckerContext &C) const; 177 178 void preFseek(const FnDescription *Desc, const CallEvent &Call, 179 CheckerContext &C) const; 180 181 void preDefault(const FnDescription *Desc, const CallEvent &Call, 182 CheckerContext &C) const; 183 184 void evalClearerr(const FnDescription *Desc, const CallEvent &Call, 185 CheckerContext &C) const; 186 187 template <bool (StreamState::*IsOfError)() const> 188 void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call, 189 CheckerContext &C) const; 190 191 template <StreamState (*GetState)()> 192 void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call, 193 CheckerContext &C) const; 194 195 /// Check that the stream (in StreamVal) is not NULL. 196 /// If it can only be NULL a fatal error is emitted and nullptr returned. 197 /// Otherwise the return value is a new state where the stream is constrained 198 /// to be non-null. 199 ProgramStateRef ensureStreamNonNull(SVal StreamVal, CheckerContext &C, 200 ProgramStateRef State) const; 201 202 /// Check that the stream is the opened state. 203 /// If the stream is known to be not opened an error is generated 204 /// and nullptr returned, otherwise the original state is returned. 205 ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C, 206 ProgramStateRef State) const; 207 208 /// Check the legality of the 'whence' argument of 'fseek'. 209 /// Generate error and return nullptr if it is found to be illegal. 210 /// Otherwise returns the state. 211 /// (State is not changed here because the "whence" value is already known.) 212 ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 213 ProgramStateRef State) const; 214 215 /// Find the description data of the function called by a call event. 216 /// Returns nullptr if no function is recognized. 217 const FnDescription *lookupFn(const CallEvent &Call) const { 218 // Recognize "global C functions" with only integral or pointer arguments 219 // (and matching name) as stream functions. 220 if (!Call.isGlobalCFunction()) 221 return nullptr; 222 for (auto P : Call.parameters()) { 223 QualType T = P->getType(); 224 if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) 225 return nullptr; 226 } 227 228 return FnDescriptions.lookup(Call); 229 } 230 }; 231 232 } // end anonymous namespace 233 234 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 235 236 inline void assertStreamStateOpened(const StreamState *SS) { 237 assert(SS->isOpened() && 238 "Previous create of error node for non-opened stream failed?"); 239 } 240 241 void StreamChecker::checkPreCall(const CallEvent &Call, 242 CheckerContext &C) const { 243 const FnDescription *Desc = lookupFn(Call); 244 if (!Desc || !Desc->PreFn) 245 return; 246 247 Desc->PreFn(this, Desc, Call, C); 248 } 249 250 bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { 251 const FnDescription *Desc = lookupFn(Call); 252 if (!Desc && TestMode) 253 Desc = FnTestDescriptions.lookup(Call); 254 if (!Desc || !Desc->EvalFn) 255 return false; 256 257 Desc->EvalFn(this, Desc, Call, C); 258 259 return C.isDifferent(); 260 } 261 262 void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call, 263 CheckerContext &C) const { 264 ProgramStateRef State = C.getState(); 265 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 266 if (!CE) 267 return; 268 269 DefinedSVal RetVal = makeRetVal(C, CE); 270 SymbolRef RetSym = RetVal.getAsSymbol(); 271 assert(RetSym && "RetVal must be a symbol here."); 272 273 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 274 275 // Bifurcate the state into two: one with a valid FILE* pointer, the other 276 // with a NULL. 277 ProgramStateRef StateNotNull, StateNull; 278 std::tie(StateNotNull, StateNull) = 279 C.getConstraintManager().assumeDual(State, RetVal); 280 281 StateNotNull = StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened()); 282 StateNull = StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed()); 283 284 C.addTransition(StateNotNull); 285 C.addTransition(StateNull); 286 } 287 288 void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call, 289 CheckerContext &C) const { 290 // Do not allow NULL as passed stream pointer but allow a closed stream. 291 ProgramStateRef State = C.getState(); 292 State = ensureStreamNonNull(getStreamArg(Desc, Call), C, State); 293 if (!State) 294 return; 295 296 C.addTransition(State); 297 } 298 299 void StreamChecker::evalFreopen(const FnDescription *Desc, 300 const CallEvent &Call, 301 CheckerContext &C) const { 302 ProgramStateRef State = C.getState(); 303 304 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 305 if (!CE) 306 return; 307 308 Optional<DefinedSVal> StreamVal = 309 getStreamArg(Desc, Call).getAs<DefinedSVal>(); 310 if (!StreamVal) 311 return; 312 313 SymbolRef StreamSym = StreamVal->getAsSymbol(); 314 // Do not care about concrete values for stream ("(FILE *)0x12345"?). 315 // FIXME: Are stdin, stdout, stderr such values? 316 if (!StreamSym) 317 return; 318 319 // Generate state for non-failed case. 320 // Return value is the passed stream pointer. 321 // According to the documentations, the stream is closed first 322 // but any close error is ignored. The state changes to (or remains) opened. 323 ProgramStateRef StateRetNotNull = 324 State->BindExpr(CE, C.getLocationContext(), *StreamVal); 325 // Generate state for NULL return value. 326 // Stream switches to OpenFailed state. 327 ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(), 328 C.getSValBuilder().makeNull()); 329 330 StateRetNotNull = 331 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened()); 332 StateRetNull = 333 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed()); 334 335 C.addTransition(StateRetNotNull); 336 C.addTransition(StateRetNull); 337 } 338 339 void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, 340 CheckerContext &C) const { 341 ProgramStateRef State = C.getState(); 342 SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); 343 if (!Sym) 344 return; 345 346 const StreamState *SS = State->get<StreamMap>(Sym); 347 if (!SS) 348 return; 349 350 assertStreamStateOpened(SS); 351 352 // Close the File Descriptor. 353 // Regardless if the close fails or not, stream becomes "closed" 354 // and can not be used any more. 355 State = State->set<StreamMap>(Sym, StreamState::getClosed()); 356 357 C.addTransition(State); 358 } 359 360 void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, 361 CheckerContext &C) const { 362 ProgramStateRef State = C.getState(); 363 SVal StreamVal = getStreamArg(Desc, Call); 364 State = ensureStreamNonNull(StreamVal, C, State); 365 if (!State) 366 return; 367 State = ensureStreamOpened(StreamVal, C, State); 368 if (!State) 369 return; 370 State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); 371 if (!State) 372 return; 373 374 C.addTransition(State); 375 } 376 377 void StreamChecker::evalClearerr(const FnDescription *Desc, 378 const CallEvent &Call, 379 CheckerContext &C) const { 380 ProgramStateRef State = C.getState(); 381 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 382 if (!StreamSym) 383 return; 384 385 const StreamState *SS = State->get<StreamMap>(StreamSym); 386 if (!SS) 387 return; 388 389 assertStreamStateOpened(SS); 390 391 if (SS->isNoError()) 392 return; 393 394 State = State->set<StreamMap>(StreamSym, StreamState::getOpened()); 395 C.addTransition(State); 396 } 397 398 template <bool (StreamState::*IsOfError)() const> 399 void StreamChecker::evalFeofFerror(const FnDescription *Desc, 400 const CallEvent &Call, 401 CheckerContext &C) const { 402 ProgramStateRef State = C.getState(); 403 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 404 if (!StreamSym) 405 return; 406 407 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 408 if (!CE) 409 return; 410 411 const StreamState *SS = State->get<StreamMap>(StreamSym); 412 // Ignore the call if the stream is not tracked. 413 if (!SS) 414 return; 415 416 assertStreamStateOpened(SS); 417 418 if ((SS->*IsOfError)()) { 419 // Function returns nonzero. 420 DefinedSVal RetVal = makeRetVal(C, CE); 421 State = State->BindExpr(CE, C.getLocationContext(), RetVal); 422 State = State->assume(RetVal, true); 423 assert(State && "Assumption on new value should not fail."); 424 } else { 425 // Return zero. 426 State = State->BindExpr(CE, C.getLocationContext(), 427 C.getSValBuilder().makeIntVal(0, false)); 428 } 429 C.addTransition(State); 430 } 431 432 void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, 433 CheckerContext &C) const { 434 ProgramStateRef State = C.getState(); 435 SVal StreamVal = getStreamArg(Desc, Call); 436 State = ensureStreamNonNull(StreamVal, C, State); 437 if (!State) 438 return; 439 State = ensureStreamOpened(StreamVal, C, State); 440 if (!State) 441 return; 442 443 C.addTransition(State); 444 } 445 446 template <StreamState (*GetState)()> 447 void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, 448 const CallEvent &Call, 449 CheckerContext &C) const { 450 ProgramStateRef State = C.getState(); 451 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 452 assert(StreamSym && "Operation not permitted on non-symbolic stream value."); 453 State = State->set<StreamMap>(StreamSym, (*GetState)()); 454 C.addTransition(State); 455 } 456 457 ProgramStateRef 458 StreamChecker::ensureStreamNonNull(SVal StreamVal, CheckerContext &C, 459 ProgramStateRef State) const { 460 auto Stream = StreamVal.getAs<DefinedSVal>(); 461 if (!Stream) 462 return State; 463 464 ConstraintManager &CM = C.getConstraintManager(); 465 466 ProgramStateRef StateNotNull, StateNull; 467 std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream); 468 469 if (!StateNotNull && StateNull) { 470 if (ExplodedNode *N = C.generateErrorNode(StateNull)) { 471 if (!BT_nullfp) 472 BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer", 473 "Stream pointer might be NULL.")); 474 C.emitReport(std::make_unique<PathSensitiveBugReport>( 475 *BT_nullfp, BT_nullfp->getDescription(), N)); 476 } 477 return nullptr; 478 } 479 480 return StateNotNull; 481 } 482 483 ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, 484 CheckerContext &C, 485 ProgramStateRef State) const { 486 SymbolRef Sym = StreamVal.getAsSymbol(); 487 if (!Sym) 488 return State; 489 490 const StreamState *SS = State->get<StreamMap>(Sym); 491 if (!SS) 492 return State; 493 494 if (SS->isClosed()) { 495 // Using a stream pointer after 'fclose' causes undefined behavior 496 // according to cppreference.com . 497 ExplodedNode *N = C.generateErrorNode(); 498 if (N) { 499 if (!BT_UseAfterClose) 500 BT_UseAfterClose.reset(new BuiltinBug(this, "Closed stream", 501 "Stream might be already closed. " 502 "Causes undefined behaviour.")); 503 C.emitReport(std::make_unique<PathSensitiveBugReport>( 504 *BT_UseAfterClose, BT_UseAfterClose->getDescription(), N)); 505 return nullptr; 506 } 507 508 return State; 509 } 510 511 if (SS->isOpenFailed()) { 512 // Using a stream that has failed to open is likely to cause problems. 513 // This should usually not occur because stream pointer is NULL. 514 // But freopen can cause a state when stream pointer remains non-null but 515 // failed to open. 516 ExplodedNode *N = C.generateErrorNode(); 517 if (N) { 518 if (!BT_UseAfterOpenFailed) 519 BT_UseAfterOpenFailed.reset( 520 new BuiltinBug(this, "Invalid stream", 521 "Stream might be invalid after " 522 "(re-)opening it has failed. " 523 "Can cause undefined behaviour.")); 524 C.emitReport(std::make_unique<PathSensitiveBugReport>( 525 *BT_UseAfterOpenFailed, BT_UseAfterOpenFailed->getDescription(), N)); 526 return nullptr; 527 } 528 return State; 529 } 530 531 return State; 532 } 533 534 ProgramStateRef 535 StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 536 ProgramStateRef State) const { 537 Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>(); 538 if (!CI) 539 return State; 540 541 int64_t X = CI->getValue().getSExtValue(); 542 if (X >= 0 && X <= 2) 543 return State; 544 545 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 546 if (!BT_illegalwhence) 547 BT_illegalwhence.reset( 548 new BuiltinBug(this, "Illegal whence argument", 549 "The whence argument to fseek() should be " 550 "SEEK_SET, SEEK_END, or SEEK_CUR.")); 551 C.emitReport(std::make_unique<PathSensitiveBugReport>( 552 *BT_illegalwhence, BT_illegalwhence->getDescription(), N)); 553 return nullptr; 554 } 555 556 return State; 557 } 558 559 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 560 CheckerContext &C) const { 561 ProgramStateRef State = C.getState(); 562 563 // TODO: Clean up the state. 564 const StreamMapTy &Map = State->get<StreamMap>(); 565 for (const auto &I : Map) { 566 SymbolRef Sym = I.first; 567 const StreamState &SS = I.second; 568 if (!SymReaper.isDead(Sym) || !SS.isOpened()) 569 continue; 570 571 ExplodedNode *N = C.generateErrorNode(); 572 if (!N) 573 continue; 574 575 if (!BT_ResourceLeak) 576 BT_ResourceLeak.reset( 577 new BuiltinBug(this, "Resource Leak", 578 "Opened File never closed. Potential Resource leak.")); 579 C.emitReport(std::make_unique<PathSensitiveBugReport>( 580 *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N)); 581 } 582 } 583 584 void ento::registerStreamChecker(CheckerManager &Mgr) { 585 Mgr.registerChecker<StreamChecker>(); 586 } 587 588 bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) { 589 return true; 590 } 591 592 void ento::registerStreamTesterChecker(CheckerManager &Mgr) { 593 auto *Checker = Mgr.getChecker<StreamChecker>(); 594 Checker->TestMode = true; 595 } 596 597 bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) { 598 return true; 599 } 600 601