1 //=== FuchsiaHandleChecker.cpp - Find handle leaks/double closes -*- 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 checker checks if the handle of Fuchsia is properly used according to 10 // following rules. 11 // - If a handle is acquired, it should be released before execution 12 // ends. 13 // - If a handle is released, it should not be released again. 14 // - If a handle is released, it should not be used for other purposes 15 // such as I/O. 16 // 17 // In this checker, each tracked handle is associated with a state. When the 18 // handle variable is passed to different function calls or syscalls, its state 19 // changes. The state changes can be generally represented by following ASCII 20 // Art: 21 // 22 // 23 // +-------------+ +------------+ 24 // acquire_func succeeded | | Escape | | 25 // +-----------------> Allocated +---------> Escaped <--+ 26 // | | | | | | 27 // | +-----+------++ +------------+ | 28 // | | | | 29 // acquire_func | release_func | +--+ | 30 // failed | | | handle +--------+ | 31 // +---------+ | | | dies | | | 32 // | | | +----v-----+ +---------> Leaked | | 33 // | | | | | |(REPORT)| | 34 // | +----------+--+ | Released | Escape +--------+ | 35 // | | | | +---------------------------+ 36 // +--> Not tracked | +----+---+-+ 37 // | | | | As argument by value 38 // +----------+--+ release_func | +------+ in function call 39 // | | | or by reference in 40 // | | | use_func call 41 // unowned | +----v-----+ | +-----------+ 42 // acquire_func | | Double | +-----> Use after | 43 // succeeded | | released | | released | 44 // | | (REPORT) | | (REPORT) | 45 // +---------------+ +----------+ +-----------+ 46 // | Allocated | 47 // | Unowned | release_func 48 // | +---------+ 49 // +---------------+ | 50 // | 51 // +-----v----------+ 52 // | Release of | 53 // | unowned handle | 54 // | (REPORT) | 55 // +----------------+ 56 // 57 // acquire_func represents the functions or syscalls that may acquire a handle. 58 // release_func represents the functions or syscalls that may release a handle. 59 // use_func represents the functions or syscall that requires an open handle. 60 // 61 // If a tracked handle dies in "Released" or "Not Tracked" state, we assume it 62 // is properly used. Otherwise a bug and will be reported. 63 // 64 // Note that, the analyzer does not always know for sure if a function failed 65 // or succeeded. In those cases we use the state MaybeAllocated. 66 // Thus, the diagram above captures the intent, not implementation details. 67 // 68 // Due to the fact that the number of handle related syscalls in Fuchsia 69 // is large, we adopt the annotation attributes to descript syscalls' 70 // operations(acquire/release/use) on handles instead of hardcoding 71 // everything in the checker. 72 // 73 // We use following annotation attributes for handle related syscalls or 74 // functions: 75 // 1. __attribute__((acquire_handle("Fuchsia"))) |handle will be acquired 76 // 2. __attribute__((release_handle("Fuchsia"))) |handle will be released 77 // 3. __attribute__((use_handle("Fuchsia"))) |handle will not transit to 78 // escaped state, it also needs to be open. 79 // 80 // For example, an annotated syscall: 81 // zx_status_t zx_channel_create( 82 // uint32_t options, 83 // zx_handle_t* out0 __attribute__((acquire_handle("Fuchsia"))) , 84 // zx_handle_t* out1 __attribute__((acquire_handle("Fuchsia")))); 85 // denotes a syscall which will acquire two handles and save them to 'out0' and 86 // 'out1' when succeeded. 87 // 88 //===----------------------------------------------------------------------===// 89 90 #include "clang/AST/Attr.h" 91 #include "clang/AST/Decl.h" 92 #include "clang/AST/Type.h" 93 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 94 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 95 #include "clang/StaticAnalyzer/Core/Checker.h" 96 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 97 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 98 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 99 #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" 100 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 101 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 102 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" 103 #include "llvm/ADT/StringExtras.h" 104 #include <optional> 105 106 using namespace clang; 107 using namespace ento; 108 109 namespace { 110 111 static const StringRef HandleTypeName = "zx_handle_t"; 112 static const StringRef ErrorTypeName = "zx_status_t"; 113 114 class HandleState { 115 private: 116 enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K; 117 SymbolRef ErrorSym; 118 HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {} 119 120 public: 121 bool operator==(const HandleState &Other) const { 122 return K == Other.K && ErrorSym == Other.ErrorSym; 123 } 124 bool isAllocated() const { return K == Kind::Allocated; } 125 bool maybeAllocated() const { return K == Kind::MaybeAllocated; } 126 bool isReleased() const { return K == Kind::Released; } 127 bool isEscaped() const { return K == Kind::Escaped; } 128 bool isUnowned() const { return K == Kind::Unowned; } 129 130 static HandleState getMaybeAllocated(SymbolRef ErrorSym) { 131 return HandleState(Kind::MaybeAllocated, ErrorSym); 132 } 133 static HandleState getAllocated(ProgramStateRef State, HandleState S) { 134 assert(S.maybeAllocated()); 135 assert(State->getConstraintManager() 136 .isNull(State, S.getErrorSym()) 137 .isConstrained()); 138 return HandleState(Kind::Allocated, nullptr); 139 } 140 static HandleState getReleased() { 141 return HandleState(Kind::Released, nullptr); 142 } 143 static HandleState getEscaped() { 144 return HandleState(Kind::Escaped, nullptr); 145 } 146 static HandleState getUnowned() { 147 return HandleState(Kind::Unowned, nullptr); 148 } 149 150 SymbolRef getErrorSym() const { return ErrorSym; } 151 152 void Profile(llvm::FoldingSetNodeID &ID) const { 153 ID.AddInteger(static_cast<int>(K)); 154 ID.AddPointer(ErrorSym); 155 } 156 157 LLVM_DUMP_METHOD void dump(raw_ostream &OS) const { 158 switch (K) { 159 #define CASE(ID) \ 160 case ID: \ 161 OS << #ID; \ 162 break; 163 CASE(Kind::MaybeAllocated) 164 CASE(Kind::Allocated) 165 CASE(Kind::Released) 166 CASE(Kind::Escaped) 167 CASE(Kind::Unowned) 168 } 169 if (ErrorSym) { 170 OS << " ErrorSym: "; 171 ErrorSym->dumpToStream(OS); 172 } 173 } 174 175 LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); } 176 }; 177 178 template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) { 179 return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia"; 180 } 181 182 template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) { 183 return D->hasAttr<Attr>() && 184 D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned"; 185 } 186 187 class FuchsiaHandleChecker 188 : public Checker<check::PostCall, check::PreCall, check::DeadSymbols, 189 check::PointerEscape, eval::Assume> { 190 BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error", 191 /*SuppressOnSink=*/true}; 192 BugType DoubleReleaseBugType{this, "Fuchsia handle double release", 193 "Fuchsia Handle Error"}; 194 BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release", 195 "Fuchsia Handle Error"}; 196 BugType ReleaseUnownedBugType{ 197 this, "Fuchsia handle release of unowned handle", "Fuchsia Handle Error"}; 198 199 public: 200 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 201 void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 202 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 203 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, 204 bool Assumption) const; 205 ProgramStateRef checkPointerEscape(ProgramStateRef State, 206 const InvalidatedSymbols &Escaped, 207 const CallEvent *Call, 208 PointerEscapeKind Kind) const; 209 210 ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles, 211 CheckerContext &C, ExplodedNode *Pred) const; 212 213 void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range, 214 CheckerContext &C) const; 215 216 void reportUnownedRelease(SymbolRef HandleSym, const SourceRange &Range, 217 CheckerContext &C) const; 218 219 void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range, 220 CheckerContext &C) const; 221 222 void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C, 223 const SourceRange *Range, const BugType &Type, 224 StringRef Msg) const; 225 226 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 227 const char *Sep) const override; 228 }; 229 } // end anonymous namespace 230 231 REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState) 232 233 static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym, 234 CheckerContext &Ctx) { 235 ProgramStateRef State = N->getState(); 236 // When bug type is handle leak, exploded node N does not have state info for 237 // leaking handle. Get the predecessor of N instead. 238 if (!State->get<HStateMap>(Sym)) 239 N = N->getFirstPred(); 240 241 const ExplodedNode *Pred = N; 242 while (N) { 243 State = N->getState(); 244 if (!State->get<HStateMap>(Sym)) { 245 const HandleState *HState = Pred->getState()->get<HStateMap>(Sym); 246 if (HState && (HState->isAllocated() || HState->maybeAllocated())) 247 return N; 248 } 249 Pred = N; 250 N = N->getFirstPred(); 251 } 252 return nullptr; 253 } 254 255 namespace { 256 class FuchsiaHandleSymbolVisitor final : public SymbolVisitor { 257 public: 258 bool VisitSymbol(SymbolRef S) override { 259 if (const auto *HandleType = S->getType()->getAs<TypedefType>()) 260 if (HandleType->getDecl()->getName() == HandleTypeName) 261 Symbols.push_back(S); 262 return true; 263 } 264 265 SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; } 266 267 private: 268 SmallVector<SymbolRef, 1024> Symbols; 269 }; 270 } // end anonymous namespace 271 272 /// Returns the symbols extracted from the argument or empty vector if it cannot 273 /// be found. It is unlikely to have over 1024 symbols in one argument. 274 static SmallVector<SymbolRef, 1024> 275 getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) { 276 int PtrToHandleLevel = 0; 277 while (QT->isAnyPointerType() || QT->isReferenceType()) { 278 ++PtrToHandleLevel; 279 QT = QT->getPointeeType(); 280 } 281 if (QT->isStructureType()) { 282 // If we see a structure, see if there is any handle referenced by the 283 // structure. 284 FuchsiaHandleSymbolVisitor Visitor; 285 State->scanReachableSymbols(Arg, Visitor); 286 return Visitor.GetSymbols(); 287 } 288 if (const auto *HandleType = QT->getAs<TypedefType>()) { 289 if (HandleType->getDecl()->getName() != HandleTypeName) 290 return {}; 291 if (PtrToHandleLevel > 1) 292 // Not supported yet. 293 return {}; 294 295 if (PtrToHandleLevel == 0) { 296 SymbolRef Sym = Arg.getAsSymbol(); 297 if (Sym) { 298 return {Sym}; 299 } else { 300 return {}; 301 } 302 } else { 303 assert(PtrToHandleLevel == 1); 304 if (std::optional<Loc> ArgLoc = Arg.getAs<Loc>()) { 305 SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol(); 306 if (Sym) { 307 return {Sym}; 308 } else { 309 return {}; 310 } 311 } 312 } 313 } 314 return {}; 315 } 316 317 void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call, 318 CheckerContext &C) const { 319 ProgramStateRef State = C.getState(); 320 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 321 if (!FuncDecl) { 322 // Unknown call, escape by value handles. They are not covered by 323 // PointerEscape callback. 324 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 325 if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol()) 326 State = State->set<HStateMap>(Handle, HandleState::getEscaped()); 327 } 328 C.addTransition(State); 329 return; 330 } 331 332 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 333 if (Arg >= FuncDecl->getNumParams()) 334 break; 335 const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 336 SmallVector<SymbolRef, 1024> Handles = 337 getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State); 338 339 // Handled in checkPostCall. 340 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) || 341 hasFuchsiaAttr<AcquireHandleAttr>(PVD)) 342 continue; 343 344 for (SymbolRef Handle : Handles) { 345 const HandleState *HState = State->get<HStateMap>(Handle); 346 if (!HState || HState->isEscaped()) 347 continue; 348 349 if (hasFuchsiaAttr<UseHandleAttr>(PVD) || 350 PVD->getType()->isIntegerType()) { 351 if (HState->isReleased()) { 352 reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C); 353 return; 354 } 355 } 356 } 357 } 358 C.addTransition(State); 359 } 360 361 void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, 362 CheckerContext &C) const { 363 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 364 if (!FuncDecl) 365 return; 366 367 // If we analyzed the function body, then ignore the annotations. 368 if (C.wasInlined) 369 return; 370 371 ProgramStateRef State = C.getState(); 372 373 std::vector<std::function<std::string(BugReport & BR)>> Notes; 374 SymbolRef ResultSymbol = nullptr; 375 if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>()) 376 if (TypeDefTy->getDecl()->getName() == ErrorTypeName) 377 ResultSymbol = Call.getReturnValue().getAsSymbol(); 378 379 // Function returns an open handle. 380 if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) { 381 SymbolRef RetSym = Call.getReturnValue().getAsSymbol(); 382 Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string { 383 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 384 if (PathBR->getInterestingnessKind(RetSym)) { 385 std::string SBuf; 386 llvm::raw_string_ostream OS(SBuf); 387 OS << "Function '" << FuncDecl->getDeclName() 388 << "' returns an open handle"; 389 return SBuf; 390 } else 391 return ""; 392 }); 393 State = 394 State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr)); 395 } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) { 396 // Function returns an unowned handle 397 SymbolRef RetSym = Call.getReturnValue().getAsSymbol(); 398 Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string { 399 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 400 if (PathBR->getInterestingnessKind(RetSym)) { 401 std::string SBuf; 402 llvm::raw_string_ostream OS(SBuf); 403 OS << "Function '" << FuncDecl->getDeclName() 404 << "' returns an unowned handle"; 405 return SBuf; 406 } else 407 return ""; 408 }); 409 State = State->set<HStateMap>(RetSym, HandleState::getUnowned()); 410 } 411 412 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 413 if (Arg >= FuncDecl->getNumParams()) 414 break; 415 const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 416 unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1; 417 SmallVector<SymbolRef, 1024> Handles = 418 getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State); 419 420 for (SymbolRef Handle : Handles) { 421 const HandleState *HState = State->get<HStateMap>(Handle); 422 if (HState && HState->isEscaped()) 423 continue; 424 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) { 425 if (HState && HState->isReleased()) { 426 reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C); 427 return; 428 } else if (HState && HState->isUnowned()) { 429 reportUnownedRelease(Handle, Call.getArgSourceRange(Arg), C); 430 return; 431 } else { 432 Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { 433 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 434 if (PathBR->getInterestingnessKind(Handle)) { 435 std::string SBuf; 436 llvm::raw_string_ostream OS(SBuf); 437 OS << "Handle released through " << ParamDiagIdx 438 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; 439 return SBuf; 440 } else 441 return ""; 442 }); 443 State = State->set<HStateMap>(Handle, HandleState::getReleased()); 444 } 445 } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) { 446 Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { 447 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 448 if (PathBR->getInterestingnessKind(Handle)) { 449 std::string SBuf; 450 llvm::raw_string_ostream OS(SBuf); 451 OS << "Handle allocated through " << ParamDiagIdx 452 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; 453 return SBuf; 454 } else 455 return ""; 456 }); 457 State = State->set<HStateMap>( 458 Handle, HandleState::getMaybeAllocated(ResultSymbol)); 459 } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)) { 460 Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { 461 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 462 if (PathBR->getInterestingnessKind(Handle)) { 463 std::string SBuf; 464 llvm::raw_string_ostream OS(SBuf); 465 OS << "Unowned handle allocated through " << ParamDiagIdx 466 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; 467 return SBuf; 468 } else 469 return ""; 470 }); 471 State = State->set<HStateMap>(Handle, HandleState::getUnowned()); 472 } else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) && 473 PVD->getType()->isIntegerType()) { 474 // Working around integer by-value escapes. 475 // The by-value escape would not be captured in checkPointerEscape. 476 // If the function was not analyzed (otherwise wasInlined should be 477 // true) and there is no annotation on the handle, we assume the handle 478 // is escaped. 479 State = State->set<HStateMap>(Handle, HandleState::getEscaped()); 480 } 481 } 482 } 483 const NoteTag *T = nullptr; 484 if (!Notes.empty()) { 485 T = C.getNoteTag([this, Notes{std::move(Notes)}]( 486 PathSensitiveBugReport &BR) -> std::string { 487 if (&BR.getBugType() != &UseAfterReleaseBugType && 488 &BR.getBugType() != &LeakBugType && 489 &BR.getBugType() != &DoubleReleaseBugType && 490 &BR.getBugType() != &ReleaseUnownedBugType) 491 return ""; 492 for (auto &Note : Notes) { 493 std::string Text = Note(BR); 494 if (!Text.empty()) 495 return Text; 496 } 497 return ""; 498 }); 499 } 500 C.addTransition(State, T); 501 } 502 503 void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper, 504 CheckerContext &C) const { 505 ProgramStateRef State = C.getState(); 506 SmallVector<SymbolRef, 2> LeakedSyms; 507 HStateMapTy TrackedHandles = State->get<HStateMap>(); 508 for (auto &CurItem : TrackedHandles) { 509 SymbolRef ErrorSym = CurItem.second.getErrorSym(); 510 // Keeping zombie handle symbols. In case the error symbol is dying later 511 // than the handle symbol we might produce spurious leak warnings (in case 512 // we find out later from the status code that the handle allocation failed 513 // in the first place). 514 if (!SymReaper.isDead(CurItem.first) || 515 (ErrorSym && !SymReaper.isDead(ErrorSym))) 516 continue; 517 if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated()) 518 LeakedSyms.push_back(CurItem.first); 519 State = State->remove<HStateMap>(CurItem.first); 520 } 521 522 ExplodedNode *N = C.getPredecessor(); 523 if (!LeakedSyms.empty()) 524 N = reportLeaks(LeakedSyms, C, N); 525 526 C.addTransition(State, N); 527 } 528 529 // Acquiring a handle is not always successful. In Fuchsia most functions 530 // return a status code that determines the status of the handle. 531 // When we split the path based on this status code we know that on one 532 // path we do have the handle and on the other path the acquire failed. 533 // This method helps avoiding false positive leak warnings on paths where 534 // the function failed. 535 // Moreover, when a handle is known to be zero (the invalid handle), 536 // we no longer can follow the symbol on the path, becaue the constant 537 // zero will be used instead of the symbol. We also do not need to release 538 // an invalid handle, so we remove the corresponding symbol from the state. 539 ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State, 540 SVal Cond, 541 bool Assumption) const { 542 // TODO: add notes about successes/fails for APIs. 543 ConstraintManager &Cmr = State->getConstraintManager(); 544 HStateMapTy TrackedHandles = State->get<HStateMap>(); 545 for (auto &CurItem : TrackedHandles) { 546 ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first); 547 if (HandleVal.isConstrainedTrue()) { 548 // The handle is invalid. We can no longer follow the symbol on this path. 549 State = State->remove<HStateMap>(CurItem.first); 550 } 551 SymbolRef ErrorSym = CurItem.second.getErrorSym(); 552 if (!ErrorSym) 553 continue; 554 ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym); 555 if (ErrorVal.isConstrainedTrue()) { 556 // Allocation succeeded. 557 if (CurItem.second.maybeAllocated()) 558 State = State->set<HStateMap>( 559 CurItem.first, HandleState::getAllocated(State, CurItem.second)); 560 } else if (ErrorVal.isConstrainedFalse()) { 561 // Allocation failed. 562 if (CurItem.second.maybeAllocated()) 563 State = State->remove<HStateMap>(CurItem.first); 564 } 565 } 566 return State; 567 } 568 569 ProgramStateRef FuchsiaHandleChecker::checkPointerEscape( 570 ProgramStateRef State, const InvalidatedSymbols &Escaped, 571 const CallEvent *Call, PointerEscapeKind Kind) const { 572 const FunctionDecl *FuncDecl = 573 Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr; 574 575 llvm::DenseSet<SymbolRef> UnEscaped; 576 // Not all calls should escape our symbols. 577 if (FuncDecl && 578 (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall || 579 Kind == PSK_EscapeOutParameters)) { 580 for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) { 581 if (Arg >= FuncDecl->getNumParams()) 582 break; 583 const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 584 SmallVector<SymbolRef, 1024> Handles = 585 getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Arg), State); 586 for (SymbolRef Handle : Handles) { 587 if (hasFuchsiaAttr<UseHandleAttr>(PVD) || 588 hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) { 589 UnEscaped.insert(Handle); 590 } 591 } 592 } 593 } 594 595 // For out params, we have to deal with derived symbols. See 596 // MacOSKeychainAPIChecker for details. 597 for (auto I : State->get<HStateMap>()) { 598 if (Escaped.count(I.first) && !UnEscaped.count(I.first)) 599 State = State->set<HStateMap>(I.first, HandleState::getEscaped()); 600 if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) { 601 auto ParentSym = SD->getParentSymbol(); 602 if (Escaped.count(ParentSym)) 603 State = State->set<HStateMap>(I.first, HandleState::getEscaped()); 604 } 605 } 606 607 return State; 608 } 609 610 ExplodedNode * 611 FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles, 612 CheckerContext &C, ExplodedNode *Pred) const { 613 ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred); 614 for (SymbolRef LeakedHandle : LeakedHandles) { 615 reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType, 616 "Potential leak of handle"); 617 } 618 return ErrNode; 619 } 620 621 void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym, 622 const SourceRange &Range, 623 CheckerContext &C) const { 624 ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); 625 reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType, 626 "Releasing a previously released handle"); 627 } 628 629 void FuchsiaHandleChecker::reportUnownedRelease(SymbolRef HandleSym, 630 const SourceRange &Range, 631 CheckerContext &C) const { 632 ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); 633 reportBug(HandleSym, ErrNode, C, &Range, ReleaseUnownedBugType, 634 "Releasing an unowned handle"); 635 } 636 637 void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym, 638 const SourceRange &Range, 639 CheckerContext &C) const { 640 ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); 641 reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType, 642 "Using a previously released handle"); 643 } 644 645 void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, 646 CheckerContext &C, 647 const SourceRange *Range, 648 const BugType &Type, StringRef Msg) const { 649 if (!ErrorNode) 650 return; 651 652 std::unique_ptr<PathSensitiveBugReport> R; 653 if (Type.isSuppressOnSink()) { 654 const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C); 655 if (AcquireNode) { 656 const Stmt *S = AcquireNode->getStmtForDiagnostics(); 657 assert(S && "Statement cannot be null."); 658 PathDiagnosticLocation LocUsedForUniqueing = 659 PathDiagnosticLocation::createBegin( 660 S, C.getSourceManager(), AcquireNode->getLocationContext()); 661 662 R = std::make_unique<PathSensitiveBugReport>( 663 Type, Msg, ErrorNode, LocUsedForUniqueing, 664 AcquireNode->getLocationContext()->getDecl()); 665 } 666 } 667 if (!R) 668 R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode); 669 if (Range) 670 R->addRange(*Range); 671 R->markInteresting(Sym); 672 C.emitReport(std::move(R)); 673 } 674 675 void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) { 676 mgr.registerChecker<FuchsiaHandleChecker>(); 677 } 678 679 bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) { 680 return true; 681 } 682 683 void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State, 684 const char *NL, const char *Sep) const { 685 686 HStateMapTy StateMap = State->get<HStateMap>(); 687 688 if (!StateMap.isEmpty()) { 689 Out << Sep << "FuchsiaHandleChecker :" << NL; 690 for (const auto &[Sym, HandleState] : StateMap) { 691 Sym->dumpToStream(Out); 692 Out << " : "; 693 HandleState.dump(Out); 694 Out << NL; 695 } 696 } 697 } 698