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