1*480093f4SDimitry Andric //=== FuchsiaHandleChecker.cpp - Find handle leaks/double closes -*- C++ -*--=// 2*480093f4SDimitry Andric // 3*480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*480093f4SDimitry Andric // 7*480093f4SDimitry Andric //===----------------------------------------------------------------------===// 8*480093f4SDimitry Andric // 9*480093f4SDimitry Andric // This checker checks if the handle of Fuchsia is properly used according to 10*480093f4SDimitry Andric // following rules. 11*480093f4SDimitry Andric // - If a handle is acquired, it should be released before execution 12*480093f4SDimitry Andric // ends. 13*480093f4SDimitry Andric // - If a handle is released, it should not be released again. 14*480093f4SDimitry Andric // - If a handle is released, it should not be used for other purposes 15*480093f4SDimitry Andric // such as I/O. 16*480093f4SDimitry Andric // 17*480093f4SDimitry Andric // In this checker, each tracked handle is associated with a state. When the 18*480093f4SDimitry Andric // handle variable is passed to different function calls or syscalls, its state 19*480093f4SDimitry Andric // changes. The state changes can be generally represented by following ASCII 20*480093f4SDimitry Andric // Art: 21*480093f4SDimitry Andric // 22*480093f4SDimitry Andric // 23*480093f4SDimitry Andric // +-+---------v-+ +------------+ 24*480093f4SDimitry Andric // acquire_func succeeded | | Escape | | 25*480093f4SDimitry Andric // +-----------------> Allocated +---------> Escaped <--+ 26*480093f4SDimitry Andric // | | | | | | 27*480093f4SDimitry Andric // | +-----+------++ +------------+ | 28*480093f4SDimitry Andric // | | | | 29*480093f4SDimitry Andric // | release_func | +--+ | 30*480093f4SDimitry Andric // | | | handle +--------+ | 31*480093f4SDimitry Andric // | | | dies | | | 32*480093f4SDimitry Andric // | +----v-----+ +---------> Leaked | | 33*480093f4SDimitry Andric // | | | |(REPORT)| | 34*480093f4SDimitry Andric // +----------+--+ | Released | Escape +--------+ | 35*480093f4SDimitry Andric // | | | +---------------------------+ 36*480093f4SDimitry Andric // | Not tracked <--+ +----+---+-+ 37*480093f4SDimitry Andric // | | | | | As argument by value 38*480093f4SDimitry Andric // +------+------+ | release_func | +------+ in function call 39*480093f4SDimitry Andric // | | | | or by reference in 40*480093f4SDimitry Andric // | | | | use_func call 41*480093f4SDimitry Andric // +---------+ +----v-----+ | +-----------+ 42*480093f4SDimitry Andric // acquire_func failed | Double | +-----> Use after | 43*480093f4SDimitry Andric // | released | | released | 44*480093f4SDimitry Andric // | (REPORT) | | (REPORT) | 45*480093f4SDimitry Andric // +----------+ +-----------+ 46*480093f4SDimitry Andric // 47*480093f4SDimitry Andric // acquire_func represents the functions or syscalls that may acquire a handle. 48*480093f4SDimitry Andric // release_func represents the functions or syscalls that may release a handle. 49*480093f4SDimitry Andric // use_func represents the functions or syscall that requires an open handle. 50*480093f4SDimitry Andric // 51*480093f4SDimitry Andric // If a tracked handle dies in "Released" or "Not Tracked" state, we assume it 52*480093f4SDimitry Andric // is properly used. Otherwise a bug and will be reported. 53*480093f4SDimitry Andric // 54*480093f4SDimitry Andric // Note that, the analyzer does not always know for sure if a function failed 55*480093f4SDimitry Andric // or succeeded. In those cases we use the state MaybeAllocated. 56*480093f4SDimitry Andric // Thus, the diagramm above captures the intent, not implementation details. 57*480093f4SDimitry Andric // 58*480093f4SDimitry Andric // Due to the fact that the number of handle related syscalls in Fuchsia 59*480093f4SDimitry Andric // is large, we adopt the annotation attributes to descript syscalls' 60*480093f4SDimitry Andric // operations(acquire/release/use) on handles instead of hardcoding 61*480093f4SDimitry Andric // everything in the checker. 62*480093f4SDimitry Andric // 63*480093f4SDimitry Andric // We use following annotation attributes for handle related syscalls or 64*480093f4SDimitry Andric // functions: 65*480093f4SDimitry Andric // 1. __attribute__((acquire_handle("Fuchsia"))) |handle will be acquired 66*480093f4SDimitry Andric // 2. __attribute__((release_handle("Fuchsia"))) |handle will be released 67*480093f4SDimitry Andric // 3. __attribute__((use_handle("Fuchsia"))) |handle will not transit to 68*480093f4SDimitry Andric // escaped state, it also needs to be open. 69*480093f4SDimitry Andric // 70*480093f4SDimitry Andric // For example, an annotated syscall: 71*480093f4SDimitry Andric // zx_status_t zx_channel_create( 72*480093f4SDimitry Andric // uint32_t options, 73*480093f4SDimitry Andric // zx_handle_t* out0 __attribute__((acquire_handle("Fuchsia"))) , 74*480093f4SDimitry Andric // zx_handle_t* out1 __attribute__((acquire_handle("Fuchsia")))); 75*480093f4SDimitry Andric // denotes a syscall which will acquire two handles and save them to 'out0' and 76*480093f4SDimitry Andric // 'out1' when succeeded. 77*480093f4SDimitry Andric // 78*480093f4SDimitry Andric //===----------------------------------------------------------------------===// 79*480093f4SDimitry Andric 80*480093f4SDimitry Andric #include "clang/AST/Attr.h" 81*480093f4SDimitry Andric #include "clang/AST/Decl.h" 82*480093f4SDimitry Andric #include "clang/AST/Type.h" 83*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 84*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 85*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 86*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 87*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 88*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 89*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" 90*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 91*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 92*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" 93*480093f4SDimitry Andric 94*480093f4SDimitry Andric using namespace clang; 95*480093f4SDimitry Andric using namespace ento; 96*480093f4SDimitry Andric 97*480093f4SDimitry Andric namespace { 98*480093f4SDimitry Andric 99*480093f4SDimitry Andric static const StringRef HandleTypeName = "zx_handle_t"; 100*480093f4SDimitry Andric static const StringRef ErrorTypeName = "zx_status_t"; 101*480093f4SDimitry Andric 102*480093f4SDimitry Andric class HandleState { 103*480093f4SDimitry Andric private: 104*480093f4SDimitry Andric enum class Kind { MaybeAllocated, Allocated, Released, Escaped } K; 105*480093f4SDimitry Andric SymbolRef ErrorSym; 106*480093f4SDimitry Andric HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {} 107*480093f4SDimitry Andric 108*480093f4SDimitry Andric public: 109*480093f4SDimitry Andric bool operator==(const HandleState &Other) const { 110*480093f4SDimitry Andric return K == Other.K && ErrorSym == Other.ErrorSym; 111*480093f4SDimitry Andric } 112*480093f4SDimitry Andric bool isAllocated() const { return K == Kind::Allocated; } 113*480093f4SDimitry Andric bool maybeAllocated() const { return K == Kind::MaybeAllocated; } 114*480093f4SDimitry Andric bool isReleased() const { return K == Kind::Released; } 115*480093f4SDimitry Andric bool isEscaped() const { return K == Kind::Escaped; } 116*480093f4SDimitry Andric 117*480093f4SDimitry Andric static HandleState getMaybeAllocated(SymbolRef ErrorSym) { 118*480093f4SDimitry Andric return HandleState(Kind::MaybeAllocated, ErrorSym); 119*480093f4SDimitry Andric } 120*480093f4SDimitry Andric static HandleState getAllocated(ProgramStateRef State, HandleState S) { 121*480093f4SDimitry Andric assert(S.maybeAllocated()); 122*480093f4SDimitry Andric assert(State->getConstraintManager() 123*480093f4SDimitry Andric .isNull(State, S.getErrorSym()) 124*480093f4SDimitry Andric .isConstrained()); 125*480093f4SDimitry Andric return HandleState(Kind::Allocated, nullptr); 126*480093f4SDimitry Andric } 127*480093f4SDimitry Andric static HandleState getReleased() { 128*480093f4SDimitry Andric return HandleState(Kind::Released, nullptr); 129*480093f4SDimitry Andric } 130*480093f4SDimitry Andric static HandleState getEscaped() { 131*480093f4SDimitry Andric return HandleState(Kind::Escaped, nullptr); 132*480093f4SDimitry Andric } 133*480093f4SDimitry Andric 134*480093f4SDimitry Andric SymbolRef getErrorSym() const { return ErrorSym; } 135*480093f4SDimitry Andric 136*480093f4SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const { 137*480093f4SDimitry Andric ID.AddInteger(static_cast<int>(K)); 138*480093f4SDimitry Andric ID.AddPointer(ErrorSym); 139*480093f4SDimitry Andric } 140*480093f4SDimitry Andric 141*480093f4SDimitry Andric LLVM_DUMP_METHOD void dump(raw_ostream &OS) const { 142*480093f4SDimitry Andric switch (K) { 143*480093f4SDimitry Andric #define CASE(ID) \ 144*480093f4SDimitry Andric case ID: \ 145*480093f4SDimitry Andric OS << #ID; \ 146*480093f4SDimitry Andric break; 147*480093f4SDimitry Andric CASE(Kind::MaybeAllocated) 148*480093f4SDimitry Andric CASE(Kind::Allocated) 149*480093f4SDimitry Andric CASE(Kind::Released) 150*480093f4SDimitry Andric CASE(Kind::Escaped) 151*480093f4SDimitry Andric } 152*480093f4SDimitry Andric } 153*480093f4SDimitry Andric 154*480093f4SDimitry Andric LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); } 155*480093f4SDimitry Andric }; 156*480093f4SDimitry Andric 157*480093f4SDimitry Andric template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) { 158*480093f4SDimitry Andric return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia"; 159*480093f4SDimitry Andric } 160*480093f4SDimitry Andric 161*480093f4SDimitry Andric class FuchsiaHandleChecker 162*480093f4SDimitry Andric : public Checker<check::PostCall, check::PreCall, check::DeadSymbols, 163*480093f4SDimitry Andric check::PointerEscape, eval::Assume> { 164*480093f4SDimitry Andric BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error", 165*480093f4SDimitry Andric /*SuppressOnSink=*/true}; 166*480093f4SDimitry Andric BugType DoubleReleaseBugType{this, "Fuchsia handle double release", 167*480093f4SDimitry Andric "Fuchsia Handle Error"}; 168*480093f4SDimitry Andric BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release", 169*480093f4SDimitry Andric "Fuchsia Handle Error"}; 170*480093f4SDimitry Andric 171*480093f4SDimitry Andric public: 172*480093f4SDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 173*480093f4SDimitry Andric void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 174*480093f4SDimitry Andric void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 175*480093f4SDimitry Andric ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, 176*480093f4SDimitry Andric bool Assumption) const; 177*480093f4SDimitry Andric ProgramStateRef checkPointerEscape(ProgramStateRef State, 178*480093f4SDimitry Andric const InvalidatedSymbols &Escaped, 179*480093f4SDimitry Andric const CallEvent *Call, 180*480093f4SDimitry Andric PointerEscapeKind Kind) const; 181*480093f4SDimitry Andric 182*480093f4SDimitry Andric ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles, 183*480093f4SDimitry Andric CheckerContext &C, ExplodedNode *Pred) const; 184*480093f4SDimitry Andric 185*480093f4SDimitry Andric void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range, 186*480093f4SDimitry Andric CheckerContext &C) const; 187*480093f4SDimitry Andric 188*480093f4SDimitry Andric void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range, 189*480093f4SDimitry Andric CheckerContext &C) const; 190*480093f4SDimitry Andric 191*480093f4SDimitry Andric void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C, 192*480093f4SDimitry Andric const SourceRange *Range, const BugType &Type, 193*480093f4SDimitry Andric StringRef Msg) const; 194*480093f4SDimitry Andric 195*480093f4SDimitry Andric void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 196*480093f4SDimitry Andric const char *Sep) const override; 197*480093f4SDimitry Andric }; 198*480093f4SDimitry Andric } // end anonymous namespace 199*480093f4SDimitry Andric 200*480093f4SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState) 201*480093f4SDimitry Andric 202*480093f4SDimitry Andric static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym, 203*480093f4SDimitry Andric CheckerContext &Ctx) { 204*480093f4SDimitry Andric ProgramStateRef State = N->getState(); 205*480093f4SDimitry Andric // When bug type is handle leak, exploded node N does not have state info for 206*480093f4SDimitry Andric // leaking handle. Get the predecessor of N instead. 207*480093f4SDimitry Andric if (!State->get<HStateMap>(Sym)) 208*480093f4SDimitry Andric N = N->getFirstPred(); 209*480093f4SDimitry Andric 210*480093f4SDimitry Andric const ExplodedNode *Pred = N; 211*480093f4SDimitry Andric while (N) { 212*480093f4SDimitry Andric State = N->getState(); 213*480093f4SDimitry Andric if (!State->get<HStateMap>(Sym)) { 214*480093f4SDimitry Andric const HandleState *HState = Pred->getState()->get<HStateMap>(Sym); 215*480093f4SDimitry Andric if (HState && (HState->isAllocated() || HState->maybeAllocated())) 216*480093f4SDimitry Andric return N; 217*480093f4SDimitry Andric } 218*480093f4SDimitry Andric Pred = N; 219*480093f4SDimitry Andric N = N->getFirstPred(); 220*480093f4SDimitry Andric } 221*480093f4SDimitry Andric return nullptr; 222*480093f4SDimitry Andric } 223*480093f4SDimitry Andric 224*480093f4SDimitry Andric /// Returns the symbols extracted from the argument or null if it cannot be 225*480093f4SDimitry Andric /// found. 226*480093f4SDimitry Andric static SymbolRef getFuchsiaHandleSymbol(QualType QT, SVal Arg, 227*480093f4SDimitry Andric ProgramStateRef State) { 228*480093f4SDimitry Andric int PtrToHandleLevel = 0; 229*480093f4SDimitry Andric while (QT->isAnyPointerType() || QT->isReferenceType()) { 230*480093f4SDimitry Andric ++PtrToHandleLevel; 231*480093f4SDimitry Andric QT = QT->getPointeeType(); 232*480093f4SDimitry Andric } 233*480093f4SDimitry Andric if (const auto *HandleType = QT->getAs<TypedefType>()) { 234*480093f4SDimitry Andric if (HandleType->getDecl()->getName() != HandleTypeName) 235*480093f4SDimitry Andric return nullptr; 236*480093f4SDimitry Andric if (PtrToHandleLevel > 1) { 237*480093f4SDimitry Andric // Not supported yet. 238*480093f4SDimitry Andric return nullptr; 239*480093f4SDimitry Andric } 240*480093f4SDimitry Andric 241*480093f4SDimitry Andric if (PtrToHandleLevel == 0) { 242*480093f4SDimitry Andric return Arg.getAsSymbol(); 243*480093f4SDimitry Andric } else { 244*480093f4SDimitry Andric assert(PtrToHandleLevel == 1); 245*480093f4SDimitry Andric if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) 246*480093f4SDimitry Andric return State->getSVal(*ArgLoc).getAsSymbol(); 247*480093f4SDimitry Andric } 248*480093f4SDimitry Andric } 249*480093f4SDimitry Andric return nullptr; 250*480093f4SDimitry Andric } 251*480093f4SDimitry Andric 252*480093f4SDimitry Andric void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call, 253*480093f4SDimitry Andric CheckerContext &C) const { 254*480093f4SDimitry Andric ProgramStateRef State = C.getState(); 255*480093f4SDimitry Andric const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 256*480093f4SDimitry Andric if (!FuncDecl) { 257*480093f4SDimitry Andric // Unknown call, escape by value handles. They are not covered by 258*480093f4SDimitry Andric // PointerEscape callback. 259*480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 260*480093f4SDimitry Andric if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol()) 261*480093f4SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getEscaped()); 262*480093f4SDimitry Andric } 263*480093f4SDimitry Andric C.addTransition(State); 264*480093f4SDimitry Andric return; 265*480093f4SDimitry Andric } 266*480093f4SDimitry Andric 267*480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 268*480093f4SDimitry Andric if (Arg >= FuncDecl->getNumParams()) 269*480093f4SDimitry Andric break; 270*480093f4SDimitry Andric const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 271*480093f4SDimitry Andric SymbolRef Handle = 272*480093f4SDimitry Andric getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State); 273*480093f4SDimitry Andric if (!Handle) 274*480093f4SDimitry Andric continue; 275*480093f4SDimitry Andric 276*480093f4SDimitry Andric // Handled in checkPostCall. 277*480093f4SDimitry Andric if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) || 278*480093f4SDimitry Andric hasFuchsiaAttr<AcquireHandleAttr>(PVD)) 279*480093f4SDimitry Andric continue; 280*480093f4SDimitry Andric 281*480093f4SDimitry Andric const HandleState *HState = State->get<HStateMap>(Handle); 282*480093f4SDimitry Andric if (!HState || HState->isEscaped()) 283*480093f4SDimitry Andric continue; 284*480093f4SDimitry Andric 285*480093f4SDimitry Andric if (hasFuchsiaAttr<UseHandleAttr>(PVD) || PVD->getType()->isIntegerType()) { 286*480093f4SDimitry Andric if (HState->isReleased()) { 287*480093f4SDimitry Andric reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C); 288*480093f4SDimitry Andric return; 289*480093f4SDimitry Andric } 290*480093f4SDimitry Andric } 291*480093f4SDimitry Andric if (!hasFuchsiaAttr<UseHandleAttr>(PVD) && 292*480093f4SDimitry Andric PVD->getType()->isIntegerType()) { 293*480093f4SDimitry Andric // Working around integer by-value escapes. 294*480093f4SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getEscaped()); 295*480093f4SDimitry Andric } 296*480093f4SDimitry Andric } 297*480093f4SDimitry Andric C.addTransition(State); 298*480093f4SDimitry Andric } 299*480093f4SDimitry Andric 300*480093f4SDimitry Andric void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, 301*480093f4SDimitry Andric CheckerContext &C) const { 302*480093f4SDimitry Andric const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 303*480093f4SDimitry Andric if (!FuncDecl) 304*480093f4SDimitry Andric return; 305*480093f4SDimitry Andric 306*480093f4SDimitry Andric ProgramStateRef State = C.getState(); 307*480093f4SDimitry Andric 308*480093f4SDimitry Andric std::vector<std::function<std::string(BugReport & BR)>> Notes; 309*480093f4SDimitry Andric SymbolRef ResultSymbol = nullptr; 310*480093f4SDimitry Andric if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>()) 311*480093f4SDimitry Andric if (TypeDefTy->getDecl()->getName() == ErrorTypeName) 312*480093f4SDimitry Andric ResultSymbol = Call.getReturnValue().getAsSymbol(); 313*480093f4SDimitry Andric 314*480093f4SDimitry Andric // Function returns an open handle. 315*480093f4SDimitry Andric if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) { 316*480093f4SDimitry Andric SymbolRef RetSym = Call.getReturnValue().getAsSymbol(); 317*480093f4SDimitry Andric State = 318*480093f4SDimitry Andric State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr)); 319*480093f4SDimitry Andric } 320*480093f4SDimitry Andric 321*480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 322*480093f4SDimitry Andric if (Arg >= FuncDecl->getNumParams()) 323*480093f4SDimitry Andric break; 324*480093f4SDimitry Andric const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 325*480093f4SDimitry Andric SymbolRef Handle = 326*480093f4SDimitry Andric getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State); 327*480093f4SDimitry Andric if (!Handle) 328*480093f4SDimitry Andric continue; 329*480093f4SDimitry Andric 330*480093f4SDimitry Andric const HandleState *HState = State->get<HStateMap>(Handle); 331*480093f4SDimitry Andric if (HState && HState->isEscaped()) 332*480093f4SDimitry Andric continue; 333*480093f4SDimitry Andric if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) { 334*480093f4SDimitry Andric if (HState && HState->isReleased()) { 335*480093f4SDimitry Andric reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C); 336*480093f4SDimitry Andric return; 337*480093f4SDimitry Andric } else { 338*480093f4SDimitry Andric Notes.push_back([Handle](BugReport &BR) { 339*480093f4SDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 340*480093f4SDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { 341*480093f4SDimitry Andric return "Handle released here."; 342*480093f4SDimitry Andric } else 343*480093f4SDimitry Andric return ""; 344*480093f4SDimitry Andric }); 345*480093f4SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getReleased()); 346*480093f4SDimitry Andric } 347*480093f4SDimitry Andric } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) { 348*480093f4SDimitry Andric Notes.push_back([Handle](BugReport &BR) { 349*480093f4SDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 350*480093f4SDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { 351*480093f4SDimitry Andric return "Handle allocated here."; 352*480093f4SDimitry Andric } else 353*480093f4SDimitry Andric return ""; 354*480093f4SDimitry Andric }); 355*480093f4SDimitry Andric State = State->set<HStateMap>( 356*480093f4SDimitry Andric Handle, HandleState::getMaybeAllocated(ResultSymbol)); 357*480093f4SDimitry Andric } 358*480093f4SDimitry Andric } 359*480093f4SDimitry Andric const NoteTag *T = nullptr; 360*480093f4SDimitry Andric if (!Notes.empty()) { 361*480093f4SDimitry Andric T = C.getNoteTag( 362*480093f4SDimitry Andric [this, Notes{std::move(Notes)}](BugReport &BR) -> std::string { 363*480093f4SDimitry Andric if (&BR.getBugType() != &UseAfterReleaseBugType && 364*480093f4SDimitry Andric &BR.getBugType() != &LeakBugType && 365*480093f4SDimitry Andric &BR.getBugType() != &DoubleReleaseBugType) 366*480093f4SDimitry Andric return ""; 367*480093f4SDimitry Andric for (auto &Note : Notes) { 368*480093f4SDimitry Andric std::string Text = Note(BR); 369*480093f4SDimitry Andric if (!Text.empty()) 370*480093f4SDimitry Andric return Text; 371*480093f4SDimitry Andric } 372*480093f4SDimitry Andric return ""; 373*480093f4SDimitry Andric }); 374*480093f4SDimitry Andric } 375*480093f4SDimitry Andric C.addTransition(State, T); 376*480093f4SDimitry Andric } 377*480093f4SDimitry Andric 378*480093f4SDimitry Andric void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper, 379*480093f4SDimitry Andric CheckerContext &C) const { 380*480093f4SDimitry Andric ProgramStateRef State = C.getState(); 381*480093f4SDimitry Andric SmallVector<SymbolRef, 2> LeakedSyms; 382*480093f4SDimitry Andric HStateMapTy TrackedHandles = State->get<HStateMap>(); 383*480093f4SDimitry Andric for (auto &CurItem : TrackedHandles) { 384*480093f4SDimitry Andric if (!SymReaper.isDead(CurItem.first)) 385*480093f4SDimitry Andric continue; 386*480093f4SDimitry Andric if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated()) 387*480093f4SDimitry Andric LeakedSyms.push_back(CurItem.first); 388*480093f4SDimitry Andric State = State->remove<HStateMap>(CurItem.first); 389*480093f4SDimitry Andric } 390*480093f4SDimitry Andric 391*480093f4SDimitry Andric ExplodedNode *N = C.getPredecessor(); 392*480093f4SDimitry Andric if (!LeakedSyms.empty()) 393*480093f4SDimitry Andric N = reportLeaks(LeakedSyms, C, N); 394*480093f4SDimitry Andric 395*480093f4SDimitry Andric C.addTransition(State, N); 396*480093f4SDimitry Andric } 397*480093f4SDimitry Andric 398*480093f4SDimitry Andric // Acquiring a handle is not always successful. In Fuchsia most functions 399*480093f4SDimitry Andric // return a status code that determines the status of the handle. 400*480093f4SDimitry Andric // When we split the path based on this status code we know that on one 401*480093f4SDimitry Andric // path we do have the handle and on the other path the acquire failed. 402*480093f4SDimitry Andric // This method helps avoiding false positive leak warnings on paths where 403*480093f4SDimitry Andric // the function failed. 404*480093f4SDimitry Andric // Moreover, when a handle is known to be zero (the invalid handle), 405*480093f4SDimitry Andric // we no longer can follow the symbol on the path, becaue the constant 406*480093f4SDimitry Andric // zero will be used instead of the symbol. We also do not need to release 407*480093f4SDimitry Andric // an invalid handle, so we remove the corresponding symbol from the state. 408*480093f4SDimitry Andric ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State, 409*480093f4SDimitry Andric SVal Cond, 410*480093f4SDimitry Andric bool Assumption) const { 411*480093f4SDimitry Andric // TODO: add notes about successes/fails for APIs. 412*480093f4SDimitry Andric ConstraintManager &Cmr = State->getConstraintManager(); 413*480093f4SDimitry Andric HStateMapTy TrackedHandles = State->get<HStateMap>(); 414*480093f4SDimitry Andric for (auto &CurItem : TrackedHandles) { 415*480093f4SDimitry Andric ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first); 416*480093f4SDimitry Andric if (HandleVal.isConstrainedTrue()) { 417*480093f4SDimitry Andric // The handle is invalid. We can no longer follow the symbol on this path. 418*480093f4SDimitry Andric State = State->remove<HStateMap>(CurItem.first); 419*480093f4SDimitry Andric } 420*480093f4SDimitry Andric SymbolRef ErrorSym = CurItem.second.getErrorSym(); 421*480093f4SDimitry Andric if (!ErrorSym) 422*480093f4SDimitry Andric continue; 423*480093f4SDimitry Andric ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym); 424*480093f4SDimitry Andric if (ErrorVal.isConstrainedTrue()) { 425*480093f4SDimitry Andric // Allocation succeeded. 426*480093f4SDimitry Andric if (CurItem.second.maybeAllocated()) 427*480093f4SDimitry Andric State = State->set<HStateMap>( 428*480093f4SDimitry Andric CurItem.first, HandleState::getAllocated(State, CurItem.second)); 429*480093f4SDimitry Andric } else if (ErrorVal.isConstrainedFalse()) { 430*480093f4SDimitry Andric // Allocation failed. 431*480093f4SDimitry Andric if (CurItem.second.maybeAllocated()) 432*480093f4SDimitry Andric State = State->remove<HStateMap>(CurItem.first); 433*480093f4SDimitry Andric } 434*480093f4SDimitry Andric } 435*480093f4SDimitry Andric return State; 436*480093f4SDimitry Andric } 437*480093f4SDimitry Andric 438*480093f4SDimitry Andric ProgramStateRef FuchsiaHandleChecker::checkPointerEscape( 439*480093f4SDimitry Andric ProgramStateRef State, const InvalidatedSymbols &Escaped, 440*480093f4SDimitry Andric const CallEvent *Call, PointerEscapeKind Kind) const { 441*480093f4SDimitry Andric const FunctionDecl *FuncDecl = 442*480093f4SDimitry Andric Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr; 443*480093f4SDimitry Andric 444*480093f4SDimitry Andric llvm::DenseSet<SymbolRef> UnEscaped; 445*480093f4SDimitry Andric // Not all calls should escape our symbols. 446*480093f4SDimitry Andric if (FuncDecl && 447*480093f4SDimitry Andric (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall || 448*480093f4SDimitry Andric Kind == PSK_EscapeOutParameters)) { 449*480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) { 450*480093f4SDimitry Andric if (Arg >= FuncDecl->getNumParams()) 451*480093f4SDimitry Andric break; 452*480093f4SDimitry Andric const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 453*480093f4SDimitry Andric SymbolRef Handle = 454*480093f4SDimitry Andric getFuchsiaHandleSymbol(PVD->getType(), Call->getArgSVal(Arg), State); 455*480093f4SDimitry Andric if (!Handle) 456*480093f4SDimitry Andric continue; 457*480093f4SDimitry Andric if (hasFuchsiaAttr<UseHandleAttr>(PVD) || 458*480093f4SDimitry Andric hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) 459*480093f4SDimitry Andric UnEscaped.insert(Handle); 460*480093f4SDimitry Andric } 461*480093f4SDimitry Andric } 462*480093f4SDimitry Andric 463*480093f4SDimitry Andric // For out params, we have to deal with derived symbols. See 464*480093f4SDimitry Andric // MacOSKeychainAPIChecker for details. 465*480093f4SDimitry Andric for (auto I : State->get<HStateMap>()) { 466*480093f4SDimitry Andric if (Escaped.count(I.first) && !UnEscaped.count(I.first)) 467*480093f4SDimitry Andric State = State->set<HStateMap>(I.first, HandleState::getEscaped()); 468*480093f4SDimitry Andric if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) { 469*480093f4SDimitry Andric auto ParentSym = SD->getParentSymbol(); 470*480093f4SDimitry Andric if (Escaped.count(ParentSym)) 471*480093f4SDimitry Andric State = State->set<HStateMap>(I.first, HandleState::getEscaped()); 472*480093f4SDimitry Andric } 473*480093f4SDimitry Andric } 474*480093f4SDimitry Andric 475*480093f4SDimitry Andric return State; 476*480093f4SDimitry Andric } 477*480093f4SDimitry Andric 478*480093f4SDimitry Andric ExplodedNode * 479*480093f4SDimitry Andric FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles, 480*480093f4SDimitry Andric CheckerContext &C, ExplodedNode *Pred) const { 481*480093f4SDimitry Andric ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred); 482*480093f4SDimitry Andric for (SymbolRef LeakedHandle : LeakedHandles) { 483*480093f4SDimitry Andric reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType, 484*480093f4SDimitry Andric "Potential leak of handle"); 485*480093f4SDimitry Andric } 486*480093f4SDimitry Andric return ErrNode; 487*480093f4SDimitry Andric } 488*480093f4SDimitry Andric 489*480093f4SDimitry Andric void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym, 490*480093f4SDimitry Andric const SourceRange &Range, 491*480093f4SDimitry Andric CheckerContext &C) const { 492*480093f4SDimitry Andric ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); 493*480093f4SDimitry Andric reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType, 494*480093f4SDimitry Andric "Releasing a previously released handle"); 495*480093f4SDimitry Andric } 496*480093f4SDimitry Andric 497*480093f4SDimitry Andric void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym, 498*480093f4SDimitry Andric const SourceRange &Range, 499*480093f4SDimitry Andric CheckerContext &C) const { 500*480093f4SDimitry Andric ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); 501*480093f4SDimitry Andric reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType, 502*480093f4SDimitry Andric "Using a previously released handle"); 503*480093f4SDimitry Andric } 504*480093f4SDimitry Andric 505*480093f4SDimitry Andric void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, 506*480093f4SDimitry Andric CheckerContext &C, 507*480093f4SDimitry Andric const SourceRange *Range, 508*480093f4SDimitry Andric const BugType &Type, StringRef Msg) const { 509*480093f4SDimitry Andric if (!ErrorNode) 510*480093f4SDimitry Andric return; 511*480093f4SDimitry Andric 512*480093f4SDimitry Andric std::unique_ptr<PathSensitiveBugReport> R; 513*480093f4SDimitry Andric if (Type.isSuppressOnSink()) { 514*480093f4SDimitry Andric const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C); 515*480093f4SDimitry Andric if (AcquireNode) { 516*480093f4SDimitry Andric PathDiagnosticLocation LocUsedForUniqueing = 517*480093f4SDimitry Andric PathDiagnosticLocation::createBegin( 518*480093f4SDimitry Andric AcquireNode->getStmtForDiagnostics(), C.getSourceManager(), 519*480093f4SDimitry Andric AcquireNode->getLocationContext()); 520*480093f4SDimitry Andric 521*480093f4SDimitry Andric R = std::make_unique<PathSensitiveBugReport>( 522*480093f4SDimitry Andric Type, Msg, ErrorNode, LocUsedForUniqueing, 523*480093f4SDimitry Andric AcquireNode->getLocationContext()->getDecl()); 524*480093f4SDimitry Andric } 525*480093f4SDimitry Andric } 526*480093f4SDimitry Andric if (!R) 527*480093f4SDimitry Andric R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode); 528*480093f4SDimitry Andric if (Range) 529*480093f4SDimitry Andric R->addRange(*Range); 530*480093f4SDimitry Andric R->markInteresting(Sym); 531*480093f4SDimitry Andric C.emitReport(std::move(R)); 532*480093f4SDimitry Andric } 533*480093f4SDimitry Andric 534*480093f4SDimitry Andric void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) { 535*480093f4SDimitry Andric mgr.registerChecker<FuchsiaHandleChecker>(); 536*480093f4SDimitry Andric } 537*480093f4SDimitry Andric 538*480093f4SDimitry Andric bool ento::shouldRegisterFuchsiaHandleChecker(const LangOptions &LO) { 539*480093f4SDimitry Andric return true; 540*480093f4SDimitry Andric } 541*480093f4SDimitry Andric 542*480093f4SDimitry Andric void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State, 543*480093f4SDimitry Andric const char *NL, const char *Sep) const { 544*480093f4SDimitry Andric 545*480093f4SDimitry Andric HStateMapTy StateMap = State->get<HStateMap>(); 546*480093f4SDimitry Andric 547*480093f4SDimitry Andric if (!StateMap.isEmpty()) { 548*480093f4SDimitry Andric Out << Sep << "FuchsiaHandleChecker :" << NL; 549*480093f4SDimitry Andric for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E; 550*480093f4SDimitry Andric ++I) { 551*480093f4SDimitry Andric I.getKey()->dumpToStream(Out); 552*480093f4SDimitry Andric Out << " : "; 553*480093f4SDimitry Andric I.getData().dump(Out); 554*480093f4SDimitry Andric Out << NL; 555*480093f4SDimitry Andric } 556*480093f4SDimitry Andric } 557*480093f4SDimitry Andric } 558