123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700 |
- //=== FuchsiaHandleChecker.cpp - Find handle leaks/double closes -*- C++ -*--=//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This checker checks if the handle of Fuchsia is properly used according to
- // following rules.
- // - If a handle is acquired, it should be released before execution
- // ends.
- // - If a handle is released, it should not be released again.
- // - If a handle is released, it should not be used for other purposes
- // such as I/O.
- //
- // In this checker, each tracked handle is associated with a state. When the
- // handle variable is passed to different function calls or syscalls, its state
- // changes. The state changes can be generally represented by following ASCII
- // Art:
- //
- //
- // +-------------+ +------------+
- // acquire_func succeeded | | Escape | |
- // +-----------------> Allocated +---------> Escaped <--+
- // | | | | | |
- // | +-----+------++ +------------+ |
- // | | | |
- // acquire_func | release_func | +--+ |
- // failed | | | handle +--------+ |
- // +---------+ | | | dies | | |
- // | | | +----v-----+ +---------> Leaked | |
- // | | | | | |(REPORT)| |
- // | +----------+--+ | Released | Escape +--------+ |
- // | | | | +---------------------------+
- // +--> Not tracked | +----+---+-+
- // | | | | As argument by value
- // +----------+--+ release_func | +------+ in function call
- // | | | or by reference in
- // | | | use_func call
- // unowned | +----v-----+ | +-----------+
- // acquire_func | | Double | +-----> Use after |
- // succeeded | | released | | released |
- // | | (REPORT) | | (REPORT) |
- // +---------------+ +----------+ +-----------+
- // | Allocated |
- // | Unowned | release_func
- // | +---------+
- // +---------------+ |
- // |
- // +-----v----------+
- // | Release of |
- // | unowned handle |
- // | (REPORT) |
- // +----------------+
- //
- // acquire_func represents the functions or syscalls that may acquire a handle.
- // release_func represents the functions or syscalls that may release a handle.
- // use_func represents the functions or syscall that requires an open handle.
- //
- // If a tracked handle dies in "Released" or "Not Tracked" state, we assume it
- // is properly used. Otherwise a bug and will be reported.
- //
- // Note that, the analyzer does not always know for sure if a function failed
- // or succeeded. In those cases we use the state MaybeAllocated.
- // Thus, the diagram above captures the intent, not implementation details.
- //
- // Due to the fact that the number of handle related syscalls in Fuchsia
- // is large, we adopt the annotation attributes to descript syscalls'
- // operations(acquire/release/use) on handles instead of hardcoding
- // everything in the checker.
- //
- // We use following annotation attributes for handle related syscalls or
- // functions:
- // 1. __attribute__((acquire_handle("Fuchsia"))) |handle will be acquired
- // 2. __attribute__((release_handle("Fuchsia"))) |handle will be released
- // 3. __attribute__((use_handle("Fuchsia"))) |handle will not transit to
- // escaped state, it also needs to be open.
- //
- // For example, an annotated syscall:
- // zx_status_t zx_channel_create(
- // uint32_t options,
- // zx_handle_t* out0 __attribute__((acquire_handle("Fuchsia"))) ,
- // zx_handle_t* out1 __attribute__((acquire_handle("Fuchsia"))));
- // denotes a syscall which will acquire two handles and save them to 'out0' and
- // 'out1' when succeeded.
- //
- //===----------------------------------------------------------------------===//
- #include "clang/AST/Attr.h"
- #include "clang/AST/Decl.h"
- #include "clang/AST/Type.h"
- #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
- #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
- #include "clang/StaticAnalyzer/Core/Checker.h"
- #include "clang/StaticAnalyzer/Core/CheckerManager.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
- #include "llvm/ADT/StringExtras.h"
- using namespace clang;
- using namespace ento;
- namespace {
- static const StringRef HandleTypeName = "zx_handle_t";
- static const StringRef ErrorTypeName = "zx_status_t";
- class HandleState {
- private:
- enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K;
- SymbolRef ErrorSym;
- HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
- public:
- bool operator==(const HandleState &Other) const {
- return K == Other.K && ErrorSym == Other.ErrorSym;
- }
- bool isAllocated() const { return K == Kind::Allocated; }
- bool maybeAllocated() const { return K == Kind::MaybeAllocated; }
- bool isReleased() const { return K == Kind::Released; }
- bool isEscaped() const { return K == Kind::Escaped; }
- bool isUnowned() const { return K == Kind::Unowned; }
- static HandleState getMaybeAllocated(SymbolRef ErrorSym) {
- return HandleState(Kind::MaybeAllocated, ErrorSym);
- }
- static HandleState getAllocated(ProgramStateRef State, HandleState S) {
- assert(S.maybeAllocated());
- assert(State->getConstraintManager()
- .isNull(State, S.getErrorSym())
- .isConstrained());
- return HandleState(Kind::Allocated, nullptr);
- }
- static HandleState getReleased() {
- return HandleState(Kind::Released, nullptr);
- }
- static HandleState getEscaped() {
- return HandleState(Kind::Escaped, nullptr);
- }
- static HandleState getUnowned() {
- return HandleState(Kind::Unowned, nullptr);
- }
- SymbolRef getErrorSym() const { return ErrorSym; }
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddInteger(static_cast<int>(K));
- ID.AddPointer(ErrorSym);
- }
- LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
- switch (K) {
- #define CASE(ID) \
- case ID: \
- OS << #ID; \
- break;
- CASE(Kind::MaybeAllocated)
- CASE(Kind::Allocated)
- CASE(Kind::Released)
- CASE(Kind::Escaped)
- CASE(Kind::Unowned)
- }
- if (ErrorSym) {
- OS << " ErrorSym: ";
- ErrorSym->dumpToStream(OS);
- }
- }
- LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
- };
- template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {
- return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";
- }
- template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) {
- return D->hasAttr<Attr>() &&
- D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned";
- }
- class FuchsiaHandleChecker
- : public Checker<check::PostCall, check::PreCall, check::DeadSymbols,
- check::PointerEscape, eval::Assume> {
- BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error",
- /*SuppressOnSink=*/true};
- BugType DoubleReleaseBugType{this, "Fuchsia handle double release",
- "Fuchsia Handle Error"};
- BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release",
- "Fuchsia Handle Error"};
- BugType ReleaseUnownedBugType{
- this, "Fuchsia handle release of unowned handle", "Fuchsia Handle Error"};
- public:
- void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
- void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
- void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
- ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
- bool Assumption) const;
- ProgramStateRef checkPointerEscape(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind) const;
- ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
- CheckerContext &C, ExplodedNode *Pred) const;
- void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range,
- CheckerContext &C) const;
- void reportUnownedRelease(SymbolRef HandleSym, const SourceRange &Range,
- CheckerContext &C) const;
- void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range,
- CheckerContext &C) const;
- void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,
- const SourceRange *Range, const BugType &Type,
- StringRef Msg) const;
- void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
- const char *Sep) const override;
- };
- } // end anonymous namespace
- REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
- static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym,
- CheckerContext &Ctx) {
- ProgramStateRef State = N->getState();
- // When bug type is handle leak, exploded node N does not have state info for
- // leaking handle. Get the predecessor of N instead.
- if (!State->get<HStateMap>(Sym))
- N = N->getFirstPred();
- const ExplodedNode *Pred = N;
- while (N) {
- State = N->getState();
- if (!State->get<HStateMap>(Sym)) {
- const HandleState *HState = Pred->getState()->get<HStateMap>(Sym);
- if (HState && (HState->isAllocated() || HState->maybeAllocated()))
- return N;
- }
- Pred = N;
- N = N->getFirstPred();
- }
- return nullptr;
- }
- namespace {
- class FuchsiaHandleSymbolVisitor final : public SymbolVisitor {
- public:
- FuchsiaHandleSymbolVisitor(ProgramStateRef State) : State(std::move(State)) {}
- ProgramStateRef getState() const { return State; }
- bool VisitSymbol(SymbolRef S) override {
- if (const auto *HandleType = S->getType()->getAs<TypedefType>())
- if (HandleType->getDecl()->getName() == HandleTypeName)
- Symbols.push_back(S);
- return true;
- }
- SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; }
- private:
- SmallVector<SymbolRef, 1024> Symbols;
- ProgramStateRef State;
- };
- } // end anonymous namespace
- /// Returns the symbols extracted from the argument or empty vector if it cannot
- /// be found. It is unlikely to have over 1024 symbols in one argument.
- static SmallVector<SymbolRef, 1024>
- getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) {
- int PtrToHandleLevel = 0;
- while (QT->isAnyPointerType() || QT->isReferenceType()) {
- ++PtrToHandleLevel;
- QT = QT->getPointeeType();
- }
- if (QT->isStructureType()) {
- // If we see a structure, see if there is any handle referenced by the
- // structure.
- FuchsiaHandleSymbolVisitor Visitor(State);
- State->scanReachableSymbols(Arg, Visitor);
- return Visitor.GetSymbols();
- }
- if (const auto *HandleType = QT->getAs<TypedefType>()) {
- if (HandleType->getDecl()->getName() != HandleTypeName)
- return {};
- if (PtrToHandleLevel > 1)
- // Not supported yet.
- return {};
- if (PtrToHandleLevel == 0) {
- SymbolRef Sym = Arg.getAsSymbol();
- if (Sym) {
- return {Sym};
- } else {
- return {};
- }
- } else {
- assert(PtrToHandleLevel == 1);
- if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) {
- SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol();
- if (Sym) {
- return {Sym};
- } else {
- return {};
- }
- }
- }
- }
- return {};
- }
- void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
- if (!FuncDecl) {
- // Unknown call, escape by value handles. They are not covered by
- // PointerEscape callback.
- for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
- if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol())
- State = State->set<HStateMap>(Handle, HandleState::getEscaped());
- }
- C.addTransition(State);
- return;
- }
- for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
- if (Arg >= FuncDecl->getNumParams())
- break;
- const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
- SmallVector<SymbolRef, 1024> Handles =
- getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);
- // Handled in checkPostCall.
- if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
- hasFuchsiaAttr<AcquireHandleAttr>(PVD))
- continue;
- for (SymbolRef Handle : Handles) {
- const HandleState *HState = State->get<HStateMap>(Handle);
- if (!HState || HState->isEscaped())
- continue;
- if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
- PVD->getType()->isIntegerType()) {
- if (HState->isReleased()) {
- reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
- return;
- }
- }
- }
- }
- C.addTransition(State);
- }
- void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
- CheckerContext &C) const {
- const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
- if (!FuncDecl)
- return;
- // If we analyzed the function body, then ignore the annotations.
- if (C.wasInlined)
- return;
- ProgramStateRef State = C.getState();
- std::vector<std::function<std::string(BugReport & BR)>> Notes;
- SymbolRef ResultSymbol = nullptr;
- if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>())
- if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
- ResultSymbol = Call.getReturnValue().getAsSymbol();
- // Function returns an open handle.
- if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
- SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
- Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {
- auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
- if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
- std::string SBuf;
- llvm::raw_string_ostream OS(SBuf);
- OS << "Function '" << FuncDecl->getDeclName()
- << "' returns an open handle";
- return SBuf;
- } else
- return "";
- });
- State =
- State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));
- } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) {
- // Function returns an unowned handle
- SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
- Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {
- auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
- if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
- std::string SBuf;
- llvm::raw_string_ostream OS(SBuf);
- OS << "Function '" << FuncDecl->getDeclName()
- << "' returns an unowned handle";
- return SBuf;
- } else
- return "";
- });
- State = State->set<HStateMap>(RetSym, HandleState::getUnowned());
- }
- for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
- if (Arg >= FuncDecl->getNumParams())
- break;
- const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
- unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1;
- SmallVector<SymbolRef, 1024> Handles =
- getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);
- for (SymbolRef Handle : Handles) {
- const HandleState *HState = State->get<HStateMap>(Handle);
- if (HState && HState->isEscaped())
- continue;
- if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
- if (HState && HState->isReleased()) {
- reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
- return;
- } else if (HState && HState->isUnowned()) {
- reportUnownedRelease(Handle, Call.getArgSourceRange(Arg), C);
- return;
- } else {
- Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
- auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
- if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
- std::string SBuf;
- llvm::raw_string_ostream OS(SBuf);
- OS << "Handle released through " << ParamDiagIdx
- << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
- return SBuf;
- } else
- return "";
- });
- State = State->set<HStateMap>(Handle, HandleState::getReleased());
- }
- } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
- Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
- auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
- if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
- std::string SBuf;
- llvm::raw_string_ostream OS(SBuf);
- OS << "Handle allocated through " << ParamDiagIdx
- << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
- return SBuf;
- } else
- return "";
- });
- State = State->set<HStateMap>(
- Handle, HandleState::getMaybeAllocated(ResultSymbol));
- } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)) {
- Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
- auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
- if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
- std::string SBuf;
- llvm::raw_string_ostream OS(SBuf);
- OS << "Unowned handle allocated through " << ParamDiagIdx
- << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
- return SBuf;
- } else
- return "";
- });
- State = State->set<HStateMap>(Handle, HandleState::getUnowned());
- } else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
- PVD->getType()->isIntegerType()) {
- // Working around integer by-value escapes.
- // The by-value escape would not be captured in checkPointerEscape.
- // If the function was not analyzed (otherwise wasInlined should be
- // true) and there is no annotation on the handle, we assume the handle
- // is escaped.
- State = State->set<HStateMap>(Handle, HandleState::getEscaped());
- }
- }
- }
- const NoteTag *T = nullptr;
- if (!Notes.empty()) {
- T = C.getNoteTag([this, Notes{std::move(Notes)}](
- PathSensitiveBugReport &BR) -> std::string {
- if (&BR.getBugType() != &UseAfterReleaseBugType &&
- &BR.getBugType() != &LeakBugType &&
- &BR.getBugType() != &DoubleReleaseBugType &&
- &BR.getBugType() != &ReleaseUnownedBugType)
- return "";
- for (auto &Note : Notes) {
- std::string Text = Note(BR);
- if (!Text.empty())
- return Text;
- }
- return "";
- });
- }
- C.addTransition(State, T);
- }
- void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- SmallVector<SymbolRef, 2> LeakedSyms;
- HStateMapTy TrackedHandles = State->get<HStateMap>();
- for (auto &CurItem : TrackedHandles) {
- SymbolRef ErrorSym = CurItem.second.getErrorSym();
- // Keeping zombie handle symbols. In case the error symbol is dying later
- // than the handle symbol we might produce spurious leak warnings (in case
- // we find out later from the status code that the handle allocation failed
- // in the first place).
- if (!SymReaper.isDead(CurItem.first) ||
- (ErrorSym && !SymReaper.isDead(ErrorSym)))
- continue;
- if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
- LeakedSyms.push_back(CurItem.first);
- State = State->remove<HStateMap>(CurItem.first);
- }
- ExplodedNode *N = C.getPredecessor();
- if (!LeakedSyms.empty())
- N = reportLeaks(LeakedSyms, C, N);
- C.addTransition(State, N);
- }
- // Acquiring a handle is not always successful. In Fuchsia most functions
- // return a status code that determines the status of the handle.
- // When we split the path based on this status code we know that on one
- // path we do have the handle and on the other path the acquire failed.
- // This method helps avoiding false positive leak warnings on paths where
- // the function failed.
- // Moreover, when a handle is known to be zero (the invalid handle),
- // we no longer can follow the symbol on the path, becaue the constant
- // zero will be used instead of the symbol. We also do not need to release
- // an invalid handle, so we remove the corresponding symbol from the state.
- ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,
- SVal Cond,
- bool Assumption) const {
- // TODO: add notes about successes/fails for APIs.
- ConstraintManager &Cmr = State->getConstraintManager();
- HStateMapTy TrackedHandles = State->get<HStateMap>();
- for (auto &CurItem : TrackedHandles) {
- ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first);
- if (HandleVal.isConstrainedTrue()) {
- // The handle is invalid. We can no longer follow the symbol on this path.
- State = State->remove<HStateMap>(CurItem.first);
- }
- SymbolRef ErrorSym = CurItem.second.getErrorSym();
- if (!ErrorSym)
- continue;
- ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym);
- if (ErrorVal.isConstrainedTrue()) {
- // Allocation succeeded.
- if (CurItem.second.maybeAllocated())
- State = State->set<HStateMap>(
- CurItem.first, HandleState::getAllocated(State, CurItem.second));
- } else if (ErrorVal.isConstrainedFalse()) {
- // Allocation failed.
- if (CurItem.second.maybeAllocated())
- State = State->remove<HStateMap>(CurItem.first);
- }
- }
- return State;
- }
- ProgramStateRef FuchsiaHandleChecker::checkPointerEscape(
- ProgramStateRef State, const InvalidatedSymbols &Escaped,
- const CallEvent *Call, PointerEscapeKind Kind) const {
- const FunctionDecl *FuncDecl =
- Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr;
- llvm::DenseSet<SymbolRef> UnEscaped;
- // Not all calls should escape our symbols.
- if (FuncDecl &&
- (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall ||
- Kind == PSK_EscapeOutParameters)) {
- for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {
- if (Arg >= FuncDecl->getNumParams())
- break;
- const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
- SmallVector<SymbolRef, 1024> Handles =
- getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Arg), State);
- for (SymbolRef Handle : Handles) {
- if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
- hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
- UnEscaped.insert(Handle);
- }
- }
- }
- }
- // For out params, we have to deal with derived symbols. See
- // MacOSKeychainAPIChecker for details.
- for (auto I : State->get<HStateMap>()) {
- if (Escaped.count(I.first) && !UnEscaped.count(I.first))
- State = State->set<HStateMap>(I.first, HandleState::getEscaped());
- if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) {
- auto ParentSym = SD->getParentSymbol();
- if (Escaped.count(ParentSym))
- State = State->set<HStateMap>(I.first, HandleState::getEscaped());
- }
- }
- return State;
- }
- ExplodedNode *
- FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
- CheckerContext &C, ExplodedNode *Pred) const {
- ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred);
- for (SymbolRef LeakedHandle : LeakedHandles) {
- reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType,
- "Potential leak of handle");
- }
- return ErrNode;
- }
- void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
- const SourceRange &Range,
- CheckerContext &C) const {
- ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
- reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
- "Releasing a previously released handle");
- }
- void FuchsiaHandleChecker::reportUnownedRelease(SymbolRef HandleSym,
- const SourceRange &Range,
- CheckerContext &C) const {
- ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
- reportBug(HandleSym, ErrNode, C, &Range, ReleaseUnownedBugType,
- "Releasing an unowned handle");
- }
- void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
- const SourceRange &Range,
- CheckerContext &C) const {
- ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
- reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType,
- "Using a previously released handle");
- }
- void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
- CheckerContext &C,
- const SourceRange *Range,
- const BugType &Type, StringRef Msg) const {
- if (!ErrorNode)
- return;
- std::unique_ptr<PathSensitiveBugReport> R;
- if (Type.isSuppressOnSink()) {
- const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C);
- if (AcquireNode) {
- PathDiagnosticLocation LocUsedForUniqueing =
- PathDiagnosticLocation::createBegin(
- AcquireNode->getStmtForDiagnostics(), C.getSourceManager(),
- AcquireNode->getLocationContext());
- R = std::make_unique<PathSensitiveBugReport>(
- Type, Msg, ErrorNode, LocUsedForUniqueing,
- AcquireNode->getLocationContext()->getDecl());
- }
- }
- if (!R)
- R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
- if (Range)
- R->addRange(*Range);
- R->markInteresting(Sym);
- C.emitReport(std::move(R));
- }
- void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
- mgr.registerChecker<FuchsiaHandleChecker>();
- }
- bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {
- return true;
- }
- void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const {
- HStateMapTy StateMap = State->get<HStateMap>();
- if (!StateMap.isEmpty()) {
- Out << Sep << "FuchsiaHandleChecker :" << NL;
- for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
- ++I) {
- I.getKey()->dumpToStream(Out);
- Out << " : ";
- I.getData().dump(Out);
- Out << NL;
- }
- }
- }
|