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" 104480093f4SDimitry Andric 105480093f4SDimitry Andric using namespace clang; 106480093f4SDimitry Andric using namespace ento; 107480093f4SDimitry Andric 108480093f4SDimitry Andric namespace { 109480093f4SDimitry Andric 110480093f4SDimitry Andric static const StringRef HandleTypeName = "zx_handle_t"; 111480093f4SDimitry Andric static const StringRef ErrorTypeName = "zx_status_t"; 112480093f4SDimitry Andric 113480093f4SDimitry Andric class HandleState { 114480093f4SDimitry Andric private: 115e8d8bef9SDimitry Andric enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K; 116480093f4SDimitry Andric SymbolRef ErrorSym; 117480093f4SDimitry Andric HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {} 118480093f4SDimitry Andric 119480093f4SDimitry Andric public: 120480093f4SDimitry Andric bool operator==(const HandleState &Other) const { 121480093f4SDimitry Andric return K == Other.K && ErrorSym == Other.ErrorSym; 122480093f4SDimitry Andric } 123480093f4SDimitry Andric bool isAllocated() const { return K == Kind::Allocated; } 124480093f4SDimitry Andric bool maybeAllocated() const { return K == Kind::MaybeAllocated; } 125480093f4SDimitry Andric bool isReleased() const { return K == Kind::Released; } 126480093f4SDimitry Andric bool isEscaped() const { return K == Kind::Escaped; } 127e8d8bef9SDimitry Andric bool isUnowned() const { return K == Kind::Unowned; } 128480093f4SDimitry Andric 129480093f4SDimitry Andric static HandleState getMaybeAllocated(SymbolRef ErrorSym) { 130480093f4SDimitry Andric return HandleState(Kind::MaybeAllocated, ErrorSym); 131480093f4SDimitry Andric } 132480093f4SDimitry Andric static HandleState getAllocated(ProgramStateRef State, HandleState S) { 133480093f4SDimitry Andric assert(S.maybeAllocated()); 134480093f4SDimitry Andric assert(State->getConstraintManager() 135480093f4SDimitry Andric .isNull(State, S.getErrorSym()) 136480093f4SDimitry Andric .isConstrained()); 137480093f4SDimitry Andric return HandleState(Kind::Allocated, nullptr); 138480093f4SDimitry Andric } 139480093f4SDimitry Andric static HandleState getReleased() { 140480093f4SDimitry Andric return HandleState(Kind::Released, nullptr); 141480093f4SDimitry Andric } 142480093f4SDimitry Andric static HandleState getEscaped() { 143480093f4SDimitry Andric return HandleState(Kind::Escaped, nullptr); 144480093f4SDimitry Andric } 145e8d8bef9SDimitry Andric static HandleState getUnowned() { 146e8d8bef9SDimitry Andric return HandleState(Kind::Unowned, nullptr); 147e8d8bef9SDimitry Andric } 148480093f4SDimitry Andric 149480093f4SDimitry Andric SymbolRef getErrorSym() const { return ErrorSym; } 150480093f4SDimitry Andric 151480093f4SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const { 152480093f4SDimitry Andric ID.AddInteger(static_cast<int>(K)); 153480093f4SDimitry Andric ID.AddPointer(ErrorSym); 154480093f4SDimitry Andric } 155480093f4SDimitry Andric 156480093f4SDimitry Andric LLVM_DUMP_METHOD void dump(raw_ostream &OS) const { 157480093f4SDimitry Andric switch (K) { 158480093f4SDimitry Andric #define CASE(ID) \ 159480093f4SDimitry Andric case ID: \ 160480093f4SDimitry Andric OS << #ID; \ 161480093f4SDimitry Andric break; 162480093f4SDimitry Andric CASE(Kind::MaybeAllocated) 163480093f4SDimitry Andric CASE(Kind::Allocated) 164480093f4SDimitry Andric CASE(Kind::Released) 165480093f4SDimitry Andric CASE(Kind::Escaped) 166e8d8bef9SDimitry Andric CASE(Kind::Unowned) 167480093f4SDimitry Andric } 1685ffd83dbSDimitry Andric if (ErrorSym) { 1695ffd83dbSDimitry Andric OS << " ErrorSym: "; 1705ffd83dbSDimitry Andric ErrorSym->dumpToStream(OS); 1715ffd83dbSDimitry Andric } 172480093f4SDimitry Andric } 173480093f4SDimitry Andric 174480093f4SDimitry Andric LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); } 175480093f4SDimitry Andric }; 176480093f4SDimitry Andric 177480093f4SDimitry Andric template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) { 178480093f4SDimitry Andric return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia"; 179480093f4SDimitry Andric } 180480093f4SDimitry Andric 181e8d8bef9SDimitry Andric template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) { 182e8d8bef9SDimitry Andric return D->hasAttr<Attr>() && 183e8d8bef9SDimitry Andric D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned"; 184e8d8bef9SDimitry Andric } 185e8d8bef9SDimitry Andric 186480093f4SDimitry Andric class FuchsiaHandleChecker 187480093f4SDimitry Andric : public Checker<check::PostCall, check::PreCall, check::DeadSymbols, 188480093f4SDimitry Andric check::PointerEscape, eval::Assume> { 189480093f4SDimitry Andric BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error", 190480093f4SDimitry Andric /*SuppressOnSink=*/true}; 191480093f4SDimitry Andric BugType DoubleReleaseBugType{this, "Fuchsia handle double release", 192480093f4SDimitry Andric "Fuchsia Handle Error"}; 193480093f4SDimitry Andric BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release", 194480093f4SDimitry Andric "Fuchsia Handle Error"}; 195e8d8bef9SDimitry Andric BugType ReleaseUnownedBugType{ 196e8d8bef9SDimitry Andric this, "Fuchsia handle release of unowned handle", "Fuchsia Handle Error"}; 197480093f4SDimitry Andric 198480093f4SDimitry Andric public: 199480093f4SDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 200480093f4SDimitry Andric void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 201480093f4SDimitry Andric void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 202480093f4SDimitry Andric ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, 203480093f4SDimitry Andric bool Assumption) const; 204480093f4SDimitry Andric ProgramStateRef checkPointerEscape(ProgramStateRef State, 205480093f4SDimitry Andric const InvalidatedSymbols &Escaped, 206480093f4SDimitry Andric const CallEvent *Call, 207480093f4SDimitry Andric PointerEscapeKind Kind) const; 208480093f4SDimitry Andric 209480093f4SDimitry Andric ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles, 210480093f4SDimitry Andric CheckerContext &C, ExplodedNode *Pred) const; 211480093f4SDimitry Andric 212480093f4SDimitry Andric void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range, 213480093f4SDimitry Andric CheckerContext &C) const; 214480093f4SDimitry Andric 215e8d8bef9SDimitry Andric void reportUnownedRelease(SymbolRef HandleSym, const SourceRange &Range, 216e8d8bef9SDimitry Andric CheckerContext &C) const; 217e8d8bef9SDimitry Andric 218480093f4SDimitry Andric void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range, 219480093f4SDimitry Andric CheckerContext &C) const; 220480093f4SDimitry Andric 221480093f4SDimitry Andric void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C, 222480093f4SDimitry Andric const SourceRange *Range, const BugType &Type, 223480093f4SDimitry Andric StringRef Msg) const; 224480093f4SDimitry Andric 225480093f4SDimitry Andric void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 226480093f4SDimitry Andric const char *Sep) const override; 227480093f4SDimitry Andric }; 228480093f4SDimitry Andric } // end anonymous namespace 229480093f4SDimitry Andric 230480093f4SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState) 231480093f4SDimitry Andric 232480093f4SDimitry Andric static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym, 233480093f4SDimitry Andric CheckerContext &Ctx) { 234480093f4SDimitry Andric ProgramStateRef State = N->getState(); 235480093f4SDimitry Andric // When bug type is handle leak, exploded node N does not have state info for 236480093f4SDimitry Andric // leaking handle. Get the predecessor of N instead. 237480093f4SDimitry Andric if (!State->get<HStateMap>(Sym)) 238480093f4SDimitry Andric N = N->getFirstPred(); 239480093f4SDimitry Andric 240480093f4SDimitry Andric const ExplodedNode *Pred = N; 241480093f4SDimitry Andric while (N) { 242480093f4SDimitry Andric State = N->getState(); 243480093f4SDimitry Andric if (!State->get<HStateMap>(Sym)) { 244480093f4SDimitry Andric const HandleState *HState = Pred->getState()->get<HStateMap>(Sym); 245480093f4SDimitry Andric if (HState && (HState->isAllocated() || HState->maybeAllocated())) 246480093f4SDimitry Andric return N; 247480093f4SDimitry Andric } 248480093f4SDimitry Andric Pred = N; 249480093f4SDimitry Andric N = N->getFirstPred(); 250480093f4SDimitry Andric } 251480093f4SDimitry Andric return nullptr; 252480093f4SDimitry Andric } 253480093f4SDimitry Andric 254e8d8bef9SDimitry Andric namespace { 255e8d8bef9SDimitry Andric class FuchsiaHandleSymbolVisitor final : public SymbolVisitor { 256e8d8bef9SDimitry Andric public: 257e8d8bef9SDimitry Andric FuchsiaHandleSymbolVisitor(ProgramStateRef State) : State(std::move(State)) {} 258e8d8bef9SDimitry Andric ProgramStateRef getState() const { return State; } 259e8d8bef9SDimitry Andric 260e8d8bef9SDimitry Andric bool VisitSymbol(SymbolRef S) override { 261e8d8bef9SDimitry Andric if (const auto *HandleType = S->getType()->getAs<TypedefType>()) 262e8d8bef9SDimitry Andric if (HandleType->getDecl()->getName() == HandleTypeName) 263e8d8bef9SDimitry Andric Symbols.push_back(S); 264e8d8bef9SDimitry Andric return true; 265e8d8bef9SDimitry Andric } 266e8d8bef9SDimitry Andric 267e8d8bef9SDimitry Andric SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; } 268e8d8bef9SDimitry Andric 269e8d8bef9SDimitry Andric private: 270e8d8bef9SDimitry Andric SmallVector<SymbolRef, 1024> Symbols; 271e8d8bef9SDimitry Andric ProgramStateRef State; 272e8d8bef9SDimitry Andric }; 273e8d8bef9SDimitry Andric } // end anonymous namespace 274e8d8bef9SDimitry Andric 275e8d8bef9SDimitry Andric /// Returns the symbols extracted from the argument or empty vector if it cannot 276e8d8bef9SDimitry Andric /// be found. It is unlikely to have over 1024 symbols in one argument. 277e8d8bef9SDimitry Andric static SmallVector<SymbolRef, 1024> 278e8d8bef9SDimitry Andric getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) { 279480093f4SDimitry Andric int PtrToHandleLevel = 0; 280480093f4SDimitry Andric while (QT->isAnyPointerType() || QT->isReferenceType()) { 281480093f4SDimitry Andric ++PtrToHandleLevel; 282480093f4SDimitry Andric QT = QT->getPointeeType(); 283480093f4SDimitry Andric } 284e8d8bef9SDimitry Andric if (QT->isStructureType()) { 285e8d8bef9SDimitry Andric // If we see a structure, see if there is any handle referenced by the 286e8d8bef9SDimitry Andric // structure. 287e8d8bef9SDimitry Andric FuchsiaHandleSymbolVisitor Visitor(State); 288e8d8bef9SDimitry Andric State->scanReachableSymbols(Arg, Visitor); 289e8d8bef9SDimitry Andric return Visitor.GetSymbols(); 290e8d8bef9SDimitry Andric } 291480093f4SDimitry Andric if (const auto *HandleType = QT->getAs<TypedefType>()) { 292480093f4SDimitry Andric if (HandleType->getDecl()->getName() != HandleTypeName) 293e8d8bef9SDimitry Andric return {}; 294e8d8bef9SDimitry Andric if (PtrToHandleLevel > 1) 295480093f4SDimitry Andric // Not supported yet. 296e8d8bef9SDimitry Andric return {}; 297480093f4SDimitry Andric 298480093f4SDimitry Andric if (PtrToHandleLevel == 0) { 299e8d8bef9SDimitry Andric SymbolRef Sym = Arg.getAsSymbol(); 300e8d8bef9SDimitry Andric if (Sym) { 301e8d8bef9SDimitry Andric return {Sym}; 302e8d8bef9SDimitry Andric } else { 303e8d8bef9SDimitry Andric return {}; 304e8d8bef9SDimitry Andric } 305480093f4SDimitry Andric } else { 306480093f4SDimitry Andric assert(PtrToHandleLevel == 1); 307e8d8bef9SDimitry Andric if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) { 308e8d8bef9SDimitry Andric SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol(); 309e8d8bef9SDimitry Andric if (Sym) { 310e8d8bef9SDimitry Andric return {Sym}; 311e8d8bef9SDimitry Andric } else { 312e8d8bef9SDimitry Andric return {}; 313480093f4SDimitry Andric } 314480093f4SDimitry Andric } 315e8d8bef9SDimitry Andric } 316e8d8bef9SDimitry Andric } 317e8d8bef9SDimitry Andric return {}; 318480093f4SDimitry Andric } 319480093f4SDimitry Andric 320480093f4SDimitry Andric void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call, 321480093f4SDimitry Andric CheckerContext &C) const { 322480093f4SDimitry Andric ProgramStateRef State = C.getState(); 323480093f4SDimitry Andric const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 324480093f4SDimitry Andric if (!FuncDecl) { 325480093f4SDimitry Andric // Unknown call, escape by value handles. They are not covered by 326480093f4SDimitry Andric // PointerEscape callback. 327480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 328480093f4SDimitry Andric if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol()) 329480093f4SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getEscaped()); 330480093f4SDimitry Andric } 331480093f4SDimitry Andric C.addTransition(State); 332480093f4SDimitry Andric return; 333480093f4SDimitry Andric } 334480093f4SDimitry Andric 335480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 336480093f4SDimitry Andric if (Arg >= FuncDecl->getNumParams()) 337480093f4SDimitry Andric break; 338480093f4SDimitry Andric const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 339e8d8bef9SDimitry Andric SmallVector<SymbolRef, 1024> Handles = 340e8d8bef9SDimitry Andric getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State); 341480093f4SDimitry Andric 342480093f4SDimitry Andric // Handled in checkPostCall. 343480093f4SDimitry Andric if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) || 344480093f4SDimitry Andric hasFuchsiaAttr<AcquireHandleAttr>(PVD)) 345480093f4SDimitry Andric continue; 346480093f4SDimitry Andric 347e8d8bef9SDimitry Andric for (SymbolRef Handle : Handles) { 348480093f4SDimitry Andric const HandleState *HState = State->get<HStateMap>(Handle); 349480093f4SDimitry Andric if (!HState || HState->isEscaped()) 350480093f4SDimitry Andric continue; 351480093f4SDimitry Andric 352e8d8bef9SDimitry Andric if (hasFuchsiaAttr<UseHandleAttr>(PVD) || 353e8d8bef9SDimitry Andric PVD->getType()->isIntegerType()) { 354480093f4SDimitry Andric if (HState->isReleased()) { 355480093f4SDimitry Andric reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C); 356480093f4SDimitry Andric return; 357480093f4SDimitry Andric } 358480093f4SDimitry Andric } 359480093f4SDimitry Andric } 360480093f4SDimitry Andric } 361480093f4SDimitry Andric C.addTransition(State); 362480093f4SDimitry Andric } 363480093f4SDimitry Andric 364480093f4SDimitry Andric void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, 365480093f4SDimitry Andric CheckerContext &C) const { 366480093f4SDimitry Andric const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 367480093f4SDimitry Andric if (!FuncDecl) 368480093f4SDimitry Andric return; 369480093f4SDimitry Andric 370e8d8bef9SDimitry Andric // If we analyzed the function body, then ignore the annotations. 371e8d8bef9SDimitry Andric if (C.wasInlined) 372e8d8bef9SDimitry Andric return; 373e8d8bef9SDimitry Andric 374480093f4SDimitry Andric ProgramStateRef State = C.getState(); 375480093f4SDimitry Andric 376480093f4SDimitry Andric std::vector<std::function<std::string(BugReport & BR)>> Notes; 377480093f4SDimitry Andric SymbolRef ResultSymbol = nullptr; 378480093f4SDimitry Andric if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>()) 379480093f4SDimitry Andric if (TypeDefTy->getDecl()->getName() == ErrorTypeName) 380480093f4SDimitry Andric ResultSymbol = Call.getReturnValue().getAsSymbol(); 381480093f4SDimitry Andric 382480093f4SDimitry Andric // Function returns an open handle. 383480093f4SDimitry Andric if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) { 384480093f4SDimitry Andric SymbolRef RetSym = Call.getReturnValue().getAsSymbol(); 3855ffd83dbSDimitry Andric Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string { 3865ffd83dbSDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 3875ffd83dbSDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) { 3885ffd83dbSDimitry Andric std::string SBuf; 3895ffd83dbSDimitry Andric llvm::raw_string_ostream OS(SBuf); 390e8d8bef9SDimitry Andric OS << "Function '" << FuncDecl->getDeclName() 3915ffd83dbSDimitry Andric << "' returns an open handle"; 392*0eae32dcSDimitry Andric return SBuf; 3935ffd83dbSDimitry Andric } else 3945ffd83dbSDimitry Andric return ""; 3955ffd83dbSDimitry Andric }); 396480093f4SDimitry Andric State = 397480093f4SDimitry Andric State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr)); 398e8d8bef9SDimitry Andric } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) { 399e8d8bef9SDimitry Andric // Function returns an unowned handle 400e8d8bef9SDimitry Andric SymbolRef RetSym = Call.getReturnValue().getAsSymbol(); 401e8d8bef9SDimitry Andric Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string { 402e8d8bef9SDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 403e8d8bef9SDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) { 404e8d8bef9SDimitry Andric std::string SBuf; 405e8d8bef9SDimitry Andric llvm::raw_string_ostream OS(SBuf); 406e8d8bef9SDimitry Andric OS << "Function '" << FuncDecl->getDeclName() 407e8d8bef9SDimitry Andric << "' returns an unowned handle"; 408*0eae32dcSDimitry Andric return SBuf; 409e8d8bef9SDimitry Andric } else 410e8d8bef9SDimitry Andric return ""; 411e8d8bef9SDimitry Andric }); 412e8d8bef9SDimitry Andric State = State->set<HStateMap>(RetSym, HandleState::getUnowned()); 413480093f4SDimitry Andric } 414480093f4SDimitry Andric 415480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 416480093f4SDimitry Andric if (Arg >= FuncDecl->getNumParams()) 417480093f4SDimitry Andric break; 418480093f4SDimitry Andric const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 4195ffd83dbSDimitry Andric unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1; 420e8d8bef9SDimitry Andric SmallVector<SymbolRef, 1024> Handles = 421e8d8bef9SDimitry Andric getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State); 422480093f4SDimitry Andric 423e8d8bef9SDimitry Andric for (SymbolRef Handle : Handles) { 424480093f4SDimitry Andric const HandleState *HState = State->get<HStateMap>(Handle); 425480093f4SDimitry Andric if (HState && HState->isEscaped()) 426480093f4SDimitry Andric continue; 427480093f4SDimitry Andric if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) { 428480093f4SDimitry Andric if (HState && HState->isReleased()) { 429480093f4SDimitry Andric reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C); 430480093f4SDimitry Andric return; 431e8d8bef9SDimitry Andric } else if (HState && HState->isUnowned()) { 432e8d8bef9SDimitry Andric reportUnownedRelease(Handle, Call.getArgSourceRange(Arg), C); 433e8d8bef9SDimitry Andric return; 434480093f4SDimitry Andric } else { 4355ffd83dbSDimitry Andric Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { 436480093f4SDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 437480093f4SDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { 4385ffd83dbSDimitry Andric std::string SBuf; 4395ffd83dbSDimitry Andric llvm::raw_string_ostream OS(SBuf); 4405ffd83dbSDimitry Andric OS << "Handle released through " << ParamDiagIdx 4415ffd83dbSDimitry Andric << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; 442*0eae32dcSDimitry Andric return SBuf; 443480093f4SDimitry Andric } else 444480093f4SDimitry Andric return ""; 445480093f4SDimitry Andric }); 446480093f4SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getReleased()); 447480093f4SDimitry Andric } 448480093f4SDimitry Andric } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) { 4495ffd83dbSDimitry Andric Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { 450480093f4SDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 451480093f4SDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { 4525ffd83dbSDimitry Andric std::string SBuf; 4535ffd83dbSDimitry Andric llvm::raw_string_ostream OS(SBuf); 4545ffd83dbSDimitry Andric OS << "Handle allocated through " << ParamDiagIdx 4555ffd83dbSDimitry Andric << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; 456*0eae32dcSDimitry Andric return SBuf; 457480093f4SDimitry Andric } else 458480093f4SDimitry Andric return ""; 459480093f4SDimitry Andric }); 460480093f4SDimitry Andric State = State->set<HStateMap>( 461480093f4SDimitry Andric Handle, HandleState::getMaybeAllocated(ResultSymbol)); 462e8d8bef9SDimitry Andric } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)) { 463e8d8bef9SDimitry Andric Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { 464e8d8bef9SDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 465e8d8bef9SDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { 466e8d8bef9SDimitry Andric std::string SBuf; 467e8d8bef9SDimitry Andric llvm::raw_string_ostream OS(SBuf); 468e8d8bef9SDimitry Andric OS << "Unowned handle allocated through " << ParamDiagIdx 469e8d8bef9SDimitry Andric << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; 470*0eae32dcSDimitry Andric return SBuf; 471e8d8bef9SDimitry Andric } else 472e8d8bef9SDimitry Andric return ""; 473e8d8bef9SDimitry Andric }); 474e8d8bef9SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getUnowned()); 475e8d8bef9SDimitry Andric } else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) && 476e8d8bef9SDimitry Andric PVD->getType()->isIntegerType()) { 477e8d8bef9SDimitry Andric // Working around integer by-value escapes. 478e8d8bef9SDimitry Andric // The by-value escape would not be captured in checkPointerEscape. 479e8d8bef9SDimitry Andric // If the function was not analyzed (otherwise wasInlined should be 480e8d8bef9SDimitry Andric // true) and there is no annotation on the handle, we assume the handle 481e8d8bef9SDimitry Andric // is escaped. 482e8d8bef9SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getEscaped()); 483e8d8bef9SDimitry Andric } 484480093f4SDimitry Andric } 485480093f4SDimitry Andric } 486480093f4SDimitry Andric const NoteTag *T = nullptr; 487480093f4SDimitry Andric if (!Notes.empty()) { 4885ffd83dbSDimitry Andric T = C.getNoteTag([this, Notes{std::move(Notes)}]( 4895ffd83dbSDimitry Andric PathSensitiveBugReport &BR) -> std::string { 490480093f4SDimitry Andric if (&BR.getBugType() != &UseAfterReleaseBugType && 491480093f4SDimitry Andric &BR.getBugType() != &LeakBugType && 492e8d8bef9SDimitry Andric &BR.getBugType() != &DoubleReleaseBugType && 493e8d8bef9SDimitry Andric &BR.getBugType() != &ReleaseUnownedBugType) 494480093f4SDimitry Andric return ""; 495480093f4SDimitry Andric for (auto &Note : Notes) { 496480093f4SDimitry Andric std::string Text = Note(BR); 497480093f4SDimitry Andric if (!Text.empty()) 498480093f4SDimitry Andric return Text; 499480093f4SDimitry Andric } 500480093f4SDimitry Andric return ""; 501480093f4SDimitry Andric }); 502480093f4SDimitry Andric } 503480093f4SDimitry Andric C.addTransition(State, T); 504480093f4SDimitry Andric } 505480093f4SDimitry Andric 506480093f4SDimitry Andric void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper, 507480093f4SDimitry Andric CheckerContext &C) const { 508480093f4SDimitry Andric ProgramStateRef State = C.getState(); 509480093f4SDimitry Andric SmallVector<SymbolRef, 2> LeakedSyms; 510480093f4SDimitry Andric HStateMapTy TrackedHandles = State->get<HStateMap>(); 511480093f4SDimitry Andric for (auto &CurItem : TrackedHandles) { 5125ffd83dbSDimitry Andric SymbolRef ErrorSym = CurItem.second.getErrorSym(); 5135ffd83dbSDimitry Andric // Keeping zombie handle symbols. In case the error symbol is dying later 5145ffd83dbSDimitry Andric // than the handle symbol we might produce spurious leak warnings (in case 5155ffd83dbSDimitry Andric // we find out later from the status code that the handle allocation failed 5165ffd83dbSDimitry Andric // in the first place). 5175ffd83dbSDimitry Andric if (!SymReaper.isDead(CurItem.first) || 5185ffd83dbSDimitry Andric (ErrorSym && !SymReaper.isDead(ErrorSym))) 519480093f4SDimitry Andric continue; 520480093f4SDimitry Andric if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated()) 521480093f4SDimitry Andric LeakedSyms.push_back(CurItem.first); 522480093f4SDimitry Andric State = State->remove<HStateMap>(CurItem.first); 523480093f4SDimitry Andric } 524480093f4SDimitry Andric 525480093f4SDimitry Andric ExplodedNode *N = C.getPredecessor(); 526480093f4SDimitry Andric if (!LeakedSyms.empty()) 527480093f4SDimitry Andric N = reportLeaks(LeakedSyms, C, N); 528480093f4SDimitry Andric 529480093f4SDimitry Andric C.addTransition(State, N); 530480093f4SDimitry Andric } 531480093f4SDimitry Andric 532480093f4SDimitry Andric // Acquiring a handle is not always successful. In Fuchsia most functions 533480093f4SDimitry Andric // return a status code that determines the status of the handle. 534480093f4SDimitry Andric // When we split the path based on this status code we know that on one 535480093f4SDimitry Andric // path we do have the handle and on the other path the acquire failed. 536480093f4SDimitry Andric // This method helps avoiding false positive leak warnings on paths where 537480093f4SDimitry Andric // the function failed. 538480093f4SDimitry Andric // Moreover, when a handle is known to be zero (the invalid handle), 539480093f4SDimitry Andric // we no longer can follow the symbol on the path, becaue the constant 540480093f4SDimitry Andric // zero will be used instead of the symbol. We also do not need to release 541480093f4SDimitry Andric // an invalid handle, so we remove the corresponding symbol from the state. 542480093f4SDimitry Andric ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State, 543480093f4SDimitry Andric SVal Cond, 544480093f4SDimitry Andric bool Assumption) const { 545480093f4SDimitry Andric // TODO: add notes about successes/fails for APIs. 546480093f4SDimitry Andric ConstraintManager &Cmr = State->getConstraintManager(); 547480093f4SDimitry Andric HStateMapTy TrackedHandles = State->get<HStateMap>(); 548480093f4SDimitry Andric for (auto &CurItem : TrackedHandles) { 549480093f4SDimitry Andric ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first); 550480093f4SDimitry Andric if (HandleVal.isConstrainedTrue()) { 551480093f4SDimitry Andric // The handle is invalid. We can no longer follow the symbol on this path. 552480093f4SDimitry Andric State = State->remove<HStateMap>(CurItem.first); 553480093f4SDimitry Andric } 554480093f4SDimitry Andric SymbolRef ErrorSym = CurItem.second.getErrorSym(); 555480093f4SDimitry Andric if (!ErrorSym) 556480093f4SDimitry Andric continue; 557480093f4SDimitry Andric ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym); 558480093f4SDimitry Andric if (ErrorVal.isConstrainedTrue()) { 559480093f4SDimitry Andric // Allocation succeeded. 560480093f4SDimitry Andric if (CurItem.second.maybeAllocated()) 561480093f4SDimitry Andric State = State->set<HStateMap>( 562480093f4SDimitry Andric CurItem.first, HandleState::getAllocated(State, CurItem.second)); 563480093f4SDimitry Andric } else if (ErrorVal.isConstrainedFalse()) { 564480093f4SDimitry Andric // Allocation failed. 565480093f4SDimitry Andric if (CurItem.second.maybeAllocated()) 566480093f4SDimitry Andric State = State->remove<HStateMap>(CurItem.first); 567480093f4SDimitry Andric } 568480093f4SDimitry Andric } 569480093f4SDimitry Andric return State; 570480093f4SDimitry Andric } 571480093f4SDimitry Andric 572480093f4SDimitry Andric ProgramStateRef FuchsiaHandleChecker::checkPointerEscape( 573480093f4SDimitry Andric ProgramStateRef State, const InvalidatedSymbols &Escaped, 574480093f4SDimitry Andric const CallEvent *Call, PointerEscapeKind Kind) const { 575480093f4SDimitry Andric const FunctionDecl *FuncDecl = 576480093f4SDimitry Andric Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr; 577480093f4SDimitry Andric 578480093f4SDimitry Andric llvm::DenseSet<SymbolRef> UnEscaped; 579480093f4SDimitry Andric // Not all calls should escape our symbols. 580480093f4SDimitry Andric if (FuncDecl && 581480093f4SDimitry Andric (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall || 582480093f4SDimitry Andric Kind == PSK_EscapeOutParameters)) { 583480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) { 584480093f4SDimitry Andric if (Arg >= FuncDecl->getNumParams()) 585480093f4SDimitry Andric break; 586480093f4SDimitry Andric const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 587e8d8bef9SDimitry Andric SmallVector<SymbolRef, 1024> Handles = 588e8d8bef9SDimitry Andric getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Arg), State); 589e8d8bef9SDimitry Andric for (SymbolRef Handle : Handles) { 590480093f4SDimitry Andric if (hasFuchsiaAttr<UseHandleAttr>(PVD) || 591e8d8bef9SDimitry Andric hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) { 592480093f4SDimitry Andric UnEscaped.insert(Handle); 593480093f4SDimitry Andric } 594480093f4SDimitry Andric } 595e8d8bef9SDimitry Andric } 596e8d8bef9SDimitry Andric } 597480093f4SDimitry Andric 598480093f4SDimitry Andric // For out params, we have to deal with derived symbols. See 599480093f4SDimitry Andric // MacOSKeychainAPIChecker for details. 600480093f4SDimitry Andric for (auto I : State->get<HStateMap>()) { 601480093f4SDimitry Andric if (Escaped.count(I.first) && !UnEscaped.count(I.first)) 602480093f4SDimitry Andric State = State->set<HStateMap>(I.first, HandleState::getEscaped()); 603480093f4SDimitry Andric if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) { 604480093f4SDimitry Andric auto ParentSym = SD->getParentSymbol(); 605480093f4SDimitry Andric if (Escaped.count(ParentSym)) 606480093f4SDimitry Andric State = State->set<HStateMap>(I.first, HandleState::getEscaped()); 607480093f4SDimitry Andric } 608480093f4SDimitry Andric } 609480093f4SDimitry Andric 610480093f4SDimitry Andric return State; 611480093f4SDimitry Andric } 612480093f4SDimitry Andric 613480093f4SDimitry Andric ExplodedNode * 614480093f4SDimitry Andric FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles, 615480093f4SDimitry Andric CheckerContext &C, ExplodedNode *Pred) const { 616480093f4SDimitry Andric ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred); 617480093f4SDimitry Andric for (SymbolRef LeakedHandle : LeakedHandles) { 618480093f4SDimitry Andric reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType, 619480093f4SDimitry Andric "Potential leak of handle"); 620480093f4SDimitry Andric } 621480093f4SDimitry Andric return ErrNode; 622480093f4SDimitry Andric } 623480093f4SDimitry Andric 624480093f4SDimitry Andric void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym, 625480093f4SDimitry Andric const SourceRange &Range, 626480093f4SDimitry Andric CheckerContext &C) const { 627480093f4SDimitry Andric ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); 628480093f4SDimitry Andric reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType, 629480093f4SDimitry Andric "Releasing a previously released handle"); 630480093f4SDimitry Andric } 631480093f4SDimitry Andric 632e8d8bef9SDimitry Andric void FuchsiaHandleChecker::reportUnownedRelease(SymbolRef HandleSym, 633e8d8bef9SDimitry Andric const SourceRange &Range, 634e8d8bef9SDimitry Andric CheckerContext &C) const { 635e8d8bef9SDimitry Andric ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); 636e8d8bef9SDimitry Andric reportBug(HandleSym, ErrNode, C, &Range, ReleaseUnownedBugType, 637e8d8bef9SDimitry Andric "Releasing an unowned handle"); 638e8d8bef9SDimitry Andric } 639e8d8bef9SDimitry Andric 640480093f4SDimitry Andric void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym, 641480093f4SDimitry Andric const SourceRange &Range, 642480093f4SDimitry Andric CheckerContext &C) const { 643480093f4SDimitry Andric ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); 644480093f4SDimitry Andric reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType, 645480093f4SDimitry Andric "Using a previously released handle"); 646480093f4SDimitry Andric } 647480093f4SDimitry Andric 648480093f4SDimitry Andric void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, 649480093f4SDimitry Andric CheckerContext &C, 650480093f4SDimitry Andric const SourceRange *Range, 651480093f4SDimitry Andric const BugType &Type, StringRef Msg) const { 652480093f4SDimitry Andric if (!ErrorNode) 653480093f4SDimitry Andric return; 654480093f4SDimitry Andric 655480093f4SDimitry Andric std::unique_ptr<PathSensitiveBugReport> R; 656480093f4SDimitry Andric if (Type.isSuppressOnSink()) { 657480093f4SDimitry Andric const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C); 658480093f4SDimitry Andric if (AcquireNode) { 659480093f4SDimitry Andric PathDiagnosticLocation LocUsedForUniqueing = 660480093f4SDimitry Andric PathDiagnosticLocation::createBegin( 661480093f4SDimitry Andric AcquireNode->getStmtForDiagnostics(), C.getSourceManager(), 662480093f4SDimitry Andric AcquireNode->getLocationContext()); 663480093f4SDimitry Andric 664480093f4SDimitry Andric R = std::make_unique<PathSensitiveBugReport>( 665480093f4SDimitry Andric Type, Msg, ErrorNode, LocUsedForUniqueing, 666480093f4SDimitry Andric AcquireNode->getLocationContext()->getDecl()); 667480093f4SDimitry Andric } 668480093f4SDimitry Andric } 669480093f4SDimitry Andric if (!R) 670480093f4SDimitry Andric R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode); 671480093f4SDimitry Andric if (Range) 672480093f4SDimitry Andric R->addRange(*Range); 673480093f4SDimitry Andric R->markInteresting(Sym); 674480093f4SDimitry Andric C.emitReport(std::move(R)); 675480093f4SDimitry Andric } 676480093f4SDimitry Andric 677480093f4SDimitry Andric void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) { 678480093f4SDimitry Andric mgr.registerChecker<FuchsiaHandleChecker>(); 679480093f4SDimitry Andric } 680480093f4SDimitry Andric 6815ffd83dbSDimitry Andric bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) { 682480093f4SDimitry Andric return true; 683480093f4SDimitry Andric } 684480093f4SDimitry Andric 685480093f4SDimitry Andric void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State, 686480093f4SDimitry Andric const char *NL, const char *Sep) const { 687480093f4SDimitry Andric 688480093f4SDimitry Andric HStateMapTy StateMap = State->get<HStateMap>(); 689480093f4SDimitry Andric 690480093f4SDimitry Andric if (!StateMap.isEmpty()) { 691480093f4SDimitry Andric Out << Sep << "FuchsiaHandleChecker :" << NL; 692480093f4SDimitry Andric for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E; 693480093f4SDimitry Andric ++I) { 694480093f4SDimitry Andric I.getKey()->dumpToStream(Out); 695480093f4SDimitry Andric Out << " : "; 696480093f4SDimitry Andric I.getData().dump(Out); 697480093f4SDimitry Andric Out << NL; 698480093f4SDimitry Andric } 699480093f4SDimitry Andric } 700480093f4SDimitry Andric } 701