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 // 23480093f4SDimitry Andric // +-+---------v-+ +------------+ 24480093f4SDimitry Andric // acquire_func succeeded | | Escape | | 25480093f4SDimitry Andric // +-----------------> Allocated +---------> Escaped <--+ 26480093f4SDimitry Andric // | | | | | | 27480093f4SDimitry Andric // | +-----+------++ +------------+ | 28480093f4SDimitry Andric // | | | | 29480093f4SDimitry Andric // | release_func | +--+ | 30480093f4SDimitry Andric // | | | handle +--------+ | 31480093f4SDimitry Andric // | | | dies | | | 32480093f4SDimitry Andric // | +----v-----+ +---------> Leaked | | 33480093f4SDimitry Andric // | | | |(REPORT)| | 34480093f4SDimitry Andric // +----------+--+ | Released | Escape +--------+ | 35480093f4SDimitry Andric // | | | +---------------------------+ 36480093f4SDimitry Andric // | Not tracked <--+ +----+---+-+ 37480093f4SDimitry Andric // | | | | | As argument by value 38480093f4SDimitry Andric // +------+------+ | release_func | +------+ in function call 39480093f4SDimitry Andric // | | | | or by reference in 40480093f4SDimitry Andric // | | | | use_func call 41480093f4SDimitry Andric // +---------+ +----v-----+ | +-----------+ 42480093f4SDimitry Andric // acquire_func failed | Double | +-----> Use after | 43480093f4SDimitry Andric // | released | | released | 44480093f4SDimitry Andric // | (REPORT) | | (REPORT) | 45480093f4SDimitry Andric // +----------+ +-----------+ 46480093f4SDimitry Andric // 47480093f4SDimitry Andric // acquire_func represents the functions or syscalls that may acquire a handle. 48480093f4SDimitry Andric // release_func represents the functions or syscalls that may release a handle. 49480093f4SDimitry Andric // use_func represents the functions or syscall that requires an open handle. 50480093f4SDimitry Andric // 51480093f4SDimitry Andric // If a tracked handle dies in "Released" or "Not Tracked" state, we assume it 52480093f4SDimitry Andric // is properly used. Otherwise a bug and will be reported. 53480093f4SDimitry Andric // 54480093f4SDimitry Andric // Note that, the analyzer does not always know for sure if a function failed 55480093f4SDimitry Andric // or succeeded. In those cases we use the state MaybeAllocated. 56480093f4SDimitry Andric // Thus, the diagramm above captures the intent, not implementation details. 57480093f4SDimitry Andric // 58480093f4SDimitry Andric // Due to the fact that the number of handle related syscalls in Fuchsia 59480093f4SDimitry Andric // is large, we adopt the annotation attributes to descript syscalls' 60480093f4SDimitry Andric // operations(acquire/release/use) on handles instead of hardcoding 61480093f4SDimitry Andric // everything in the checker. 62480093f4SDimitry Andric // 63480093f4SDimitry Andric // We use following annotation attributes for handle related syscalls or 64480093f4SDimitry Andric // functions: 65480093f4SDimitry Andric // 1. __attribute__((acquire_handle("Fuchsia"))) |handle will be acquired 66480093f4SDimitry Andric // 2. __attribute__((release_handle("Fuchsia"))) |handle will be released 67480093f4SDimitry Andric // 3. __attribute__((use_handle("Fuchsia"))) |handle will not transit to 68480093f4SDimitry Andric // escaped state, it also needs to be open. 69480093f4SDimitry Andric // 70480093f4SDimitry Andric // For example, an annotated syscall: 71480093f4SDimitry Andric // zx_status_t zx_channel_create( 72480093f4SDimitry Andric // uint32_t options, 73480093f4SDimitry Andric // zx_handle_t* out0 __attribute__((acquire_handle("Fuchsia"))) , 74480093f4SDimitry Andric // zx_handle_t* out1 __attribute__((acquire_handle("Fuchsia")))); 75480093f4SDimitry Andric // denotes a syscall which will acquire two handles and save them to 'out0' and 76480093f4SDimitry Andric // 'out1' when succeeded. 77480093f4SDimitry Andric // 78480093f4SDimitry Andric //===----------------------------------------------------------------------===// 79480093f4SDimitry Andric 80480093f4SDimitry Andric #include "clang/AST/Attr.h" 81480093f4SDimitry Andric #include "clang/AST/Decl.h" 82480093f4SDimitry Andric #include "clang/AST/Type.h" 83480093f4SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 84480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 85480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 86480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 87480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 88480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 89480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" 90480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 91480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 92480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" 93*5ffd83dbSDimitry Andric #include "llvm/ADT/StringExtras.h" 94480093f4SDimitry Andric 95480093f4SDimitry Andric using namespace clang; 96480093f4SDimitry Andric using namespace ento; 97480093f4SDimitry Andric 98480093f4SDimitry Andric namespace { 99480093f4SDimitry Andric 100480093f4SDimitry Andric static const StringRef HandleTypeName = "zx_handle_t"; 101480093f4SDimitry Andric static const StringRef ErrorTypeName = "zx_status_t"; 102480093f4SDimitry Andric 103480093f4SDimitry Andric class HandleState { 104480093f4SDimitry Andric private: 105480093f4SDimitry Andric enum class Kind { MaybeAllocated, Allocated, Released, Escaped } K; 106480093f4SDimitry Andric SymbolRef ErrorSym; 107480093f4SDimitry Andric HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {} 108480093f4SDimitry Andric 109480093f4SDimitry Andric public: 110480093f4SDimitry Andric bool operator==(const HandleState &Other) const { 111480093f4SDimitry Andric return K == Other.K && ErrorSym == Other.ErrorSym; 112480093f4SDimitry Andric } 113480093f4SDimitry Andric bool isAllocated() const { return K == Kind::Allocated; } 114480093f4SDimitry Andric bool maybeAllocated() const { return K == Kind::MaybeAllocated; } 115480093f4SDimitry Andric bool isReleased() const { return K == Kind::Released; } 116480093f4SDimitry Andric bool isEscaped() const { return K == Kind::Escaped; } 117480093f4SDimitry Andric 118480093f4SDimitry Andric static HandleState getMaybeAllocated(SymbolRef ErrorSym) { 119480093f4SDimitry Andric return HandleState(Kind::MaybeAllocated, ErrorSym); 120480093f4SDimitry Andric } 121480093f4SDimitry Andric static HandleState getAllocated(ProgramStateRef State, HandleState S) { 122480093f4SDimitry Andric assert(S.maybeAllocated()); 123480093f4SDimitry Andric assert(State->getConstraintManager() 124480093f4SDimitry Andric .isNull(State, S.getErrorSym()) 125480093f4SDimitry Andric .isConstrained()); 126480093f4SDimitry Andric return HandleState(Kind::Allocated, nullptr); 127480093f4SDimitry Andric } 128480093f4SDimitry Andric static HandleState getReleased() { 129480093f4SDimitry Andric return HandleState(Kind::Released, nullptr); 130480093f4SDimitry Andric } 131480093f4SDimitry Andric static HandleState getEscaped() { 132480093f4SDimitry Andric return HandleState(Kind::Escaped, nullptr); 133480093f4SDimitry Andric } 134480093f4SDimitry Andric 135480093f4SDimitry Andric SymbolRef getErrorSym() const { return ErrorSym; } 136480093f4SDimitry Andric 137480093f4SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const { 138480093f4SDimitry Andric ID.AddInteger(static_cast<int>(K)); 139480093f4SDimitry Andric ID.AddPointer(ErrorSym); 140480093f4SDimitry Andric } 141480093f4SDimitry Andric 142480093f4SDimitry Andric LLVM_DUMP_METHOD void dump(raw_ostream &OS) const { 143480093f4SDimitry Andric switch (K) { 144480093f4SDimitry Andric #define CASE(ID) \ 145480093f4SDimitry Andric case ID: \ 146480093f4SDimitry Andric OS << #ID; \ 147480093f4SDimitry Andric break; 148480093f4SDimitry Andric CASE(Kind::MaybeAllocated) 149480093f4SDimitry Andric CASE(Kind::Allocated) 150480093f4SDimitry Andric CASE(Kind::Released) 151480093f4SDimitry Andric CASE(Kind::Escaped) 152480093f4SDimitry Andric } 153*5ffd83dbSDimitry Andric if (ErrorSym) { 154*5ffd83dbSDimitry Andric OS << " ErrorSym: "; 155*5ffd83dbSDimitry Andric ErrorSym->dumpToStream(OS); 156*5ffd83dbSDimitry Andric } 157480093f4SDimitry Andric } 158480093f4SDimitry Andric 159480093f4SDimitry Andric LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); } 160480093f4SDimitry Andric }; 161480093f4SDimitry Andric 162480093f4SDimitry Andric template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) { 163480093f4SDimitry Andric return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia"; 164480093f4SDimitry Andric } 165480093f4SDimitry Andric 166480093f4SDimitry Andric class FuchsiaHandleChecker 167480093f4SDimitry Andric : public Checker<check::PostCall, check::PreCall, check::DeadSymbols, 168480093f4SDimitry Andric check::PointerEscape, eval::Assume> { 169480093f4SDimitry Andric BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error", 170480093f4SDimitry Andric /*SuppressOnSink=*/true}; 171480093f4SDimitry Andric BugType DoubleReleaseBugType{this, "Fuchsia handle double release", 172480093f4SDimitry Andric "Fuchsia Handle Error"}; 173480093f4SDimitry Andric BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release", 174480093f4SDimitry Andric "Fuchsia Handle Error"}; 175480093f4SDimitry Andric 176480093f4SDimitry Andric public: 177480093f4SDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 178480093f4SDimitry Andric void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 179480093f4SDimitry Andric void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 180480093f4SDimitry Andric ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, 181480093f4SDimitry Andric bool Assumption) const; 182480093f4SDimitry Andric ProgramStateRef checkPointerEscape(ProgramStateRef State, 183480093f4SDimitry Andric const InvalidatedSymbols &Escaped, 184480093f4SDimitry Andric const CallEvent *Call, 185480093f4SDimitry Andric PointerEscapeKind Kind) const; 186480093f4SDimitry Andric 187480093f4SDimitry Andric ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles, 188480093f4SDimitry Andric CheckerContext &C, ExplodedNode *Pred) const; 189480093f4SDimitry Andric 190480093f4SDimitry Andric void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range, 191480093f4SDimitry Andric CheckerContext &C) const; 192480093f4SDimitry Andric 193480093f4SDimitry Andric void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range, 194480093f4SDimitry Andric CheckerContext &C) const; 195480093f4SDimitry Andric 196480093f4SDimitry Andric void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C, 197480093f4SDimitry Andric const SourceRange *Range, const BugType &Type, 198480093f4SDimitry Andric StringRef Msg) const; 199480093f4SDimitry Andric 200480093f4SDimitry Andric void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 201480093f4SDimitry Andric const char *Sep) const override; 202480093f4SDimitry Andric }; 203480093f4SDimitry Andric } // end anonymous namespace 204480093f4SDimitry Andric 205480093f4SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState) 206480093f4SDimitry Andric 207480093f4SDimitry Andric static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym, 208480093f4SDimitry Andric CheckerContext &Ctx) { 209480093f4SDimitry Andric ProgramStateRef State = N->getState(); 210480093f4SDimitry Andric // When bug type is handle leak, exploded node N does not have state info for 211480093f4SDimitry Andric // leaking handle. Get the predecessor of N instead. 212480093f4SDimitry Andric if (!State->get<HStateMap>(Sym)) 213480093f4SDimitry Andric N = N->getFirstPred(); 214480093f4SDimitry Andric 215480093f4SDimitry Andric const ExplodedNode *Pred = N; 216480093f4SDimitry Andric while (N) { 217480093f4SDimitry Andric State = N->getState(); 218480093f4SDimitry Andric if (!State->get<HStateMap>(Sym)) { 219480093f4SDimitry Andric const HandleState *HState = Pred->getState()->get<HStateMap>(Sym); 220480093f4SDimitry Andric if (HState && (HState->isAllocated() || HState->maybeAllocated())) 221480093f4SDimitry Andric return N; 222480093f4SDimitry Andric } 223480093f4SDimitry Andric Pred = N; 224480093f4SDimitry Andric N = N->getFirstPred(); 225480093f4SDimitry Andric } 226480093f4SDimitry Andric return nullptr; 227480093f4SDimitry Andric } 228480093f4SDimitry Andric 229480093f4SDimitry Andric /// Returns the symbols extracted from the argument or null if it cannot be 230480093f4SDimitry Andric /// found. 231480093f4SDimitry Andric static SymbolRef getFuchsiaHandleSymbol(QualType QT, SVal Arg, 232480093f4SDimitry Andric ProgramStateRef State) { 233480093f4SDimitry Andric int PtrToHandleLevel = 0; 234480093f4SDimitry Andric while (QT->isAnyPointerType() || QT->isReferenceType()) { 235480093f4SDimitry Andric ++PtrToHandleLevel; 236480093f4SDimitry Andric QT = QT->getPointeeType(); 237480093f4SDimitry Andric } 238480093f4SDimitry Andric if (const auto *HandleType = QT->getAs<TypedefType>()) { 239480093f4SDimitry Andric if (HandleType->getDecl()->getName() != HandleTypeName) 240480093f4SDimitry Andric return nullptr; 241480093f4SDimitry Andric if (PtrToHandleLevel > 1) { 242480093f4SDimitry Andric // Not supported yet. 243480093f4SDimitry Andric return nullptr; 244480093f4SDimitry Andric } 245480093f4SDimitry Andric 246480093f4SDimitry Andric if (PtrToHandleLevel == 0) { 247480093f4SDimitry Andric return Arg.getAsSymbol(); 248480093f4SDimitry Andric } else { 249480093f4SDimitry Andric assert(PtrToHandleLevel == 1); 250480093f4SDimitry Andric if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) 251480093f4SDimitry Andric return State->getSVal(*ArgLoc).getAsSymbol(); 252480093f4SDimitry Andric } 253480093f4SDimitry Andric } 254480093f4SDimitry Andric return nullptr; 255480093f4SDimitry Andric } 256480093f4SDimitry Andric 257480093f4SDimitry Andric void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call, 258480093f4SDimitry Andric CheckerContext &C) const { 259480093f4SDimitry Andric ProgramStateRef State = C.getState(); 260480093f4SDimitry Andric const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 261480093f4SDimitry Andric if (!FuncDecl) { 262480093f4SDimitry Andric // Unknown call, escape by value handles. They are not covered by 263480093f4SDimitry Andric // PointerEscape callback. 264480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 265480093f4SDimitry Andric if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol()) 266480093f4SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getEscaped()); 267480093f4SDimitry Andric } 268480093f4SDimitry Andric C.addTransition(State); 269480093f4SDimitry Andric return; 270480093f4SDimitry Andric } 271480093f4SDimitry Andric 272480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 273480093f4SDimitry Andric if (Arg >= FuncDecl->getNumParams()) 274480093f4SDimitry Andric break; 275480093f4SDimitry Andric const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 276480093f4SDimitry Andric SymbolRef Handle = 277480093f4SDimitry Andric getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State); 278480093f4SDimitry Andric if (!Handle) 279480093f4SDimitry Andric continue; 280480093f4SDimitry Andric 281480093f4SDimitry Andric // Handled in checkPostCall. 282480093f4SDimitry Andric if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) || 283480093f4SDimitry Andric hasFuchsiaAttr<AcquireHandleAttr>(PVD)) 284480093f4SDimitry Andric continue; 285480093f4SDimitry Andric 286480093f4SDimitry Andric const HandleState *HState = State->get<HStateMap>(Handle); 287480093f4SDimitry Andric if (!HState || HState->isEscaped()) 288480093f4SDimitry Andric continue; 289480093f4SDimitry Andric 290480093f4SDimitry Andric if (hasFuchsiaAttr<UseHandleAttr>(PVD) || PVD->getType()->isIntegerType()) { 291480093f4SDimitry Andric if (HState->isReleased()) { 292480093f4SDimitry Andric reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C); 293480093f4SDimitry Andric return; 294480093f4SDimitry Andric } 295480093f4SDimitry Andric } 296480093f4SDimitry Andric if (!hasFuchsiaAttr<UseHandleAttr>(PVD) && 297480093f4SDimitry Andric PVD->getType()->isIntegerType()) { 298480093f4SDimitry Andric // Working around integer by-value escapes. 299480093f4SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getEscaped()); 300480093f4SDimitry Andric } 301480093f4SDimitry Andric } 302480093f4SDimitry Andric C.addTransition(State); 303480093f4SDimitry Andric } 304480093f4SDimitry Andric 305480093f4SDimitry Andric void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, 306480093f4SDimitry Andric CheckerContext &C) const { 307480093f4SDimitry Andric const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 308480093f4SDimitry Andric if (!FuncDecl) 309480093f4SDimitry Andric return; 310480093f4SDimitry Andric 311480093f4SDimitry Andric ProgramStateRef State = C.getState(); 312480093f4SDimitry Andric 313480093f4SDimitry Andric std::vector<std::function<std::string(BugReport & BR)>> Notes; 314480093f4SDimitry Andric SymbolRef ResultSymbol = nullptr; 315480093f4SDimitry Andric if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>()) 316480093f4SDimitry Andric if (TypeDefTy->getDecl()->getName() == ErrorTypeName) 317480093f4SDimitry Andric ResultSymbol = Call.getReturnValue().getAsSymbol(); 318480093f4SDimitry Andric 319480093f4SDimitry Andric // Function returns an open handle. 320480093f4SDimitry Andric if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) { 321480093f4SDimitry Andric SymbolRef RetSym = Call.getReturnValue().getAsSymbol(); 322*5ffd83dbSDimitry Andric Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string { 323*5ffd83dbSDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 324*5ffd83dbSDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) { 325*5ffd83dbSDimitry Andric std::string SBuf; 326*5ffd83dbSDimitry Andric llvm::raw_string_ostream OS(SBuf); 327*5ffd83dbSDimitry Andric OS << "Function '" << FuncDecl->getNameAsString() 328*5ffd83dbSDimitry Andric << "' returns an open handle"; 329*5ffd83dbSDimitry Andric return OS.str(); 330*5ffd83dbSDimitry Andric } else 331*5ffd83dbSDimitry Andric return ""; 332*5ffd83dbSDimitry Andric }); 333480093f4SDimitry Andric State = 334480093f4SDimitry Andric State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr)); 335480093f4SDimitry Andric } 336480093f4SDimitry Andric 337480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { 338480093f4SDimitry Andric if (Arg >= FuncDecl->getNumParams()) 339480093f4SDimitry Andric break; 340480093f4SDimitry Andric const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 341*5ffd83dbSDimitry Andric unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1; 342480093f4SDimitry Andric SymbolRef Handle = 343480093f4SDimitry Andric getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State); 344480093f4SDimitry Andric if (!Handle) 345480093f4SDimitry Andric continue; 346480093f4SDimitry Andric 347480093f4SDimitry Andric const HandleState *HState = State->get<HStateMap>(Handle); 348480093f4SDimitry Andric if (HState && HState->isEscaped()) 349480093f4SDimitry Andric continue; 350480093f4SDimitry Andric if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) { 351480093f4SDimitry Andric if (HState && HState->isReleased()) { 352480093f4SDimitry Andric reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C); 353480093f4SDimitry Andric return; 354480093f4SDimitry Andric } else { 355*5ffd83dbSDimitry Andric Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { 356480093f4SDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 357480093f4SDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { 358*5ffd83dbSDimitry Andric std::string SBuf; 359*5ffd83dbSDimitry Andric llvm::raw_string_ostream OS(SBuf); 360*5ffd83dbSDimitry Andric OS << "Handle released through " << ParamDiagIdx 361*5ffd83dbSDimitry Andric << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; 362*5ffd83dbSDimitry Andric return OS.str(); 363480093f4SDimitry Andric } else 364480093f4SDimitry Andric return ""; 365480093f4SDimitry Andric }); 366480093f4SDimitry Andric State = State->set<HStateMap>(Handle, HandleState::getReleased()); 367480093f4SDimitry Andric } 368480093f4SDimitry Andric } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) { 369*5ffd83dbSDimitry Andric Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { 370480093f4SDimitry Andric auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); 371480093f4SDimitry Andric if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { 372*5ffd83dbSDimitry Andric std::string SBuf; 373*5ffd83dbSDimitry Andric llvm::raw_string_ostream OS(SBuf); 374*5ffd83dbSDimitry Andric OS << "Handle allocated through " << ParamDiagIdx 375*5ffd83dbSDimitry Andric << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; 376*5ffd83dbSDimitry Andric return OS.str(); 377480093f4SDimitry Andric } else 378480093f4SDimitry Andric return ""; 379480093f4SDimitry Andric }); 380480093f4SDimitry Andric State = State->set<HStateMap>( 381480093f4SDimitry Andric Handle, HandleState::getMaybeAllocated(ResultSymbol)); 382480093f4SDimitry Andric } 383480093f4SDimitry Andric } 384480093f4SDimitry Andric const NoteTag *T = nullptr; 385480093f4SDimitry Andric if (!Notes.empty()) { 386*5ffd83dbSDimitry Andric T = C.getNoteTag([this, Notes{std::move(Notes)}]( 387*5ffd83dbSDimitry Andric PathSensitiveBugReport &BR) -> std::string { 388480093f4SDimitry Andric if (&BR.getBugType() != &UseAfterReleaseBugType && 389480093f4SDimitry Andric &BR.getBugType() != &LeakBugType && 390480093f4SDimitry Andric &BR.getBugType() != &DoubleReleaseBugType) 391480093f4SDimitry Andric return ""; 392480093f4SDimitry Andric for (auto &Note : Notes) { 393480093f4SDimitry Andric std::string Text = Note(BR); 394480093f4SDimitry Andric if (!Text.empty()) 395480093f4SDimitry Andric return Text; 396480093f4SDimitry Andric } 397480093f4SDimitry Andric return ""; 398480093f4SDimitry Andric }); 399480093f4SDimitry Andric } 400480093f4SDimitry Andric C.addTransition(State, T); 401480093f4SDimitry Andric } 402480093f4SDimitry Andric 403480093f4SDimitry Andric void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper, 404480093f4SDimitry Andric CheckerContext &C) const { 405480093f4SDimitry Andric ProgramStateRef State = C.getState(); 406480093f4SDimitry Andric SmallVector<SymbolRef, 2> LeakedSyms; 407480093f4SDimitry Andric HStateMapTy TrackedHandles = State->get<HStateMap>(); 408480093f4SDimitry Andric for (auto &CurItem : TrackedHandles) { 409*5ffd83dbSDimitry Andric SymbolRef ErrorSym = CurItem.second.getErrorSym(); 410*5ffd83dbSDimitry Andric // Keeping zombie handle symbols. In case the error symbol is dying later 411*5ffd83dbSDimitry Andric // than the handle symbol we might produce spurious leak warnings (in case 412*5ffd83dbSDimitry Andric // we find out later from the status code that the handle allocation failed 413*5ffd83dbSDimitry Andric // in the first place). 414*5ffd83dbSDimitry Andric if (!SymReaper.isDead(CurItem.first) || 415*5ffd83dbSDimitry Andric (ErrorSym && !SymReaper.isDead(ErrorSym))) 416480093f4SDimitry Andric continue; 417480093f4SDimitry Andric if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated()) 418480093f4SDimitry Andric LeakedSyms.push_back(CurItem.first); 419480093f4SDimitry Andric State = State->remove<HStateMap>(CurItem.first); 420480093f4SDimitry Andric } 421480093f4SDimitry Andric 422480093f4SDimitry Andric ExplodedNode *N = C.getPredecessor(); 423480093f4SDimitry Andric if (!LeakedSyms.empty()) 424480093f4SDimitry Andric N = reportLeaks(LeakedSyms, C, N); 425480093f4SDimitry Andric 426480093f4SDimitry Andric C.addTransition(State, N); 427480093f4SDimitry Andric } 428480093f4SDimitry Andric 429480093f4SDimitry Andric // Acquiring a handle is not always successful. In Fuchsia most functions 430480093f4SDimitry Andric // return a status code that determines the status of the handle. 431480093f4SDimitry Andric // When we split the path based on this status code we know that on one 432480093f4SDimitry Andric // path we do have the handle and on the other path the acquire failed. 433480093f4SDimitry Andric // This method helps avoiding false positive leak warnings on paths where 434480093f4SDimitry Andric // the function failed. 435480093f4SDimitry Andric // Moreover, when a handle is known to be zero (the invalid handle), 436480093f4SDimitry Andric // we no longer can follow the symbol on the path, becaue the constant 437480093f4SDimitry Andric // zero will be used instead of the symbol. We also do not need to release 438480093f4SDimitry Andric // an invalid handle, so we remove the corresponding symbol from the state. 439480093f4SDimitry Andric ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State, 440480093f4SDimitry Andric SVal Cond, 441480093f4SDimitry Andric bool Assumption) const { 442480093f4SDimitry Andric // TODO: add notes about successes/fails for APIs. 443480093f4SDimitry Andric ConstraintManager &Cmr = State->getConstraintManager(); 444480093f4SDimitry Andric HStateMapTy TrackedHandles = State->get<HStateMap>(); 445480093f4SDimitry Andric for (auto &CurItem : TrackedHandles) { 446480093f4SDimitry Andric ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first); 447480093f4SDimitry Andric if (HandleVal.isConstrainedTrue()) { 448480093f4SDimitry Andric // The handle is invalid. We can no longer follow the symbol on this path. 449480093f4SDimitry Andric State = State->remove<HStateMap>(CurItem.first); 450480093f4SDimitry Andric } 451480093f4SDimitry Andric SymbolRef ErrorSym = CurItem.second.getErrorSym(); 452480093f4SDimitry Andric if (!ErrorSym) 453480093f4SDimitry Andric continue; 454480093f4SDimitry Andric ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym); 455480093f4SDimitry Andric if (ErrorVal.isConstrainedTrue()) { 456480093f4SDimitry Andric // Allocation succeeded. 457480093f4SDimitry Andric if (CurItem.second.maybeAllocated()) 458480093f4SDimitry Andric State = State->set<HStateMap>( 459480093f4SDimitry Andric CurItem.first, HandleState::getAllocated(State, CurItem.second)); 460480093f4SDimitry Andric } else if (ErrorVal.isConstrainedFalse()) { 461480093f4SDimitry Andric // Allocation failed. 462480093f4SDimitry Andric if (CurItem.second.maybeAllocated()) 463480093f4SDimitry Andric State = State->remove<HStateMap>(CurItem.first); 464480093f4SDimitry Andric } 465480093f4SDimitry Andric } 466480093f4SDimitry Andric return State; 467480093f4SDimitry Andric } 468480093f4SDimitry Andric 469480093f4SDimitry Andric ProgramStateRef FuchsiaHandleChecker::checkPointerEscape( 470480093f4SDimitry Andric ProgramStateRef State, const InvalidatedSymbols &Escaped, 471480093f4SDimitry Andric const CallEvent *Call, PointerEscapeKind Kind) const { 472480093f4SDimitry Andric const FunctionDecl *FuncDecl = 473480093f4SDimitry Andric Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr; 474480093f4SDimitry Andric 475480093f4SDimitry Andric llvm::DenseSet<SymbolRef> UnEscaped; 476480093f4SDimitry Andric // Not all calls should escape our symbols. 477480093f4SDimitry Andric if (FuncDecl && 478480093f4SDimitry Andric (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall || 479480093f4SDimitry Andric Kind == PSK_EscapeOutParameters)) { 480480093f4SDimitry Andric for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) { 481480093f4SDimitry Andric if (Arg >= FuncDecl->getNumParams()) 482480093f4SDimitry Andric break; 483480093f4SDimitry Andric const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); 484480093f4SDimitry Andric SymbolRef Handle = 485480093f4SDimitry Andric getFuchsiaHandleSymbol(PVD->getType(), Call->getArgSVal(Arg), State); 486480093f4SDimitry Andric if (!Handle) 487480093f4SDimitry Andric continue; 488480093f4SDimitry Andric if (hasFuchsiaAttr<UseHandleAttr>(PVD) || 489480093f4SDimitry Andric hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) 490480093f4SDimitry Andric UnEscaped.insert(Handle); 491480093f4SDimitry Andric } 492480093f4SDimitry Andric } 493480093f4SDimitry Andric 494480093f4SDimitry Andric // For out params, we have to deal with derived symbols. See 495480093f4SDimitry Andric // MacOSKeychainAPIChecker for details. 496480093f4SDimitry Andric for (auto I : State->get<HStateMap>()) { 497480093f4SDimitry Andric if (Escaped.count(I.first) && !UnEscaped.count(I.first)) 498480093f4SDimitry Andric State = State->set<HStateMap>(I.first, HandleState::getEscaped()); 499480093f4SDimitry Andric if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) { 500480093f4SDimitry Andric auto ParentSym = SD->getParentSymbol(); 501480093f4SDimitry Andric if (Escaped.count(ParentSym)) 502480093f4SDimitry Andric State = State->set<HStateMap>(I.first, HandleState::getEscaped()); 503480093f4SDimitry Andric } 504480093f4SDimitry Andric } 505480093f4SDimitry Andric 506480093f4SDimitry Andric return State; 507480093f4SDimitry Andric } 508480093f4SDimitry Andric 509480093f4SDimitry Andric ExplodedNode * 510480093f4SDimitry Andric FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles, 511480093f4SDimitry Andric CheckerContext &C, ExplodedNode *Pred) const { 512480093f4SDimitry Andric ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred); 513480093f4SDimitry Andric for (SymbolRef LeakedHandle : LeakedHandles) { 514480093f4SDimitry Andric reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType, 515480093f4SDimitry Andric "Potential leak of handle"); 516480093f4SDimitry Andric } 517480093f4SDimitry Andric return ErrNode; 518480093f4SDimitry Andric } 519480093f4SDimitry Andric 520480093f4SDimitry Andric void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym, 521480093f4SDimitry Andric const SourceRange &Range, 522480093f4SDimitry Andric CheckerContext &C) const { 523480093f4SDimitry Andric ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); 524480093f4SDimitry Andric reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType, 525480093f4SDimitry Andric "Releasing a previously released handle"); 526480093f4SDimitry Andric } 527480093f4SDimitry Andric 528480093f4SDimitry Andric void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym, 529480093f4SDimitry Andric const SourceRange &Range, 530480093f4SDimitry Andric CheckerContext &C) const { 531480093f4SDimitry Andric ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); 532480093f4SDimitry Andric reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType, 533480093f4SDimitry Andric "Using a previously released handle"); 534480093f4SDimitry Andric } 535480093f4SDimitry Andric 536480093f4SDimitry Andric void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, 537480093f4SDimitry Andric CheckerContext &C, 538480093f4SDimitry Andric const SourceRange *Range, 539480093f4SDimitry Andric const BugType &Type, StringRef Msg) const { 540480093f4SDimitry Andric if (!ErrorNode) 541480093f4SDimitry Andric return; 542480093f4SDimitry Andric 543480093f4SDimitry Andric std::unique_ptr<PathSensitiveBugReport> R; 544480093f4SDimitry Andric if (Type.isSuppressOnSink()) { 545480093f4SDimitry Andric const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C); 546480093f4SDimitry Andric if (AcquireNode) { 547480093f4SDimitry Andric PathDiagnosticLocation LocUsedForUniqueing = 548480093f4SDimitry Andric PathDiagnosticLocation::createBegin( 549480093f4SDimitry Andric AcquireNode->getStmtForDiagnostics(), C.getSourceManager(), 550480093f4SDimitry Andric AcquireNode->getLocationContext()); 551480093f4SDimitry Andric 552480093f4SDimitry Andric R = std::make_unique<PathSensitiveBugReport>( 553480093f4SDimitry Andric Type, Msg, ErrorNode, LocUsedForUniqueing, 554480093f4SDimitry Andric AcquireNode->getLocationContext()->getDecl()); 555480093f4SDimitry Andric } 556480093f4SDimitry Andric } 557480093f4SDimitry Andric if (!R) 558480093f4SDimitry Andric R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode); 559480093f4SDimitry Andric if (Range) 560480093f4SDimitry Andric R->addRange(*Range); 561480093f4SDimitry Andric R->markInteresting(Sym); 562480093f4SDimitry Andric C.emitReport(std::move(R)); 563480093f4SDimitry Andric } 564480093f4SDimitry Andric 565480093f4SDimitry Andric void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) { 566480093f4SDimitry Andric mgr.registerChecker<FuchsiaHandleChecker>(); 567480093f4SDimitry Andric } 568480093f4SDimitry Andric 569*5ffd83dbSDimitry Andric bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) { 570480093f4SDimitry Andric return true; 571480093f4SDimitry Andric } 572480093f4SDimitry Andric 573480093f4SDimitry Andric void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State, 574480093f4SDimitry Andric const char *NL, const char *Sep) const { 575480093f4SDimitry Andric 576480093f4SDimitry Andric HStateMapTy StateMap = State->get<HStateMap>(); 577480093f4SDimitry Andric 578480093f4SDimitry Andric if (!StateMap.isEmpty()) { 579480093f4SDimitry Andric Out << Sep << "FuchsiaHandleChecker :" << NL; 580480093f4SDimitry Andric for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E; 581480093f4SDimitry Andric ++I) { 582480093f4SDimitry Andric I.getKey()->dumpToStream(Out); 583480093f4SDimitry Andric Out << " : "; 584480093f4SDimitry Andric I.getData().dump(Out); 585480093f4SDimitry Andric Out << NL; 586480093f4SDimitry Andric } 587480093f4SDimitry Andric } 588480093f4SDimitry Andric } 589