diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp')
| -rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp | 312 |
1 files changed, 212 insertions, 100 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp index fc35082705fa..e3f4be0726c8 100644 --- a/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp @@ -20,29 +20,39 @@ // Art: // // -// +-+---------v-+ +------------+ -// acquire_func succeeded | | Escape | | -// +-----------------> Allocated +---------> Escaped <--+ -// | | | | | | -// | +-----+------++ +------------+ | -// | | | | -// | release_func | +--+ | -// | | | 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 -// +---------+ +----v-----+ | +-----------+ -// acquire_func failed | Double | +-----> Use after | -// | released | | released | -// | (REPORT) | | (REPORT) | -// +----------+ +-----------+ +// +-------------+ +------------+ +// 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. @@ -53,7 +63,7 @@ // // 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 diagramm above captures the intent, not implementation details. +// 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' @@ -102,7 +112,7 @@ static const StringRef ErrorTypeName = "zx_status_t"; class HandleState { private: - enum class Kind { MaybeAllocated, Allocated, Released, Escaped } K; + enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K; SymbolRef ErrorSym; HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {} @@ -114,6 +124,7 @@ public: 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); @@ -131,6 +142,9 @@ public: static HandleState getEscaped() { return HandleState(Kind::Escaped, nullptr); } + static HandleState getUnowned() { + return HandleState(Kind::Unowned, nullptr); + } SymbolRef getErrorSym() const { return ErrorSym; } @@ -149,6 +163,7 @@ public: CASE(Kind::Allocated) CASE(Kind::Released) CASE(Kind::Escaped) + CASE(Kind::Unowned) } if (ErrorSym) { OS << " ErrorSym: "; @@ -163,6 +178,11 @@ 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> { @@ -172,6 +192,8 @@ class FuchsiaHandleChecker "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; @@ -190,6 +212,9 @@ public: 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; @@ -226,32 +251,70 @@ static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym, return nullptr; } -/// Returns the symbols extracted from the argument or null if it cannot be -/// found. -static SymbolRef getFuchsiaHandleSymbol(QualType QT, SVal Arg, - ProgramStateRef State) { +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 nullptr; - if (PtrToHandleLevel > 1) { + return {}; + if (PtrToHandleLevel > 1) // Not supported yet. - return nullptr; - } + return {}; if (PtrToHandleLevel == 0) { - return Arg.getAsSymbol(); + SymbolRef Sym = Arg.getAsSymbol(); + if (Sym) { + return {Sym}; + } else { + return {}; + } } else { assert(PtrToHandleLevel == 1); - if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) - return State->getSVal(*ArgLoc).getAsSymbol(); + if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) { + SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol(); + if (Sym) { + return {Sym}; + } else { + return {}; + } + } } } - return nullptr; + return {}; } void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call, @@ -273,31 +336,27 @@ void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call, if (Arg >= FuncDecl->getNumParams()) break; const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); - SymbolRef Handle = - getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State); - if (!Handle) - continue; + SmallVector<SymbolRef, 1024> Handles = + getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State); // Handled in checkPostCall. if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) || hasFuchsiaAttr<AcquireHandleAttr>(PVD)) continue; - const HandleState *HState = State->get<HStateMap>(Handle); - if (!HState || HState->isEscaped()) - 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; + if (hasFuchsiaAttr<UseHandleAttr>(PVD) || + PVD->getType()->isIntegerType()) { + if (HState->isReleased()) { + reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C); + return; + } } } - if (!hasFuchsiaAttr<UseHandleAttr>(PVD) && - PVD->getType()->isIntegerType()) { - // Working around integer by-value escapes. - State = State->set<HStateMap>(Handle, HandleState::getEscaped()); - } } C.addTransition(State); } @@ -308,6 +367,10 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, 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; @@ -324,7 +387,7 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) { std::string SBuf; llvm::raw_string_ostream OS(SBuf); - OS << "Function '" << FuncDecl->getNameAsString() + OS << "Function '" << FuncDecl->getDeclName() << "' returns an open handle"; return OS.str(); } else @@ -332,6 +395,21 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, }); 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 OS.str(); + } else + return ""; + }); + State = State->set<HStateMap>(RetSym, HandleState::getUnowned()); } for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { @@ -339,63 +417,88 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, break; const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1; - SymbolRef Handle = - getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State); - if (!Handle) - continue; + SmallVector<SymbolRef, 1024> Handles = + getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State); - 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 { + 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 OS.str(); + } 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 OS.str(); + } 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 << "Handle released through " << ParamDiagIdx + OS << "Unowned handle allocated through " << ParamDiagIdx << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; return OS.str(); } else return ""; }); - State = State->set<HStateMap>(Handle, HandleState::getReleased()); + 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()); } - } 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 OS.str(); - } else - return ""; - }); - State = State->set<HStateMap>( - Handle, HandleState::getMaybeAllocated(ResultSymbol)); } } 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) - return ""; - for (auto &Note : Notes) { - std::string Text = Note(BR); - if (!Text.empty()) - return Text; - } - return ""; - }); + 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); } @@ -481,13 +584,14 @@ ProgramStateRef FuchsiaHandleChecker::checkPointerEscape( if (Arg >= FuncDecl->getNumParams()) break; const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); - SymbolRef Handle = - getFuchsiaHandleSymbol(PVD->getType(), Call->getArgSVal(Arg), State); - if (!Handle) - continue; - if (hasFuchsiaAttr<UseHandleAttr>(PVD) || - hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) - UnEscaped.insert(Handle); + 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); + } + } } } @@ -525,6 +629,14 @@ void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym, "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 { |
