94 using namespace clang;
99 static const StringRef HandleTypeName =
"zx_handle_t";
100 static const StringRef ErrorTypeName =
"zx_status_t";
104 enum class Kind { MaybeAllocated, Allocated, Released, Escaped } K;
106 HandleState(
Kind K,
SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
109 bool operator==(
const HandleState &Other)
const {
110 return K == Other.K && ErrorSym == Other.ErrorSym;
112 bool isAllocated()
const {
return K == Kind::Allocated; }
113 bool maybeAllocated()
const {
return K == Kind::MaybeAllocated; }
114 bool isReleased()
const {
return K == Kind::Released; }
115 bool isEscaped()
const {
return K == Kind::Escaped; }
117 static HandleState getMaybeAllocated(
SymbolRef ErrorSym) {
118 return HandleState(Kind::MaybeAllocated, ErrorSym);
121 assert(S.maybeAllocated());
122 assert(State->getConstraintManager()
123 .isNull(State, S.getErrorSym())
125 return HandleState(Kind::Allocated,
nullptr);
127 static HandleState getReleased() {
128 return HandleState(Kind::Released,
nullptr);
130 static HandleState getEscaped() {
131 return HandleState(Kind::Escaped,
nullptr);
134 SymbolRef getErrorSym()
const {
return ErrorSym; }
136 void Profile(llvm::FoldingSetNodeID &
ID)
const {
137 ID.AddInteger(static_cast<int>(K));
138 ID.AddPointer(ErrorSym);
141 LLVM_DUMP_METHOD
void dump(raw_ostream &
OS)
const {
147 CASE(Kind::MaybeAllocated)
148 CASE(Kind::Allocated)
154 LLVM_DUMP_METHOD
void dump()
const {
dump(llvm::errs()); }
157 template <
typename Attr>
static bool hasFuchsiaAttr(
const Decl *D) {
161 class FuchsiaHandleChecker
162 :
public Checker<check::PostCall, check::PreCall, check::DeadSymbols,
163 check::PointerEscape, eval::Assume> {
164 BugType LeakBugType{
this,
"Fuchsia handle leak",
"Fuchsia Handle Error",
166 BugType DoubleReleaseBugType{
this,
"Fuchsia handle double release",
167 "Fuchsia Handle Error"};
168 BugType UseAfterReleaseBugType{
this,
"Fuchsia handle use after release",
169 "Fuchsia Handle Error"};
172 void checkPreCall(
const CallEvent &Call, CheckerContext &C)
const;
173 void checkPostCall(
const CallEvent &Call, CheckerContext &C)
const;
174 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C)
const;
176 bool Assumption)
const;
183 CheckerContext &C, ExplodedNode *Pred)
const;
186 CheckerContext &C)
const;
189 CheckerContext &C)
const;
191 void reportBug(
SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,
193 StringRef Msg)
const;
195 void printState(raw_ostream &Out,
ProgramStateRef State,
const char *NL,
196 const char *Sep)
const override;
203 CheckerContext &Ctx) {
207 if (!State->get<HStateMap>(Sym))
208 N = N->getFirstPred();
210 const ExplodedNode *Pred = N;
212 State = N->getState();
213 if (!State->get<HStateMap>(Sym)) {
214 const HandleState *HState = Pred->getState()->get<HStateMap>(Sym);
215 if (HState && (HState->isAllocated() || HState->maybeAllocated()))
219 N = N->getFirstPred();
228 int PtrToHandleLevel = 0;
234 if (HandleType->getDecl()->getName() != HandleTypeName)
236 if (PtrToHandleLevel > 1) {
241 if (PtrToHandleLevel == 0) {
242 return Arg.getAsSymbol();
244 assert(PtrToHandleLevel == 1);
246 return State->getSVal(*ArgLoc).getAsSymbol();
252 void FuchsiaHandleChecker::checkPreCall(
const CallEvent &Call,
253 CheckerContext &C)
const {
255 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
259 for (
unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
260 if (
SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol())
261 State = State->set<HStateMap>(Handle, HandleState::getEscaped());
263 C.addTransition(State);
267 for (
unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
277 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
278 hasFuchsiaAttr<AcquireHandleAttr>(PVD))
281 const HandleState *HState = State->get<HStateMap>(Handle);
282 if (!HState || HState->isEscaped())
286 if (HState->isReleased()) {
287 reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
291 if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
294 State = State->set<HStateMap>(Handle, HandleState::getEscaped());
297 C.addTransition(State);
300 void FuchsiaHandleChecker::checkPostCall(
const CallEvent &Call,
301 CheckerContext &C)
const {
302 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
308 std::vector<std::function<std::string(BugReport & BR)>> Notes;
311 if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
312 ResultSymbol = Call.getReturnValue().getAsSymbol();
315 if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
316 SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
318 State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(
nullptr));
321 for (
unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
330 const HandleState *HState = State->get<HStateMap>(Handle);
331 if (HState && HState->isEscaped())
333 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
334 if (HState && HState->isReleased()) {
335 reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
338 Notes.push_back([Handle](BugReport &BR) {
339 auto *PathBR =
static_cast<PathSensitiveBugReport *
>(&BR);
340 if (
auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
341 return "Handle released here.";
345 State = State->set<HStateMap>(Handle, HandleState::getReleased());
347 }
else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
348 Notes.push_back([Handle](BugReport &BR) {
349 auto *PathBR =
static_cast<PathSensitiveBugReport *
>(&BR);
350 if (
auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
351 return "Handle allocated here.";
355 State = State->set<HStateMap>(
356 Handle, HandleState::getMaybeAllocated(ResultSymbol));
359 const NoteTag *T =
nullptr;
360 if (!Notes.empty()) {
362 [
this, Notes{std::move(Notes)}](BugReport &BR) -> std::string {
363 if (&BR.getBugType() != &UseAfterReleaseBugType &&
364 &BR.getBugType() != &LeakBugType &&
365 &BR.getBugType() != &DoubleReleaseBugType)
367 for (
auto &Note : Notes) {
368 std::string
Text = Note(BR);
375 C.addTransition(State, T);
378 void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
379 CheckerContext &C)
const {
382 HStateMapTy TrackedHandles = State->get<HStateMap>();
383 for (
auto &CurItem : TrackedHandles) {
384 if (!SymReaper.isDead(CurItem.first))
386 if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
387 LeakedSyms.push_back(CurItem.first);
388 State = State->remove<HStateMap>(CurItem.first);
391 ExplodedNode *N = C.getPredecessor();
392 if (!LeakedSyms.empty())
393 N = reportLeaks(LeakedSyms, C, N);
395 C.addTransition(State, N);
410 bool Assumption)
const {
412 ConstraintManager &Cmr = State->getConstraintManager();
413 HStateMapTy TrackedHandles = State->get<HStateMap>();
414 for (
auto &CurItem : TrackedHandles) {
415 ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first);
416 if (HandleVal.isConstrainedTrue()) {
418 State = State->remove<HStateMap>(CurItem.first);
420 SymbolRef ErrorSym = CurItem.second.getErrorSym();
423 ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym);
424 if (ErrorVal.isConstrainedTrue()) {
426 if (CurItem.second.maybeAllocated())
427 State = State->set<HStateMap>(
428 CurItem.first, HandleState::getAllocated(State, CurItem.second));
429 }
else if (ErrorVal.isConstrainedFalse()) {
431 if (CurItem.second.maybeAllocated())
432 State = State->remove<HStateMap>(CurItem.first);
442 Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) :
nullptr;
449 for (
unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {
457 if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
458 hasFuchsiaAttr<ReleaseHandleAttr>(PVD))
459 UnEscaped.insert(Handle);
465 for (
auto I : State->get<HStateMap>()) {
466 if (Escaped.count(I.first) && !UnEscaped.count(I.first))
467 State = State->set<HStateMap>(I.first, HandleState::getEscaped());
468 if (
const auto *SD = dyn_cast<SymbolDerived>(I.first)) {
469 auto ParentSym = SD->getParentSymbol();
470 if (Escaped.count(ParentSym))
471 State = State->set<HStateMap>(I.first, HandleState::getEscaped());
480 CheckerContext &C, ExplodedNode *Pred)
const {
481 ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred);
482 for (
SymbolRef LeakedHandle : LeakedHandles) {
483 reportBug(LeakedHandle, ErrNode, C,
nullptr, LeakBugType,
484 "Potential leak of handle");
489 void FuchsiaHandleChecker::reportDoubleRelease(
SymbolRef HandleSym,
491 CheckerContext &C)
const {
492 ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
493 reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
494 "Releasing a previously released handle");
497 void FuchsiaHandleChecker::reportUseAfterFree(
SymbolRef HandleSym,
499 CheckerContext &C)
const {
500 ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
501 reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType,
502 "Using a previously released handle");
505 void FuchsiaHandleChecker::reportBug(
SymbolRef Sym, ExplodedNode *ErrorNode,
508 const BugType &
Type, StringRef Msg)
const {
512 std::unique_ptr<PathSensitiveBugReport> R;
513 if (Type.isSuppressOnSink()) {
514 const ExplodedNode *AcquireNode =
getAcquireSite(ErrorNode, Sym, C);
516 PathDiagnosticLocation LocUsedForUniqueing =
518 AcquireNode->getStmtForDiagnostics(), C.getSourceManager(),
519 AcquireNode->getLocationContext());
521 R = std::make_unique<PathSensitiveBugReport>(
522 Type, Msg, ErrorNode, LocUsedForUniqueing,
523 AcquireNode->getLocationContext()->getDecl());
527 R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
530 R->markInteresting(Sym);
531 C.emitReport(std::move(R));
534 void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
535 mgr.registerChecker<FuchsiaHandleChecker>();
538 bool ento::shouldRegisterFuchsiaHandleChecker(
const LangOptions &LO) {
542 void FuchsiaHandleChecker::printState(raw_ostream &Out,
ProgramStateRef State,
543 const char *NL,
const char *Sep)
const {
545 HStateMapTy StateMap = State->get<HStateMap>();
547 if (!StateMap.isEmpty()) {
548 Out << Sep <<
"FuchsiaHandleChecker :" << NL;
549 for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
551 I.getKey()->dumpToStream(Out);
553 I.getData().
dump(Out);
Represents a function declaration or definition.
A (possibly-)qualified type.
bool operator==(CanQual< T > x, CanQual< U > y)
llvm::DenseSet< SymbolRef > InvalidatedSymbols
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee...
C Language Family Type Representation.
Decl - This represents one declaration (or definition), e.g.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
The pointer has been passed to a function indirectly.
The base class of the type hierarchy.
QualType getReturnType() const
const T * getAs() const
Member-template getAs<specific type>'.
Represents a parameter to a function.
const SymExpr * SymbolRef
bool isReferenceType() const
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
Escape for a new symbol that was generated into a region that the analyzer cannot follow during a con...
static SymbolRef getFuchsiaHandleSymbol(QualType QT, SVal Arg, ProgramStateRef State)
Returns the symbols extracted from the argument or null if it cannot be found.
The pointer has been passed to a function call directly.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
const ParmVarDecl * getParamDecl(unsigned i) const
bool isAnyPointerType() const
static const ExplodedNode * getAcquireSite(const ExplodedNode *N, SymbolRef Sym, CheckerContext &Ctx)
Dataflow Directional Tag Classes.
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
static bool isReleased(SymbolRef Sym, CheckerContext &C)
Check if the memory associated with this symbol was released.
A trivial tuple used to represent a source range.
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Attr - This represents one attribute.