Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
35269 views
//=== FuchsiaHandleChecker.cpp - Find handle leaks/double closes -*- C++ -*--=//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This checker checks if the handle of Fuchsia is properly used according to9// following rules.10// - If a handle is acquired, it should be released before execution11// ends.12// - If a handle is released, it should not be released again.13// - If a handle is released, it should not be used for other purposes14// such as I/O.15//16// In this checker, each tracked handle is associated with a state. When the17// handle variable is passed to different function calls or syscalls, its state18// changes. The state changes can be generally represented by following ASCII19// Art:20//21//22// +-------------+ +------------+23// acquire_func succeeded | | Escape | |24// +-----------------> Allocated +---------> Escaped <--+25// | | | | | |26// | +-----+------++ +------------+ |27// | | | |28// acquire_func | release_func | +--+ |29// failed | | | handle +--------+ |30// +---------+ | | | dies | | |31// | | | +----v-----+ +---------> Leaked | |32// | | | | | |(REPORT)| |33// | +----------+--+ | Released | Escape +--------+ |34// | | | | +---------------------------+35// +--> Not tracked | +----+---+-+36// | | | | As argument by value37// +----------+--+ release_func | +------+ in function call38// | | | or by reference in39// | | | use_func call40// unowned | +----v-----+ | +-----------+41// acquire_func | | Double | +-----> Use after |42// succeeded | | released | | released |43// | | (REPORT) | | (REPORT) |44// +---------------+ +----------+ +-----------+45// | Allocated |46// | Unowned | release_func47// | +---------+48// +---------------+ |49// |50// +-----v----------+51// | Release of |52// | unowned handle |53// | (REPORT) |54// +----------------+55//56// acquire_func represents the functions or syscalls that may acquire a handle.57// release_func represents the functions or syscalls that may release a handle.58// use_func represents the functions or syscall that requires an open handle.59//60// If a tracked handle dies in "Released" or "Not Tracked" state, we assume it61// is properly used. Otherwise a bug and will be reported.62//63// Note that, the analyzer does not always know for sure if a function failed64// or succeeded. In those cases we use the state MaybeAllocated.65// Thus, the diagram above captures the intent, not implementation details.66//67// Due to the fact that the number of handle related syscalls in Fuchsia68// is large, we adopt the annotation attributes to descript syscalls'69// operations(acquire/release/use) on handles instead of hardcoding70// everything in the checker.71//72// We use following annotation attributes for handle related syscalls or73// functions:74// 1. __attribute__((acquire_handle("Fuchsia"))) |handle will be acquired75// 2. __attribute__((release_handle("Fuchsia"))) |handle will be released76// 3. __attribute__((use_handle("Fuchsia"))) |handle will not transit to77// escaped state, it also needs to be open.78//79// For example, an annotated syscall:80// zx_status_t zx_channel_create(81// uint32_t options,82// zx_handle_t* out0 __attribute__((acquire_handle("Fuchsia"))) ,83// zx_handle_t* out1 __attribute__((acquire_handle("Fuchsia"))));84// denotes a syscall which will acquire two handles and save them to 'out0' and85// 'out1' when succeeded.86//87//===----------------------------------------------------------------------===//8889#include "clang/AST/Attr.h"90#include "clang/AST/Decl.h"91#include "clang/AST/Type.h"92#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"93#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"94#include "clang/StaticAnalyzer/Core/Checker.h"95#include "clang/StaticAnalyzer/Core/CheckerManager.h"96#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"97#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"98#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"99#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"100#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"101#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"102#include "llvm/ADT/StringExtras.h"103#include <optional>104105using namespace clang;106using namespace ento;107108namespace {109110static const StringRef HandleTypeName = "zx_handle_t";111static const StringRef ErrorTypeName = "zx_status_t";112113class HandleState {114private:115enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K;116SymbolRef ErrorSym;117HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}118119public:120bool operator==(const HandleState &Other) const {121return K == Other.K && ErrorSym == Other.ErrorSym;122}123bool isAllocated() const { return K == Kind::Allocated; }124bool maybeAllocated() const { return K == Kind::MaybeAllocated; }125bool isReleased() const { return K == Kind::Released; }126bool isEscaped() const { return K == Kind::Escaped; }127bool isUnowned() const { return K == Kind::Unowned; }128129static HandleState getMaybeAllocated(SymbolRef ErrorSym) {130return HandleState(Kind::MaybeAllocated, ErrorSym);131}132static HandleState getAllocated(ProgramStateRef State, HandleState S) {133assert(S.maybeAllocated());134assert(State->getConstraintManager()135.isNull(State, S.getErrorSym())136.isConstrained());137return HandleState(Kind::Allocated, nullptr);138}139static HandleState getReleased() {140return HandleState(Kind::Released, nullptr);141}142static HandleState getEscaped() {143return HandleState(Kind::Escaped, nullptr);144}145static HandleState getUnowned() {146return HandleState(Kind::Unowned, nullptr);147}148149SymbolRef getErrorSym() const { return ErrorSym; }150151void Profile(llvm::FoldingSetNodeID &ID) const {152ID.AddInteger(static_cast<int>(K));153ID.AddPointer(ErrorSym);154}155156LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {157switch (K) {158#define CASE(ID) \159case ID: \160OS << #ID; \161break;162CASE(Kind::MaybeAllocated)163CASE(Kind::Allocated)164CASE(Kind::Released)165CASE(Kind::Escaped)166CASE(Kind::Unowned)167}168if (ErrorSym) {169OS << " ErrorSym: ";170ErrorSym->dumpToStream(OS);171}172}173174LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }175};176177template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {178return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";179}180181template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) {182return D->hasAttr<Attr>() &&183D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned";184}185186class FuchsiaHandleChecker187: public Checker<check::PostCall, check::PreCall, check::DeadSymbols,188check::PointerEscape, eval::Assume> {189BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error",190/*SuppressOnSink=*/true};191BugType DoubleReleaseBugType{this, "Fuchsia handle double release",192"Fuchsia Handle Error"};193BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release",194"Fuchsia Handle Error"};195BugType ReleaseUnownedBugType{196this, "Fuchsia handle release of unowned handle", "Fuchsia Handle Error"};197198public:199void checkPreCall(const CallEvent &Call, CheckerContext &C) const;200void checkPostCall(const CallEvent &Call, CheckerContext &C) const;201void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;202ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,203bool Assumption) const;204ProgramStateRef checkPointerEscape(ProgramStateRef State,205const InvalidatedSymbols &Escaped,206const CallEvent *Call,207PointerEscapeKind Kind) const;208209ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles,210CheckerContext &C, ExplodedNode *Pred) const;211212void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range,213CheckerContext &C) const;214215void reportUnownedRelease(SymbolRef HandleSym, const SourceRange &Range,216CheckerContext &C) const;217218void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range,219CheckerContext &C) const;220221void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,222const SourceRange *Range, const BugType &Type,223StringRef Msg) const;224225void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,226const char *Sep) const override;227};228} // end anonymous namespace229230REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)231232static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym,233CheckerContext &Ctx) {234ProgramStateRef State = N->getState();235// When bug type is handle leak, exploded node N does not have state info for236// leaking handle. Get the predecessor of N instead.237if (!State->get<HStateMap>(Sym))238N = N->getFirstPred();239240const ExplodedNode *Pred = N;241while (N) {242State = N->getState();243if (!State->get<HStateMap>(Sym)) {244const HandleState *HState = Pred->getState()->get<HStateMap>(Sym);245if (HState && (HState->isAllocated() || HState->maybeAllocated()))246return N;247}248Pred = N;249N = N->getFirstPred();250}251return nullptr;252}253254namespace {255class FuchsiaHandleSymbolVisitor final : public SymbolVisitor {256public:257bool VisitSymbol(SymbolRef S) override {258if (const auto *HandleType = S->getType()->getAs<TypedefType>())259if (HandleType->getDecl()->getName() == HandleTypeName)260Symbols.push_back(S);261return true;262}263264SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; }265266private:267SmallVector<SymbolRef, 1024> Symbols;268};269} // end anonymous namespace270271/// Returns the symbols extracted from the argument or empty vector if it cannot272/// be found. It is unlikely to have over 1024 symbols in one argument.273static SmallVector<SymbolRef, 1024>274getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) {275int PtrToHandleLevel = 0;276while (QT->isAnyPointerType() || QT->isReferenceType()) {277++PtrToHandleLevel;278QT = QT->getPointeeType();279}280if (QT->isStructureType()) {281// If we see a structure, see if there is any handle referenced by the282// structure.283FuchsiaHandleSymbolVisitor Visitor;284State->scanReachableSymbols(Arg, Visitor);285return Visitor.GetSymbols();286}287if (const auto *HandleType = QT->getAs<TypedefType>()) {288if (HandleType->getDecl()->getName() != HandleTypeName)289return {};290if (PtrToHandleLevel > 1)291// Not supported yet.292return {};293294if (PtrToHandleLevel == 0) {295SymbolRef Sym = Arg.getAsSymbol();296if (Sym) {297return {Sym};298} else {299return {};300}301} else {302assert(PtrToHandleLevel == 1);303if (std::optional<Loc> ArgLoc = Arg.getAs<Loc>()) {304SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol();305if (Sym) {306return {Sym};307} else {308return {};309}310}311}312}313return {};314}315316void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,317CheckerContext &C) const {318ProgramStateRef State = C.getState();319const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());320if (!FuncDecl) {321// Unknown call, escape by value handles. They are not covered by322// PointerEscape callback.323for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {324if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol())325State = State->set<HStateMap>(Handle, HandleState::getEscaped());326}327C.addTransition(State);328return;329}330331for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {332if (Arg >= FuncDecl->getNumParams())333break;334const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);335SmallVector<SymbolRef, 1024> Handles =336getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);337338// Handled in checkPostCall.339if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||340hasFuchsiaAttr<AcquireHandleAttr>(PVD))341continue;342343for (SymbolRef Handle : Handles) {344const HandleState *HState = State->get<HStateMap>(Handle);345if (!HState || HState->isEscaped())346continue;347348if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||349PVD->getType()->isIntegerType()) {350if (HState->isReleased()) {351reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);352return;353}354}355}356}357C.addTransition(State);358}359360void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,361CheckerContext &C) const {362const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());363if (!FuncDecl)364return;365366// If we analyzed the function body, then ignore the annotations.367if (C.wasInlined)368return;369370ProgramStateRef State = C.getState();371372std::vector<std::function<std::string(BugReport & BR)>> Notes;373SymbolRef ResultSymbol = nullptr;374if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>())375if (TypeDefTy->getDecl()->getName() == ErrorTypeName)376ResultSymbol = Call.getReturnValue().getAsSymbol();377378// Function returns an open handle.379if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {380SymbolRef RetSym = Call.getReturnValue().getAsSymbol();381Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {382auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);383if (PathBR->getInterestingnessKind(RetSym)) {384std::string SBuf;385llvm::raw_string_ostream OS(SBuf);386OS << "Function '" << FuncDecl->getDeclName()387<< "' returns an open handle";388return SBuf;389} else390return "";391});392State =393State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));394} else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) {395// Function returns an unowned handle396SymbolRef RetSym = Call.getReturnValue().getAsSymbol();397Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {398auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);399if (PathBR->getInterestingnessKind(RetSym)) {400std::string SBuf;401llvm::raw_string_ostream OS(SBuf);402OS << "Function '" << FuncDecl->getDeclName()403<< "' returns an unowned handle";404return SBuf;405} else406return "";407});408State = State->set<HStateMap>(RetSym, HandleState::getUnowned());409}410411for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {412if (Arg >= FuncDecl->getNumParams())413break;414const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);415unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1;416SmallVector<SymbolRef, 1024> Handles =417getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);418419for (SymbolRef Handle : Handles) {420const HandleState *HState = State->get<HStateMap>(Handle);421if (HState && HState->isEscaped())422continue;423if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {424if (HState && HState->isReleased()) {425reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);426return;427} else if (HState && HState->isUnowned()) {428reportUnownedRelease(Handle, Call.getArgSourceRange(Arg), C);429return;430} else {431Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {432auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);433if (PathBR->getInterestingnessKind(Handle)) {434std::string SBuf;435llvm::raw_string_ostream OS(SBuf);436OS << "Handle released through " << ParamDiagIdx437<< llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";438return SBuf;439} else440return "";441});442State = State->set<HStateMap>(Handle, HandleState::getReleased());443}444} else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {445Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {446auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);447if (PathBR->getInterestingnessKind(Handle)) {448std::string SBuf;449llvm::raw_string_ostream OS(SBuf);450OS << "Handle allocated through " << ParamDiagIdx451<< llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";452return SBuf;453} else454return "";455});456State = State->set<HStateMap>(457Handle, HandleState::getMaybeAllocated(ResultSymbol));458} else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)) {459Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {460auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);461if (PathBR->getInterestingnessKind(Handle)) {462std::string SBuf;463llvm::raw_string_ostream OS(SBuf);464OS << "Unowned handle allocated through " << ParamDiagIdx465<< llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";466return SBuf;467} else468return "";469});470State = State->set<HStateMap>(Handle, HandleState::getUnowned());471} else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&472PVD->getType()->isIntegerType()) {473// Working around integer by-value escapes.474// The by-value escape would not be captured in checkPointerEscape.475// If the function was not analyzed (otherwise wasInlined should be476// true) and there is no annotation on the handle, we assume the handle477// is escaped.478State = State->set<HStateMap>(Handle, HandleState::getEscaped());479}480}481}482const NoteTag *T = nullptr;483if (!Notes.empty()) {484T = C.getNoteTag([this, Notes{std::move(Notes)}](485PathSensitiveBugReport &BR) -> std::string {486if (&BR.getBugType() != &UseAfterReleaseBugType &&487&BR.getBugType() != &LeakBugType &&488&BR.getBugType() != &DoubleReleaseBugType &&489&BR.getBugType() != &ReleaseUnownedBugType)490return "";491for (auto &Note : Notes) {492std::string Text = Note(BR);493if (!Text.empty())494return Text;495}496return "";497});498}499C.addTransition(State, T);500}501502void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,503CheckerContext &C) const {504ProgramStateRef State = C.getState();505SmallVector<SymbolRef, 2> LeakedSyms;506HStateMapTy TrackedHandles = State->get<HStateMap>();507for (auto &CurItem : TrackedHandles) {508SymbolRef ErrorSym = CurItem.second.getErrorSym();509// Keeping zombie handle symbols. In case the error symbol is dying later510// than the handle symbol we might produce spurious leak warnings (in case511// we find out later from the status code that the handle allocation failed512// in the first place).513if (!SymReaper.isDead(CurItem.first) ||514(ErrorSym && !SymReaper.isDead(ErrorSym)))515continue;516if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())517LeakedSyms.push_back(CurItem.first);518State = State->remove<HStateMap>(CurItem.first);519}520521ExplodedNode *N = C.getPredecessor();522if (!LeakedSyms.empty())523N = reportLeaks(LeakedSyms, C, N);524525C.addTransition(State, N);526}527528// Acquiring a handle is not always successful. In Fuchsia most functions529// return a status code that determines the status of the handle.530// When we split the path based on this status code we know that on one531// path we do have the handle and on the other path the acquire failed.532// This method helps avoiding false positive leak warnings on paths where533// the function failed.534// Moreover, when a handle is known to be zero (the invalid handle),535// we no longer can follow the symbol on the path, becaue the constant536// zero will be used instead of the symbol. We also do not need to release537// an invalid handle, so we remove the corresponding symbol from the state.538ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,539SVal Cond,540bool Assumption) const {541// TODO: add notes about successes/fails for APIs.542ConstraintManager &Cmr = State->getConstraintManager();543HStateMapTy TrackedHandles = State->get<HStateMap>();544for (auto &CurItem : TrackedHandles) {545ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first);546if (HandleVal.isConstrainedTrue()) {547// The handle is invalid. We can no longer follow the symbol on this path.548State = State->remove<HStateMap>(CurItem.first);549}550SymbolRef ErrorSym = CurItem.second.getErrorSym();551if (!ErrorSym)552continue;553ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym);554if (ErrorVal.isConstrainedTrue()) {555// Allocation succeeded.556if (CurItem.second.maybeAllocated())557State = State->set<HStateMap>(558CurItem.first, HandleState::getAllocated(State, CurItem.second));559} else if (ErrorVal.isConstrainedFalse()) {560// Allocation failed.561if (CurItem.second.maybeAllocated())562State = State->remove<HStateMap>(CurItem.first);563}564}565return State;566}567568ProgramStateRef FuchsiaHandleChecker::checkPointerEscape(569ProgramStateRef State, const InvalidatedSymbols &Escaped,570const CallEvent *Call, PointerEscapeKind Kind) const {571const FunctionDecl *FuncDecl =572Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr;573574llvm::DenseSet<SymbolRef> UnEscaped;575// Not all calls should escape our symbols.576if (FuncDecl &&577(Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall ||578Kind == PSK_EscapeOutParameters)) {579for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {580if (Arg >= FuncDecl->getNumParams())581break;582const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);583SmallVector<SymbolRef, 1024> Handles =584getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Arg), State);585for (SymbolRef Handle : Handles) {586if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||587hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {588UnEscaped.insert(Handle);589}590}591}592}593594// For out params, we have to deal with derived symbols. See595// MacOSKeychainAPIChecker for details.596for (auto I : State->get<HStateMap>()) {597if (Escaped.count(I.first) && !UnEscaped.count(I.first))598State = State->set<HStateMap>(I.first, HandleState::getEscaped());599if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) {600auto ParentSym = SD->getParentSymbol();601if (Escaped.count(ParentSym))602State = State->set<HStateMap>(I.first, HandleState::getEscaped());603}604}605606return State;607}608609ExplodedNode *610FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,611CheckerContext &C, ExplodedNode *Pred) const {612ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred);613for (SymbolRef LeakedHandle : LeakedHandles) {614reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType,615"Potential leak of handle");616}617return ErrNode;618}619620void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,621const SourceRange &Range,622CheckerContext &C) const {623ExplodedNode *ErrNode = C.generateErrorNode(C.getState());624reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,625"Releasing a previously released handle");626}627628void FuchsiaHandleChecker::reportUnownedRelease(SymbolRef HandleSym,629const SourceRange &Range,630CheckerContext &C) const {631ExplodedNode *ErrNode = C.generateErrorNode(C.getState());632reportBug(HandleSym, ErrNode, C, &Range, ReleaseUnownedBugType,633"Releasing an unowned handle");634}635636void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,637const SourceRange &Range,638CheckerContext &C) const {639ExplodedNode *ErrNode = C.generateErrorNode(C.getState());640reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType,641"Using a previously released handle");642}643644void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,645CheckerContext &C,646const SourceRange *Range,647const BugType &Type, StringRef Msg) const {648if (!ErrorNode)649return;650651std::unique_ptr<PathSensitiveBugReport> R;652if (Type.isSuppressOnSink()) {653const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C);654if (AcquireNode) {655const Stmt *S = AcquireNode->getStmtForDiagnostics();656assert(S && "Statement cannot be null.");657PathDiagnosticLocation LocUsedForUniqueing =658PathDiagnosticLocation::createBegin(659S, C.getSourceManager(), AcquireNode->getLocationContext());660661R = std::make_unique<PathSensitiveBugReport>(662Type, Msg, ErrorNode, LocUsedForUniqueing,663AcquireNode->getLocationContext()->getDecl());664}665}666if (!R)667R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);668if (Range)669R->addRange(*Range);670R->markInteresting(Sym);671C.emitReport(std::move(R));672}673674void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {675mgr.registerChecker<FuchsiaHandleChecker>();676}677678bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {679return true;680}681682void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,683const char *NL, const char *Sep) const {684685HStateMapTy StateMap = State->get<HStateMap>();686687if (!StateMap.isEmpty()) {688Out << Sep << "FuchsiaHandleChecker :" << NL;689for (const auto &[Sym, HandleState] : StateMap) {690Sym->dumpToStream(Out);691Out << " : ";692HandleState.dump(Out);693Out << NL;694}695}696}697698699