1480093f4SDimitry Andric //=== FuchsiaHandleChecker.cpp - Find handle leaks/double closes -*- C++ -*--=// 2480093f4SDimitry Andric // 3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6480093f4SDimitry Andric // 7480093f4SDimitry Andric //===----------------------------------------------------------------------===// 8480093f4SDimitry Andric // 9480093f4SDimitry Andric // This checker checks if the handle of Fuchsia is properly used according to 10480093f4SDimitry Andric // following rules. 11480093f4SDimitry Andric // - If a handle is acquired, it should be released before execution 12480093f4SDimitry Andric // ends. 13480093f4SDimitry Andric // - If a handle is released, it should not be released again. 14480093f4SDimitry Andric // - If a handle is released, it should not be used for other purposes 15480093f4SDimitry Andric // such as I/O. 16480093f4SDimitry Andric // 17480093f4SDimitry Andric // In this checker, each tracked handle is associated with a state. When the 18480093f4SDimitry Andric // handle variable is passed to different function calls or syscalls, its state 19480093f4SDimitry Andric // changes. The state changes can be generally represented by following ASCII 20480093f4SDimitry Andric // Art: 21480093f4SDimitry Andric // 22480093f4SDimitry Andric // 23e8d8bef9SDimitry Andric // +-------------+ +------------+ 24480093f4SDimitry Andric // acquire_func succeeded | | Escape | | 25480093f4SDimitry Andric // +-----------------> Allocated +---------> Escaped <--+ 26480093f4SDimitry Andric // | | | | | | 27480093f4SDimitry Andric // | +-----+------++ +------------+ | 28480093f4SDimitry Andric // | | | | 29e8d8bef9SDimitry Andric // acquire_func | release_func | +--+ | 30e8d8bef9SDimitry Andric // failed | | | handle +--------+ | 31e8d8bef9SDimitry Andric // +---------+ | | | dies | | | 32e8d8bef9SDimitry Andric // | | | +----v-----+ +---------> Leaked | | 33e8d8bef9SDimitry Andric // | | | | | |(REPORT)| | 34e8d8bef9SDimitry Andric // | +----------+--+ | Released | Escape +--------+ | 35e8d8bef9SDimitry Andric // | | | | +---------------------------+ 36e8d8bef9SDimitry Andric // +--> Not tracked | +----+---+-+ 37e8d8bef9SDimitry Andric // | | | | As argument by value 38e8d8bef9SDimitry Andric // +----------+--+ release_func | +------+ in function call 39e8d8bef9SDimitry Andric // | | | or by reference in 40e8d8bef9SDimitry Andric // | | | use_func call 41e8d8bef9SDimitry Andric // unowned | +----v-----+ | +-----------+ 42e8d8bef9SDimitry Andric // acquire_func | | Double | +-----> Use after | 43e8d8bef9SDimitry Andric // succeeded | | released | | released | 44e8d8bef9SDimitry Andric // | | (REPORT) | | (REPORT) | 45e8d8bef9SDimitry Andric // +---------------+ +----------+ +-----------+ 46e8d8bef9SDimitry Andric // | Allocated | 47e8d8bef9SDimitry Andric // | Unowned | release_func 48e8d8bef9SDimitry Andric // | +---------+ 49e8d8bef9SDimitry Andric // +---------------+ | 50e8d8bef9SDimitry Andric // | 51e8d8bef9SDimitry Andric // +-----v----------+ 52e8d8bef9SDimitry Andric // | Release of | 53e8d8bef9SDimitry Andric // | unowned handle | 54e8d8bef9SDimitry Andric // | (REPORT) | 55e8d8bef9SDimitry Andric // +----------------+ 56480093f4SDimitry Andric // 57480093f4SDimitry Andric // acquire_func represents the functions or syscalls that may acquire a handle. 58480093f4SDimitry Andric // release_func represents the functions or syscalls that may release a handle. 59480093f4SDimitry Andric // use_func represents the functions or syscall that requires an open handle. 60480093f4SDimitry Andric // 61480093f4SDimitry Andric // If a tracked handle dies in "Released" or "Not Tracked" state, we assume it 62480093f4SDimitry Andric // is properly used. Otherwise a bug and will be reported. 63480093f4SDimitry Andric // 64480093f4SDimitry Andric // Note that, the analyzer does not always know for sure if a function failed 65480093f4SDimitry Andric // or succeeded. In those cases we use the state MaybeAllocated. 66e8d8bef9SDimitry Andric // Thus, the diagram above captures the intent, not implementation details. 67480093f4SDimitry Andric // 68480093f4SDimitry Andric // Due to the fact that the number of handle related syscalls in Fuchsia 69480093f4SDimitry Andric // is large, we adopt the annotation attributes to descript syscalls' 70480093f4SDimitry Andric // operations(acquire/release/use) on handles instead of hardcoding 71480093f4SDimitry Andric // everything in the checker. 72480093f4SDimitry Andric // 73480093f4SDimitry Andric // We use following annotation attributes for handle related syscalls or 74480093f4SDimitry Andric // functions: 75480093f4SDimitry Andric // 1. __attribute__((acquire_handle("Fuchsia"))) |handle will be acquired 76480093f4SDimitry Andric // 2. __attribute__((release_handle("Fuchsia"))) |handle will be released 77480093f4SDimitry Andric // 3. __attribute__((use_handle("Fuchsia"))) |handle will not transit to 78480093f4SDimitry Andric // escaped state, it also needs to be open. 79480093f4SDimitry Andric // 80480093f4SDimitry Andric // For example, an annotated syscall: 81480093f4SDimitry Andric // zx_status_t zx_channel_create( 82480093f4SDimitry Andric // uint32_t options, 83480093f4SDimitry Andric // zx_handle_t* out0 __attribute__((acquire_handle("Fuchsia"))) , 84480093f4SDimitry Andric // zx_handle_t* out1 __attribute__((acquire_handle("Fuchsia")))); 85480093f4SDimitry Andric // denotes a syscall which will acquire two handles and save them to 'out0' and 86480093f4SDimitry Andric // 'out1' when succeeded. 87480093f4SDimitry Andric // 88480093f4SDimitry Andric //===----------------------------------------------------------------------===// 89480093f4SDimitry Andric 90480093f4SDimitry Andric #include "clang/AST/Attr.h" 91480093f4SDimitry Andric #include "clang/AST/Decl.h" 92480093f4SDimitry Andric #include "clang/AST/Type.h" 93480093f4SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 94480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 95480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 96480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 97480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 98480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 99480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" 100480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 101480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 102480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" 1035ffd83dbSDimitry Andric #include "llvm/ADT/StringExtras.h" 104*bdd1243dSDimitry Andric #include <optional> 105480093f4SDimitry Andric 106480093f4SDimitry Andric using namespace clang; 107480093f4SDimitry Andric using namespace ento; 108480093f4SDimitry Andric 109480093f4SDimitry Andric namespace { 110480093f4SDimitry Andric 111480093f4SDimitry Andric static const StringRef HandleTypeName = "zx_handle_t"; 112480093f4SDimitry Andric static const StringRef ErrorTypeName = "zx_status_t"; 113480093f4SDimitry Andric 114480093f4SDimitry Andric class HandleState { 115480093f4SDimitry Andric private: 116e8d8bef9SDimitry Andric enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K; 117480093f4SDimitry Andric SymbolRef ErrorSym; 118480093f4SDimitry Andric HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {} 119480093f4SDimitry Andric 120480093f4SDimitry Andric public: 121480093f4SDimitry Andric bool operator==(const HandleState &Other) const { 122480093f4SDimitry Andric return K == Other.K && ErrorSym == Other.ErrorSym; 123480093f4SDimitry Andric } 124480093f4SDimitry Andric bool isAllocated() const { return K == Kind::Allocated; } 125480093f4SDimitry Andric bool maybeAllocated() const { return K == Kind::MaybeAllocated; } 126480093f4SDimitry Andric bool isReleased() const { return K == Kind::Released; } 127480093f4SDimitry Andric bool isEscaped() const { return K == Kind::Escaped; } 128e8d8bef9SDimitry Andric bool isUnowned() const { return K == Kind::Unowned; } 129480093f4SDimitry Andric 130480093f4SDimitry Andric static HandleState getMaybeAllocated(SymbolRef ErrorSym) { 131480093f4SDimitry Andric return HandleState(Kind::MaybeAllocated, ErrorSym); 132480093f4SDimitry Andric } 133480093f4SDimitry Andric static HandleState getAllocated(ProgramStateRef State, HandleState S) { 134480093f4SDimitry Andric assert(S.maybeAllocated()); 135480093f4SDimitry Andric assert(State->getConstraintManager() 136480093f4SDimitry Andric .isNull(State, S.getErrorSym()) 137480093f4SDimitry Andric .isConstrained()); 138480093f4SDimitry Andric return HandleState(Kind::Allocated, nullptr); 139480093f4SDimitry Andric } 140480093f4SDimitry Andric static HandleState getReleased() { 141480093f4SDimitry Andric return HandleState(Kind::Released, nullptr); 142480093f4SDimitry Andric } 143480093f4SDimitry Andric static HandleState getEscaped() { 144480093f4SDimitry Andric return HandleState(Kind::Escaped, nullptr); 145480093f4SDimitry Andric } 146e8d8bef9SDimitry Andric static HandleState getUnowned() { 147e8d8bef9SDimitry Andric return HandleState(Kind::Unowned, nullptr); 148e8d8bef9SDimitry Andric } 149480093f4SDimitry Andric 150480093f4SDimitry Andric SymbolRef getErrorSym() const { return ErrorSym; } 151480093f4SDimitry Andric 152480093f4SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const { 153480093f4SDimitry Andric ID.AddInteger(static_cast<int>(K)); 154480093f4SDimitry Andric ID.AddPointer(ErrorSym); 155480093f4SDimitry Andric } 156480093f4SDimitry Andric 157480093f4SDimitry Andric LLVM_DUMP_METHOD void dump(raw_ostream &OS) const { 158480093f4SDimitry Andric switch (K) { 159480093f4SDimitry Andric #define CASE(ID) \ 160480093f4SDimitry Andric case ID: \ 161480093f4SDimitry Andric OS << #ID; \ 162480093f4SDimitry Andric break; 163480093f4SDimitry Andric CASE(Kind::MaybeAllocated) 164480093f4SDimitry Andric CASE(Kind::Allocated) 165480093f4SDimitry Andric CASE(Kind::Released) 166480093f4SDimitry Andric CASE(Kind::Escaped) 167e8d8bef9SDimitry Andric CASE(Kind::Unowned) 168480093f4SDimitry Andric } 1695ffd83dbSDimitry Andric if (ErrorSym) { 1705ffd83dbSDimitry Andric OS << " ErrorSym: "; 1715ffd83dbSDimitry Andric ErrorSym->dumpToStream(OS); 1725ffd83dbSDimitry Andric } 173480093f4SDimitry Andric } 174480093f4SDimitry Andric 175480093f4SDimitry Andric LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); } 176480093f4SDimitry Andric }; 177480093f4SDimitry Andric 178480093f4SDimitry Andric template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) { 179480093f4SDimitry Andric return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia"; 180480093f4SDimitry Andric } 181480093f4SDimitry Andric 182e8d8bef9SDimitry Andric template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) { 183e8d8bef9SDimitry Andric return D->hasAttr<Attr>() && 184e8d8bef9SDimitry Andric D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned"; 185e8d8bef9SDimitry Andric } 186e8d8bef9SDimitry Andric 187480093f4SDimitry Andric class FuchsiaHandleChecker 188480093f4SDimitry Andric : public Checker<check::PostCall, check::PreCall, check::DeadSymbols, 189480093f4SDimitry Andric check::PointerEscape, eval::Assume> { 190480093f4SDimitry Andric BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error", 191480093f4SDimitry Andric /*SuppressOnSink=*/true}; 192480093f4SDimitry Andric BugType DoubleReleaseBugType{this, "Fuchsia handle double release", 193480093f4SDimitry Andric "Fuchsia Handle Error"}; 194480093f4SDimitry Andric BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release", 195480093f4SDimitry Andric "Fuchsia Handle Error"}; 196e8d8bef9SDimitry Andric BugType ReleaseUnownedBugType{ 197e8d8bef9SDimitry Andric this, "Fuchsia handle release of unowned handle", "Fuchsia Handle Error"}; 198480093f4SDimitry Andric 199480093f4SDimitry Andric public: 200480093f4SDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 201480093f4SDimitry Andric void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 202480093f4SDimitry Andric void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 203480093f4SDimitry Andric ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, 204480093f4SDimitry Andric bool Assumption) const; 205480093f4SDimitry Andric ProgramStateRef checkPointerEscape(ProgramStateRef State, 206480093f4SDimitry Andric const InvalidatedSymbols &Escaped, 207480093f4SDimitry Andric const CallEvent *Call, 208480093f4SDimitry Andric PointerEscapeKind Kind) const; 209480093f4SDimitry Andric 210480093f4SDimitry Andric ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles, 211480093f4SDimitry Andric CheckerContext &C, ExplodedNode *Pred) const; 212480093f4SDimitry Andric 213480093f4SDimitry Andric void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range, 214480093f4SDimitry Andric CheckerContext &C) const; 215480093f4SDimitry Andric 216e8d8bef9SDimitry Andric void reportUnownedRelease(SymbolRef HandleSym, const SourceRange &Range, 217e8d8bef9SDimitry Andric CheckerContext &C) const; 218e8d8bef9SDimitry Andric 219480093f4SDimitry Andric void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range, 220480093f4SDimitry Andric CheckerContext &C) const; 221480093f4SDimitry Andric 222480093f4SDimitry Andric void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C, 223480093f4SDimitry Andric const SourceRange *Range, const BugType &Type, 224480093f4SDimitry Andric StringRef Msg) const; 225480093f4SDimitry Andric 226480093f4SDimitry Andric void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 227480093f4SDimitry Andric const char *Sep) const override; 228480093f4SDimitry Andric }; 229480093f4SDimitry Andric } // end anonymous namespace 230480093f4SDimitry Andric 231480093f4SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState) 232480093f4SDimitry Andric 233480093f4SDimitry Andric static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym, 234480093f4SDimitry Andric CheckerContext &Ctx) { 235480093f4SDimitry Andric ProgramStateRef State = N->getState(); 236480093f4SDimitry Andric // When bug type is handle leak, exploded node N does not have state info for 237480093f4SDimitry Andric // leaking handle. Get the predecessor of N instead. 238480093f4SDimitry Andric if (!State->get<HStateMap>(Sym)) 239480093f4SDimitry Andric N = N->getFirstPred(); 240480093f4SDimitry Andric 241480093f4SDimitry Andric const ExplodedNode *Pred = N; 242480093f4SDimitry Andric while (N) { 243480093f4SDimitry Andric State = N->getState(); 244480093f4SDimitry Andric if (!State->get<HStateMap>(Sym)) { 245480093f4SDimitry Andric const HandleState *HState = Pred->getState()->get<HStateMap>(Sym); 246480093f4SDimitry Andric if (HState && (HState->isAllocated() || HState->maybeAllocated())) 247480093f4SDimitry Andric return N; 248480093f4SDimitry Andric } 249480093f4SDimitry Andric Pred = N; 250480093f4SDimitry Andric N = N->getFirstPred(); 251480093f4SDimitry Andric } 252480093f4SDimitry Andric return nullptr; 253480093f4SDimitry Andric } 254480093f4SDimitry Andric 255e8d8bef9SDimitry Andric namespace { 256e8d8bef9SDimitry Andric class FuchsiaHandleSymbolVisitor final : public SymbolVisitor { 257e8d8bef9SDimitry Andric public: 258e8d8bef9SDimitry Andric bool VisitSymbol(SymbolRef S) override { 259e8d8bef9SDimitry Andric if (const auto *HandleType = S->getType()->getAs<TypedefType>()) 260e8d8bef9SDimitry Andric if (HandleType->getDecl()->getName() == HandleTypeName) 261e8d8bef9SDimitry Andric Symbols.push_back(S); 262e8d8bef9SDimitry Andric return true; 263e8d8bef9SDimitry Andric } 264e8d8bef9SDimitry Andric 265e8d8bef9SDimitry Andric SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; } 266e8d8bef9SDimitry Andric 267e8d8bef9SDimitry Andric private: 268e8d8bef9SDimitry Andric SmallVector<SymbolRef, 1024> Symbols; 269e8d8bef9SDimitry Andric }; 270e8d8bef9SDimitry Andric } // end anonymous namespace 271e8d8bef9SDimitry Andric 272e8d8bef9SDimitry Andric /// Returns the symbols extracted from the argument or empty vector if it cannot 273e8d8bef9SDimitry Andric /// be found. It is unlikely to have over 1024 symbols in one argument. 274e8d8bef9SDimitry Andric static SmallVector<SymbolRef, 1024> 275e8d8bef9SDimitry Andric getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) { 276480093f4SDimitry Andric int PtrToHandleLevel = 0; 277480093f4SDimitry Andric while (QT->isAnyPointerType() || QT->isReferenceType()) { 278480093f4SDimitry Andric ++PtrToHandleLevel; 279480093f4SDimitry Andric QT = QT->getPointeeType(); 280480093f4SDimitry Andric } 281e8d8bef9SDimitry Andric if (QT->isStructureType()) { 282e8d8bef9SDimitry Andric // If we see a structure, see if there is any handle referenced by the 283e8d8bef9SDimitry Andric // structure. 28481ad6265SDimitry Andric FuchsiaHandleSymbolVisitor Visitor; 285e8d8bef9SDimitry Andric State->scanReachableSymbols(Arg, Visitor); 286e8d8bef9SDimitry Andric return Visitor.GetSymbols(); 287e8d8bef9SDimitry Andric } 288480093f4SDimitry Andric if (const auto *HandleType = QT->getAs<TypedefType>()) { 289480093f4SDimitry Andric if (HandleType->getDecl()->getName() != HandleTypeName) 290e8d8bef9SDimitry Andric return {}; 291e8d8bef9SDimitry Andric if (PtrToHandleLevel > 1) 292480093f4SDimitry Andric // Not supported yet. 293e8d8bef9SDimitry Andric return {}; 294480093f4SDimitry Andric 295480093f4SDimitry Andric if (PtrToHandleLevel == 0) { 296e8d8bef9SDimitry Andric SymbolRef Sym = Arg.getAsSymbol(); 297e8d8bef9SDimitry Andric if (Sym) { 298e8d8bef9SDimitry Andric return {Sym}; 299e8d8bef9SDimitry Andric } else { 300e8d8bef9SDimitry Andric return {}; 301e8d8bef9SDimitry Andric } 302480093f4SDimitry Andric } else { 303480093f4SDimitry Andric assert(PtrToHandleLevel == 1); 304*bdd1243dSDimitry Andric if (std::optional<Loc> ArgLoc = Arg.getAs<Loc>()) { 305e8d8bef9SDimitry Andric SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol(); 306e8d8bef9SDimitry Andric if (Sym) { 307e8d8bef9SDimitry Andric return {Sym}; 308e8d8bef9SDimitry Andric } else { 309e8d8bef9SDimitry Andric return {}; 310480093f4SDimitry Andric } 311480093f4SDimitry Andric } 312e8d8bef9SDimitry Andric } 313e8d8bef9SDimitry Andric } 314e8d8bef9SDimitry Andric return {}; 315480093f4SDimitry Andric } 316480093f4SDimitry Andric 317480093f4SDimitry Andric void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call, 318480093f4SDimitry Andric CheckerContext &C) const { 319480093f4SDimitry Andric ProgramStateRef State = C.getState(); 320480093f4SDimitry Andric const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 321480093f4SDimitry Andric if (!FuncDecl) { 322480093f4SDimitry Andric // Unknown call, escape by value handles. They are not covered by 323480093f4SDimitry Andric // PointerEscape callback. 324480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 325480093f4SDimitry Andric if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol()) 326480093f4SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getEscaped()); 327480093f4SDimitry Andric } 328480093f4SDimitry Andric C.addTransition(State); 329480093f4SDimitry Andric return; 330480093f4SDimitry Andric } 331480093f4SDimitry Andric 332480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 333480093f4SDimitry Andric if (Arg >= FuncDecl->getNumParams()) 334480093f4SDimitry Andric break; 335480093f4SDimitry Andric const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 336e8d8bef9SDimitry Andric SmallVector<SymbolRef, 1024> Handles = 337e8d8bef9SDimitry Andric getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State); 338480093f4SDimitry Andric 339480093f4SDimitry Andric // Handled in checkPostCall. 340480093f4SDimitry Andric if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) || 341480093f4SDimitry Andric hasFuchsiaAttr<AcquireHandleAttr>(PVD)) 342480093f4SDimitry Andric continue; 343480093f4SDimitry Andric 344e8d8bef9SDimitry Andric for (SymbolRef Handle : Handles) { 345480093f4SDimitry Andric const HandleState *HState = State->get<HStateMap>(Handle); 346480093f4SDimitry Andric if (!HState || HState->isEscaped()) 347480093f4SDimitry Andric continue; 348480093f4SDimitry Andric 349e8d8bef9SDimitry Andric if (hasFuchsiaAttr<UseHandleAttr>(PVD) || 350e8d8bef9SDimitry Andric PVD->getType()->isIntegerType()) { 351480093f4SDimitry Andric if (HState->isReleased()) { 352480093f4SDimitry Andric reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C); 353480093f4SDimitry Andric return; 354480093f4SDimitry Andric } 355480093f4SDimitry Andric } 356480093f4SDimitry Andric } 357480093f4SDimitry Andric } 358480093f4SDimitry Andric C.addTransition(State); 359480093f4SDimitry Andric } 360480093f4SDimitry Andric 361480093f4SDimitry Andric void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, 362480093f4SDimitry Andric CheckerContext &C) const { 363480093f4SDimitry Andric const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 364480093f4SDimitry Andric if (!FuncDecl) 365480093f4SDimitry Andric return; 366480093f4SDimitry Andric 367e8d8bef9SDimitry Andric // If we analyzed the function body, then ignore the annotations. 368e8d8bef9SDimitry Andric if (C.wasInlined) 369e8d8bef9SDimitry Andric return; 370e8d8bef9SDimitry Andric 371480093f4SDimitry Andric ProgramStateRef State = C.getState(); 372480093f4SDimitry Andric 373480093f4SDimitry Andric std::vector<std::function<std::string(BugReport & BR)>> Notes; 374480093f4SDimitry Andric SymbolRef ResultSymbol = nullptr; 375480093f4SDimitry Andric if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>()) 376480093f4SDimitry Andric if (TypeDefTy->getDecl()->getName() == ErrorTypeName) 377480093f4SDimitry Andric ResultSymbol = Call.getReturnValue().getAsSymbol(); 378480093f4SDimitry Andric 379480093f4SDimitry Andric // Function returns an open handle. 380480093f4SDimitry Andric if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) { 381480093f4SDimitry Andric SymbolRef RetSym = Call.getReturnValue().getAsSymbol(); 3825ffd83dbSDimitry Andric Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string { 3835ffd83dbSDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 3845ffd83dbSDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) { 3855ffd83dbSDimitry Andric std::string SBuf; 3865ffd83dbSDimitry Andric llvm::raw_string_ostream OS(SBuf); 387e8d8bef9SDimitry Andric OS << "Function '" << FuncDecl->getDeclName() 3885ffd83dbSDimitry Andric << "' returns an open handle"; 3890eae32dcSDimitry Andric return SBuf; 3905ffd83dbSDimitry Andric } else 3915ffd83dbSDimitry Andric return ""; 3925ffd83dbSDimitry Andric }); 393480093f4SDimitry Andric State = 394480093f4SDimitry Andric State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr)); 395e8d8bef9SDimitry Andric } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) { 396e8d8bef9SDimitry Andric // Function returns an unowned handle 397e8d8bef9SDimitry Andric SymbolRef RetSym = Call.getReturnValue().getAsSymbol(); 398e8d8bef9SDimitry Andric Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string { 399e8d8bef9SDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 400e8d8bef9SDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) { 401e8d8bef9SDimitry Andric std::string SBuf; 402e8d8bef9SDimitry Andric llvm::raw_string_ostream OS(SBuf); 403e8d8bef9SDimitry Andric OS << "Function '" << FuncDecl->getDeclName() 404e8d8bef9SDimitry Andric << "' returns an unowned handle"; 4050eae32dcSDimitry Andric return SBuf; 406e8d8bef9SDimitry Andric } else 407e8d8bef9SDimitry Andric return ""; 408e8d8bef9SDimitry Andric }); 409e8d8bef9SDimitry Andric State = State->set<HStateMap>(RetSym, HandleState::getUnowned()); 410480093f4SDimitry Andric } 411480093f4SDimitry Andric 412480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 413480093f4SDimitry Andric if (Arg >= FuncDecl->getNumParams()) 414480093f4SDimitry Andric break; 415480093f4SDimitry Andric const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 4165ffd83dbSDimitry Andric unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1; 417e8d8bef9SDimitry Andric SmallVector<SymbolRef, 1024> Handles = 418e8d8bef9SDimitry Andric getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State); 419480093f4SDimitry Andric 420e8d8bef9SDimitry Andric for (SymbolRef Handle : Handles) { 421480093f4SDimitry Andric const HandleState *HState = State->get<HStateMap>(Handle); 422480093f4SDimitry Andric if (HState && HState->isEscaped()) 423480093f4SDimitry Andric continue; 424480093f4SDimitry Andric if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) { 425480093f4SDimitry Andric if (HState && HState->isReleased()) { 426480093f4SDimitry Andric reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C); 427480093f4SDimitry Andric return; 428e8d8bef9SDimitry Andric } else if (HState && HState->isUnowned()) { 429e8d8bef9SDimitry Andric reportUnownedRelease(Handle, Call.getArgSourceRange(Arg), C); 430e8d8bef9SDimitry Andric return; 431480093f4SDimitry Andric } else { 4325ffd83dbSDimitry Andric Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { 433480093f4SDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 434480093f4SDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { 4355ffd83dbSDimitry Andric std::string SBuf; 4365ffd83dbSDimitry Andric llvm::raw_string_ostream OS(SBuf); 4375ffd83dbSDimitry Andric OS << "Handle released through " << ParamDiagIdx 4385ffd83dbSDimitry Andric << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; 4390eae32dcSDimitry Andric return SBuf; 440480093f4SDimitry Andric } else 441480093f4SDimitry Andric return ""; 442480093f4SDimitry Andric }); 443480093f4SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getReleased()); 444480093f4SDimitry Andric } 445480093f4SDimitry Andric } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) { 4465ffd83dbSDimitry Andric Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { 447480093f4SDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 448480093f4SDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { 4495ffd83dbSDimitry Andric std::string SBuf; 4505ffd83dbSDimitry Andric llvm::raw_string_ostream OS(SBuf); 4515ffd83dbSDimitry Andric OS << "Handle allocated through " << ParamDiagIdx 4525ffd83dbSDimitry Andric << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; 4530eae32dcSDimitry Andric return SBuf; 454480093f4SDimitry Andric } else 455480093f4SDimitry Andric return ""; 456480093f4SDimitry Andric }); 457480093f4SDimitry Andric State = State->set<HStateMap>( 458480093f4SDimitry Andric Handle, HandleState::getMaybeAllocated(ResultSymbol)); 459e8d8bef9SDimitry Andric } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)) { 460e8d8bef9SDimitry Andric Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { 461e8d8bef9SDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 462e8d8bef9SDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { 463e8d8bef9SDimitry Andric std::string SBuf; 464e8d8bef9SDimitry Andric llvm::raw_string_ostream OS(SBuf); 465e8d8bef9SDimitry Andric OS << "Unowned handle allocated through " << ParamDiagIdx 466e8d8bef9SDimitry Andric << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; 4670eae32dcSDimitry Andric return SBuf; 468e8d8bef9SDimitry Andric } else 469e8d8bef9SDimitry Andric return ""; 470e8d8bef9SDimitry Andric }); 471e8d8bef9SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getUnowned()); 472e8d8bef9SDimitry Andric } else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) && 473e8d8bef9SDimitry Andric PVD->getType()->isIntegerType()) { 474e8d8bef9SDimitry Andric // Working around integer by-value escapes. 475e8d8bef9SDimitry Andric // The by-value escape would not be captured in checkPointerEscape. 476e8d8bef9SDimitry Andric // If the function was not analyzed (otherwise wasInlined should be 477e8d8bef9SDimitry Andric // true) and there is no annotation on the handle, we assume the handle 478e8d8bef9SDimitry Andric // is escaped. 479e8d8bef9SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getEscaped()); 480e8d8bef9SDimitry Andric } 481480093f4SDimitry Andric } 482480093f4SDimitry Andric } 483480093f4SDimitry Andric const NoteTag *T = nullptr; 484480093f4SDimitry Andric if (!Notes.empty()) { 4855ffd83dbSDimitry Andric T = C.getNoteTag([this, Notes{std::move(Notes)}]( 4865ffd83dbSDimitry Andric PathSensitiveBugReport &BR) -> std::string { 487480093f4SDimitry Andric if (&BR.getBugType() != &UseAfterReleaseBugType && 488480093f4SDimitry Andric &BR.getBugType() != &LeakBugType && 489e8d8bef9SDimitry Andric &BR.getBugType() != &DoubleReleaseBugType && 490e8d8bef9SDimitry Andric &BR.getBugType() != &ReleaseUnownedBugType) 491480093f4SDimitry Andric return ""; 492480093f4SDimitry Andric for (auto &Note : Notes) { 493480093f4SDimitry Andric std::string Text = Note(BR); 494480093f4SDimitry Andric if (!Text.empty()) 495480093f4SDimitry Andric return Text; 496480093f4SDimitry Andric } 497480093f4SDimitry Andric return ""; 498480093f4SDimitry Andric }); 499480093f4SDimitry Andric } 500480093f4SDimitry Andric C.addTransition(State, T); 501480093f4SDimitry Andric } 502480093f4SDimitry Andric 503480093f4SDimitry Andric void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper, 504480093f4SDimitry Andric CheckerContext &C) const { 505480093f4SDimitry Andric ProgramStateRef State = C.getState(); 506480093f4SDimitry Andric SmallVector<SymbolRef, 2> LeakedSyms; 507480093f4SDimitry Andric HStateMapTy TrackedHandles = State->get<HStateMap>(); 508480093f4SDimitry Andric for (auto &CurItem : TrackedHandles) { 5095ffd83dbSDimitry Andric SymbolRef ErrorSym = CurItem.second.getErrorSym(); 5105ffd83dbSDimitry Andric // Keeping zombie handle symbols. In case the error symbol is dying later 5115ffd83dbSDimitry Andric // than the handle symbol we might produce spurious leak warnings (in case 5125ffd83dbSDimitry Andric // we find out later from the status code that the handle allocation failed 5135ffd83dbSDimitry Andric // in the first place). 5145ffd83dbSDimitry Andric if (!SymReaper.isDead(CurItem.first) || 5155ffd83dbSDimitry Andric (ErrorSym && !SymReaper.isDead(ErrorSym))) 516480093f4SDimitry Andric continue; 517480093f4SDimitry Andric if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated()) 518480093f4SDimitry Andric LeakedSyms.push_back(CurItem.first); 519480093f4SDimitry Andric State = State->remove<HStateMap>(CurItem.first); 520480093f4SDimitry Andric } 521480093f4SDimitry Andric 522480093f4SDimitry Andric ExplodedNode *N = C.getPredecessor(); 523480093f4SDimitry Andric if (!LeakedSyms.empty()) 524480093f4SDimitry Andric N = reportLeaks(LeakedSyms, C, N); 525480093f4SDimitry Andric 526480093f4SDimitry Andric C.addTransition(State, N); 527480093f4SDimitry Andric } 528480093f4SDimitry Andric 529480093f4SDimitry Andric // Acquiring a handle is not always successful. In Fuchsia most functions 530480093f4SDimitry Andric // return a status code that determines the status of the handle. 531480093f4SDimitry Andric // When we split the path based on this status code we know that on one 532480093f4SDimitry Andric // path we do have the handle and on the other path the acquire failed. 533480093f4SDimitry Andric // This method helps avoiding false positive leak warnings on paths where 534480093f4SDimitry Andric // the function failed. 535480093f4SDimitry Andric // Moreover, when a handle is known to be zero (the invalid handle), 536480093f4SDimitry Andric // we no longer can follow the symbol on the path, becaue the constant 537480093f4SDimitry Andric // zero will be used instead of the symbol. We also do not need to release 538480093f4SDimitry Andric // an invalid handle, so we remove the corresponding symbol from the state. 539480093f4SDimitry Andric ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State, 540480093f4SDimitry Andric SVal Cond, 541480093f4SDimitry Andric bool Assumption) const { 542480093f4SDimitry Andric // TODO: add notes about successes/fails for APIs. 543480093f4SDimitry Andric ConstraintManager &Cmr = State->getConstraintManager(); 544480093f4SDimitry Andric HStateMapTy TrackedHandles = State->get<HStateMap>(); 545480093f4SDimitry Andric for (auto &CurItem : TrackedHandles) { 546480093f4SDimitry Andric ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first); 547480093f4SDimitry Andric if (HandleVal.isConstrainedTrue()) { 548480093f4SDimitry Andric // The handle is invalid. We can no longer follow the symbol on this path. 549480093f4SDimitry Andric State = State->remove<HStateMap>(CurItem.first); 550480093f4SDimitry Andric } 551480093f4SDimitry Andric SymbolRef ErrorSym = CurItem.second.getErrorSym(); 552480093f4SDimitry Andric if (!ErrorSym) 553480093f4SDimitry Andric continue; 554480093f4SDimitry Andric ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym); 555480093f4SDimitry Andric if (ErrorVal.isConstrainedTrue()) { 556480093f4SDimitry Andric // Allocation succeeded. 557480093f4SDimitry Andric if (CurItem.second.maybeAllocated()) 558480093f4SDimitry Andric State = State->set<HStateMap>( 559480093f4SDimitry Andric CurItem.first, HandleState::getAllocated(State, CurItem.second)); 560480093f4SDimitry Andric } else if (ErrorVal.isConstrainedFalse()) { 561480093f4SDimitry Andric // Allocation failed. 562480093f4SDimitry Andric if (CurItem.second.maybeAllocated()) 563480093f4SDimitry Andric State = State->remove<HStateMap>(CurItem.first); 564480093f4SDimitry Andric } 565480093f4SDimitry Andric } 566480093f4SDimitry Andric return State; 567480093f4SDimitry Andric } 568480093f4SDimitry Andric 569480093f4SDimitry Andric ProgramStateRef FuchsiaHandleChecker::checkPointerEscape( 570480093f4SDimitry Andric ProgramStateRef State, const InvalidatedSymbols &Escaped, 571480093f4SDimitry Andric const CallEvent *Call, PointerEscapeKind Kind) const { 572480093f4SDimitry Andric const FunctionDecl *FuncDecl = 573480093f4SDimitry Andric Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr; 574480093f4SDimitry Andric 575480093f4SDimitry Andric llvm::DenseSet<SymbolRef> UnEscaped; 576480093f4SDimitry Andric // Not all calls should escape our symbols. 577480093f4SDimitry Andric if (FuncDecl && 578480093f4SDimitry Andric (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall || 579480093f4SDimitry Andric Kind == PSK_EscapeOutParameters)) { 580480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) { 581480093f4SDimitry Andric if (Arg >= FuncDecl->getNumParams()) 582480093f4SDimitry Andric break; 583480093f4SDimitry Andric const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 584e8d8bef9SDimitry Andric SmallVector<SymbolRef, 1024> Handles = 585e8d8bef9SDimitry Andric getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Arg), State); 586e8d8bef9SDimitry Andric for (SymbolRef Handle : Handles) { 587480093f4SDimitry Andric if (hasFuchsiaAttr<UseHandleAttr>(PVD) || 588e8d8bef9SDimitry Andric hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) { 589480093f4SDimitry Andric UnEscaped.insert(Handle); 590480093f4SDimitry Andric } 591480093f4SDimitry Andric } 592e8d8bef9SDimitry Andric } 593e8d8bef9SDimitry Andric } 594480093f4SDimitry Andric 595480093f4SDimitry Andric // For out params, we have to deal with derived symbols. See 596480093f4SDimitry Andric // MacOSKeychainAPIChecker for details. 597480093f4SDimitry Andric for (auto I : State->get<HStateMap>()) { 598480093f4SDimitry Andric if (Escaped.count(I.first) && !UnEscaped.count(I.first)) 599480093f4SDimitry Andric State = State->set<HStateMap>(I.first, HandleState::getEscaped()); 600480093f4SDimitry Andric if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) { 601480093f4SDimitry Andric auto ParentSym = SD->getParentSymbol(); 602480093f4SDimitry Andric if (Escaped.count(ParentSym)) 603480093f4SDimitry Andric State = State->set<HStateMap>(I.first, HandleState::getEscaped()); 604480093f4SDimitry Andric } 605480093f4SDimitry Andric } 606480093f4SDimitry Andric 607480093f4SDimitry Andric return State; 608480093f4SDimitry Andric } 609480093f4SDimitry Andric 610480093f4SDimitry Andric ExplodedNode * 611480093f4SDimitry Andric FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles, 612480093f4SDimitry Andric CheckerContext &C, ExplodedNode *Pred) const { 613480093f4SDimitry Andric ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred); 614480093f4SDimitry Andric for (SymbolRef LeakedHandle : LeakedHandles) { 615480093f4SDimitry Andric reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType, 616480093f4SDimitry Andric "Potential leak of handle"); 617480093f4SDimitry Andric } 618480093f4SDimitry Andric return ErrNode; 619480093f4SDimitry Andric } 620480093f4SDimitry Andric 621480093f4SDimitry Andric void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym, 622480093f4SDimitry Andric const SourceRange &Range, 623480093f4SDimitry Andric CheckerContext &C) const { 624480093f4SDimitry Andric ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); 625480093f4SDimitry Andric reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType, 626480093f4SDimitry Andric "Releasing a previously released handle"); 627480093f4SDimitry Andric } 628480093f4SDimitry Andric 629e8d8bef9SDimitry Andric void FuchsiaHandleChecker::reportUnownedRelease(SymbolRef HandleSym, 630e8d8bef9SDimitry Andric const SourceRange &Range, 631e8d8bef9SDimitry Andric CheckerContext &C) const { 632e8d8bef9SDimitry Andric ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); 633e8d8bef9SDimitry Andric reportBug(HandleSym, ErrNode, C, &Range, ReleaseUnownedBugType, 634e8d8bef9SDimitry Andric "Releasing an unowned handle"); 635e8d8bef9SDimitry Andric } 636e8d8bef9SDimitry Andric 637480093f4SDimitry Andric void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym, 638480093f4SDimitry Andric const SourceRange &Range, 639480093f4SDimitry Andric CheckerContext &C) const { 640480093f4SDimitry Andric ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); 641480093f4SDimitry Andric reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType, 642480093f4SDimitry Andric "Using a previously released handle"); 643480093f4SDimitry Andric } 644480093f4SDimitry Andric 645480093f4SDimitry Andric void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, 646480093f4SDimitry Andric CheckerContext &C, 647480093f4SDimitry Andric const SourceRange *Range, 648480093f4SDimitry Andric const BugType &Type, StringRef Msg) const { 649480093f4SDimitry Andric if (!ErrorNode) 650480093f4SDimitry Andric return; 651480093f4SDimitry Andric 652480093f4SDimitry Andric std::unique_ptr<PathSensitiveBugReport> R; 653480093f4SDimitry Andric if (Type.isSuppressOnSink()) { 654480093f4SDimitry Andric const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C); 655480093f4SDimitry Andric if (AcquireNode) { 656480093f4SDimitry Andric PathDiagnosticLocation LocUsedForUniqueing = 657480093f4SDimitry Andric PathDiagnosticLocation::createBegin( 658480093f4SDimitry Andric AcquireNode->getStmtForDiagnostics(), C.getSourceManager(), 659480093f4SDimitry Andric AcquireNode->getLocationContext()); 660480093f4SDimitry Andric 661480093f4SDimitry Andric R = std::make_unique<PathSensitiveBugReport>( 662480093f4SDimitry Andric Type, Msg, ErrorNode, LocUsedForUniqueing, 663480093f4SDimitry Andric AcquireNode->getLocationContext()->getDecl()); 664480093f4SDimitry Andric } 665480093f4SDimitry Andric } 666480093f4SDimitry Andric if (!R) 667480093f4SDimitry Andric R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode); 668480093f4SDimitry Andric if (Range) 669480093f4SDimitry Andric R->addRange(*Range); 670480093f4SDimitry Andric R->markInteresting(Sym); 671480093f4SDimitry Andric C.emitReport(std::move(R)); 672480093f4SDimitry Andric } 673480093f4SDimitry Andric 674480093f4SDimitry Andric void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) { 675480093f4SDimitry Andric mgr.registerChecker<FuchsiaHandleChecker>(); 676480093f4SDimitry Andric } 677480093f4SDimitry Andric 6785ffd83dbSDimitry Andric bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) { 679480093f4SDimitry Andric return true; 680480093f4SDimitry Andric } 681480093f4SDimitry Andric 682480093f4SDimitry Andric void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State, 683480093f4SDimitry Andric const char *NL, const char *Sep) const { 684480093f4SDimitry Andric 685480093f4SDimitry Andric HStateMapTy StateMap = State->get<HStateMap>(); 686480093f4SDimitry Andric 687480093f4SDimitry Andric if (!StateMap.isEmpty()) { 688480093f4SDimitry Andric Out << Sep << "FuchsiaHandleChecker :" << NL; 689480093f4SDimitry Andric for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E; 690480093f4SDimitry Andric ++I) { 691480093f4SDimitry Andric I.getKey()->dumpToStream(Out); 692480093f4SDimitry Andric Out << " : "; 693480093f4SDimitry Andric I.getData().dump(Out); 694480093f4SDimitry Andric Out << NL; 695480093f4SDimitry Andric } 696480093f4SDimitry Andric } 697480093f4SDimitry Andric } 698