aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp312
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 {