brintos

brintos / llvm-project-archived public Read only

0
0
Text · 27.4 KiB · 079bc61 Raw
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