103 #include "llvm/ADT/StringExtras.h"
105 using namespace clang;
106 using namespace ento;
110 static const StringRef HandleTypeName =
"zx_handle_t";
111 static const StringRef ErrorTypeName =
"zx_status_t";
115 enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K;
117 HandleState(
Kind K,
SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
120 bool operator==(
const HandleState &Other)
const {
121 return K == Other.K && ErrorSym == Other.ErrorSym;
123 bool isAllocated()
const {
return K == Kind::Allocated; }
124 bool maybeAllocated()
const {
return K == Kind::MaybeAllocated; }
125 bool isReleased()
const {
return K == Kind::Released; }
126 bool isEscaped()
const {
return K == Kind::Escaped; }
127 bool isUnowned()
const {
return K == Kind::Unowned; }
129 static HandleState getMaybeAllocated(
SymbolRef ErrorSym) {
130 return HandleState(Kind::MaybeAllocated, ErrorSym);
133 assert(S.maybeAllocated());
134 assert(
State->getConstraintManager()
135 .isNull(
State, S.getErrorSym())
137 return HandleState(Kind::Allocated,
nullptr);
139 static HandleState getReleased() {
140 return HandleState(Kind::Released,
nullptr);
142 static HandleState getEscaped() {
143 return HandleState(Kind::Escaped,
nullptr);
145 static HandleState getUnowned() {
146 return HandleState(Kind::Unowned,
nullptr);
149 SymbolRef getErrorSym()
const {
return ErrorSym; }
151 void Profile(llvm::FoldingSetNodeID &
ID)
const {
152 ID.AddInteger(
static_cast<int>(K));
153 ID.AddPointer(ErrorSym);
156 LLVM_DUMP_METHOD
void dump(raw_ostream &OS)
const {
162 CASE(Kind::MaybeAllocated)
163 CASE(Kind::Allocated)
170 ErrorSym->dumpToStream(OS);
174 LLVM_DUMP_METHOD
void dump()
const {
dump(llvm::errs()); }
177 template <
typename Attr>
static bool hasFuchsiaAttr(
const Decl *D) {
181 template <
typename Attr>
static bool hasFuchsiaUnownedAttr(
const Decl *D) {
183 D->
getAttr<
Attr>()->getHandleType() ==
"FuchsiaUnowned";
186 class FuchsiaHandleChecker
187 :
public Checker<check::PostCall, check::PreCall, check::DeadSymbols,
188 check::PointerEscape, eval::Assume> {
189 BugType LeakBugType{
this,
"Fuchsia handle leak",
"Fuchsia Handle Error",
191 BugType DoubleReleaseBugType{
this,
"Fuchsia handle double release",
192 "Fuchsia Handle Error"};
193 BugType UseAfterReleaseBugType{
this,
"Fuchsia handle use after release",
194 "Fuchsia Handle Error"};
195 BugType ReleaseUnownedBugType{
196 this,
"Fuchsia handle release of unowned handle",
"Fuchsia Handle Error"};
199 void checkPreCall(
const CallEvent &Call, CheckerContext &C)
const;
200 void checkPostCall(
const CallEvent &Call, CheckerContext &C)
const;
201 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C)
const;
203 bool Assumption)
const;
206 const CallEvent *Call,
210 CheckerContext &C, ExplodedNode *Pred)
const;
213 CheckerContext &C)
const;
216 CheckerContext &C)
const;
219 CheckerContext &C)
const;
221 void reportBug(
SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,
223 StringRef Msg)
const;
226 const char *Sep)
const override;
233 CheckerContext &Ctx) {
237 if (!
State->get<HStateMap>(Sym))
238 N = N->getFirstPred();
240 const ExplodedNode *Pred = N;
242 State = N->getState();
243 if (!
State->get<HStateMap>(Sym)) {
244 const HandleState *HState = Pred->getState()->get<HStateMap>(Sym);
245 if (HState && (HState->isAllocated() || HState->maybeAllocated()))
249 N = N->getFirstPred();
255 class FuchsiaHandleSymbolVisitor final :
public SymbolVisitor {
258 if (
const auto *HandleType = S->getType()->getAs<
TypedefType>())
259 if (HandleType->getDecl()->getName() == HandleTypeName)
260 Symbols.push_back(S);
275 int PtrToHandleLevel = 0;
283 FuchsiaHandleSymbolVisitor Visitor;
284 State->scanReachableSymbols(Arg, Visitor);
285 return Visitor.GetSymbols();
288 if (HandleType->getDecl()->getName() != HandleTypeName)
290 if (PtrToHandleLevel > 1)
294 if (PtrToHandleLevel == 0) {
302 assert(PtrToHandleLevel == 1);
316 void FuchsiaHandleChecker::checkPreCall(
const CallEvent &Call,
317 CheckerContext &C)
const {
319 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(
Call.getDecl());
323 for (
unsigned Arg = 0; Arg <
Call.getNumArgs(); ++Arg) {
325 State =
State->set<HStateMap>(Handle, HandleState::getEscaped());
331 for (
unsigned Arg = 0; Arg <
Call.getNumArgs(); ++Arg) {
339 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
340 hasFuchsiaAttr<AcquireHandleAttr>(PVD))
344 const HandleState *HState =
State->get<HStateMap>(Handle);
345 if (!HState || HState->isEscaped())
348 if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
350 if (HState->isReleased()) {
351 reportUseAfterFree(Handle,
Call.getArgSourceRange(Arg), C);
360 void FuchsiaHandleChecker::checkPostCall(
const CallEvent &Call,
361 CheckerContext &C)
const {
362 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(
Call.getDecl());
372 std::vector<std::function<
std::string(BugReport & BR)>> Notes;
375 if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
376 ResultSymbol =
Call.getReturnValue().getAsSymbol();
379 if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
381 Notes.push_back([RetSym, FuncDecl](BugReport &BR) ->
std::string {
382 auto *PathBR =
static_cast<PathSensitiveBugReport *
>(&BR);
383 if (
auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
385 llvm::raw_string_ostream
OS(SBuf);
387 <<
"' returns an open handle";
393 State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(
nullptr));
394 }
else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) {
397 Notes.push_back([RetSym, FuncDecl](BugReport &BR) ->
std::string {
398 auto *PathBR =
static_cast<PathSensitiveBugReport *
>(&BR);
399 if (
auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
401 llvm::raw_string_ostream
OS(SBuf);
403 <<
"' returns an unowned handle";
408 State =
State->set<HStateMap>(RetSym, HandleState::getUnowned());
411 for (
unsigned Arg = 0; Arg <
Call.getNumArgs(); ++Arg) {
420 const HandleState *HState =
State->get<HStateMap>(Handle);
421 if (HState && HState->isEscaped())
423 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
424 if (HState && HState->isReleased()) {
425 reportDoubleRelease(Handle,
Call.getArgSourceRange(Arg), C);
427 }
else if (HState && HState->isUnowned()) {
428 reportUnownedRelease(Handle,
Call.getArgSourceRange(Arg), C);
431 Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) ->
std::string {
432 auto *PathBR =
static_cast<PathSensitiveBugReport *
>(&BR);
433 if (
auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
435 llvm::raw_string_ostream
OS(SBuf);
436 OS <<
"Handle released through " << ParamDiagIdx
437 << llvm::getOrdinalSuffix(ParamDiagIdx) <<
" parameter";
442 State =
State->set<HStateMap>(Handle, HandleState::getReleased());
444 }
else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
445 Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) ->
std::string {
446 auto *PathBR =
static_cast<PathSensitiveBugReport *
>(&BR);
447 if (
auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
449 llvm::raw_string_ostream
OS(SBuf);
450 OS <<
"Handle allocated through " << ParamDiagIdx
451 << llvm::getOrdinalSuffix(ParamDiagIdx) <<
" parameter";
457 Handle, HandleState::getMaybeAllocated(ResultSymbol));
458 }
else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)) {
459 Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) ->
std::string {
460 auto *PathBR =
static_cast<PathSensitiveBugReport *
>(&BR);
461 if (
auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
463 llvm::raw_string_ostream
OS(SBuf);
464 OS <<
"Unowned handle allocated through " << ParamDiagIdx
465 << llvm::getOrdinalSuffix(ParamDiagIdx) <<
" parameter";
470 State =
State->set<HStateMap>(Handle, HandleState::getUnowned());
471 }
else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
478 State =
State->set<HStateMap>(Handle, HandleState::getEscaped());
482 const NoteTag *T =
nullptr;
483 if (!Notes.empty()) {
484 T =
C.getNoteTag([
this, Notes{std::move(Notes)}](
486 if (&BR.getBugType() != &UseAfterReleaseBugType &&
487 &BR.getBugType() != &LeakBugType &&
488 &BR.getBugType() != &DoubleReleaseBugType &&
489 &BR.getBugType() != &ReleaseUnownedBugType)
491 for (
auto &Note : Notes) {
499 C.addTransition(
State, T);
502 void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
503 CheckerContext &C)
const {
506 HStateMapTy TrackedHandles =
State->get<HStateMap>();
507 for (
auto &CurItem : TrackedHandles) {
508 SymbolRef ErrorSym = CurItem.second.getErrorSym();
513 if (!SymReaper.isDead(CurItem.first) ||
514 (ErrorSym && !SymReaper.isDead(ErrorSym)))
516 if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
517 LeakedSyms.push_back(CurItem.first);
518 State =
State->remove<HStateMap>(CurItem.first);
521 ExplodedNode *N =
C.getPredecessor();
522 if (!LeakedSyms.empty())
523 N = reportLeaks(LeakedSyms, C, N);
525 C.addTransition(
State, N);
540 bool Assumption)
const {
542 ConstraintManager &Cmr =
State->getConstraintManager();
543 HStateMapTy TrackedHandles =
State->get<HStateMap>();
544 for (
auto &CurItem : TrackedHandles) {
545 ConditionTruthVal HandleVal = Cmr.isNull(
State, CurItem.first);
546 if (HandleVal.isConstrainedTrue()) {
548 State =
State->remove<HStateMap>(CurItem.first);
550 SymbolRef ErrorSym = CurItem.second.getErrorSym();
553 ConditionTruthVal ErrorVal = Cmr.isNull(
State, ErrorSym);
554 if (ErrorVal.isConstrainedTrue()) {
556 if (CurItem.second.maybeAllocated())
558 CurItem.first, HandleState::getAllocated(
State, CurItem.second));
559 }
else if (ErrorVal.isConstrainedFalse()) {
561 if (CurItem.second.maybeAllocated())
562 State =
State->remove<HStateMap>(CurItem.first);
572 Call ? dyn_cast_or_null<FunctionDecl>(
Call->getDecl()) : nullptr;
579 for (
unsigned Arg = 0; Arg <
Call->getNumArgs(); ++Arg) {
586 if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
587 hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
588 UnEscaped.insert(Handle);
596 for (
auto I :
State->get<HStateMap>()) {
597 if (Escaped.count(I.first) && !UnEscaped.count(I.first))
598 State =
State->set<HStateMap>(I.first, HandleState::getEscaped());
599 if (
const auto *SD = dyn_cast<SymbolDerived>(I.first)) {
600 auto ParentSym = SD->getParentSymbol();
601 if (Escaped.count(ParentSym))
602 State =
State->set<HStateMap>(I.first, HandleState::getEscaped());
611 CheckerContext &C, ExplodedNode *Pred)
const {
612 ExplodedNode *ErrNode =
C.generateNonFatalErrorNode(
C.getState(), Pred);
613 for (
SymbolRef LeakedHandle : LeakedHandles) {
614 reportBug(LeakedHandle, ErrNode, C,
nullptr, LeakBugType,
615 "Potential leak of handle");
620 void FuchsiaHandleChecker::reportDoubleRelease(
SymbolRef HandleSym,
622 CheckerContext &C)
const {
623 ExplodedNode *ErrNode =
C.generateErrorNode(
C.getState());
624 reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
625 "Releasing a previously released handle");
628 void FuchsiaHandleChecker::reportUnownedRelease(
SymbolRef HandleSym,
630 CheckerContext &C)
const {
631 ExplodedNode *ErrNode =
C.generateErrorNode(
C.getState());
632 reportBug(HandleSym, ErrNode, C, &Range, ReleaseUnownedBugType,
633 "Releasing an unowned handle");
636 void FuchsiaHandleChecker::reportUseAfterFree(
SymbolRef HandleSym,
638 CheckerContext &C)
const {
639 ExplodedNode *ErrNode =
C.generateErrorNode(
C.getState());
640 reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType,
641 "Using a previously released handle");
644 void FuchsiaHandleChecker::reportBug(
SymbolRef Sym, ExplodedNode *ErrorNode,
647 const BugType &
Type, StringRef Msg)
const {
651 std::unique_ptr<PathSensitiveBugReport> R;
652 if (
Type.isSuppressOnSink()) {
653 const ExplodedNode *AcquireNode =
getAcquireSite(ErrorNode, Sym, C);
655 PathDiagnosticLocation LocUsedForUniqueing =
657 AcquireNode->getStmtForDiagnostics(),
C.getSourceManager(),
658 AcquireNode->getLocationContext());
660 R = std::make_unique<PathSensitiveBugReport>(
661 Type, Msg, ErrorNode, LocUsedForUniqueing,
662 AcquireNode->getLocationContext()->getDecl());
666 R = std::make_unique<PathSensitiveBugReport>(
Type, Msg, ErrorNode);
669 R->markInteresting(Sym);
670 C.emitReport(std::move(R));
673 void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
674 mgr.registerChecker<FuchsiaHandleChecker>();
677 bool ento::shouldRegisterFuchsiaHandleChecker(
const CheckerManager &mgr) {
682 const char *NL,
const char *Sep)
const {
684 HStateMapTy StateMap =
State->get<HStateMap>();
686 if (!StateMap.isEmpty()) {
687 Out << Sep <<
"FuchsiaHandleChecker :" << NL;
688 for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
690 I.getKey()->dumpToStream(Out);
692 I.getData().dump(Out);