698 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This checker checks if the handle of Fuchsia is properly used according to10// following rules.11// - If a handle is acquired, it should be released before execution12// 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 purposes15// such as I/O.16//17// In this checker, each tracked handle is associated with a state. When the18// handle variable is passed to different function calls or syscalls, its state19// changes. The state changes can be generally represented by following ASCII20// 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 value38// +----------+--+ release_func | +------+ in function call39// | | | or by reference in40// | | | use_func call41// unowned | +----v-----+ | +-----------+42// acquire_func | | Double | +-----> Use after |43// succeeded | | released | | released |44// | | (REPORT) | | (REPORT) |45// +---------------+ +----------+ +-----------+46// | Allocated |47// | Unowned | release_func48// | +---------+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 it62// 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 failed65// 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 Fuchsia69// is large, we adopt the annotation attributes to descript syscalls'70// operations(acquire/release/use) on handles instead of hardcoding71// everything in the checker.72//73// We use following annotation attributes for handle related syscalls or74// functions:75// 1. __attribute__((acquire_handle("Fuchsia"))) |handle will be acquired76// 2. __attribute__((release_handle("Fuchsia"))) |handle will be released77// 3. __attribute__((use_handle("Fuchsia"))) |handle will not transit to78// 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' and86// '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 106using namespace clang;107using namespace ento;108 109namespace {110 111static const StringRef HandleTypeName = "zx_handle_t";112static const StringRef ErrorTypeName = "zx_status_t";113 114class HandleState {115private:116 enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K;117 SymbolRef ErrorSym;118 HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}119 120public: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 178template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {179 return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";180}181 182template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) {183 return D->hasAttr<Attr>() &&184 D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned";185}186 187class FuchsiaHandleChecker188 : 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 199public: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 namespace230 231REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)232 233static 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 for237 // 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 255namespace {256class FuchsiaHandleSymbolVisitor final : public SymbolVisitor {257public: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 267private:268 SmallVector<SymbolRef, 1024> Symbols;269};270} // end anonymous namespace271 272/// Returns the symbols extracted from the argument or empty vector if it cannot273/// be found. It is unlikely to have over 1024 symbols in one argument.274static SmallVector<SymbolRef, 1024>275getFuchsiaHandleSymbols(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 the283 // 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 317void 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 by323 // 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 361void 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 } else391 return "";392 });393 State =394 State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));395 } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) {396 // Function returns an unowned handle397 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 } else407 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 " << ParamDiagIdx438 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";439 return SBuf;440 } else441 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 " << ParamDiagIdx452 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";453 return SBuf;454 } else455 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 " << ParamDiagIdx466 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";467 return SBuf;468 } else469 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 be477 // true) and there is no annotation on the handle, we assume the handle478 // 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 503void 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 later511 // than the handle symbol we might produce spurious leak warnings (in case512 // we find out later from the status code that the handle allocation failed513 // 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 functions530// 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 one532// 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 where534// 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 constant537// zero will be used instead of the symbol. We also do not need to release538// an invalid handle, so we remove the corresponding symbol from the state.539ProgramStateRef 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 569ProgramStateRef 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. See596 // 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 610ExplodedNode *611FuchsiaHandleChecker::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 621void 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 629void 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 637void 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 645void 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 675void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {676 mgr.registerChecker<FuchsiaHandleChecker>();677}678 679bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {680 return true;681}682 683void 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