;
111static constStringRef HandleTypeName =
"zx_handle_t";
112static constStringRef ErrorTypeName =
"zx_status_t";
116 enum class Kind{ MaybeAllocated, Allocated, Released, Escaped, Unowned } K;
118HandleState(Kind K,
SymbolRefErrorSym) : K(K), ErrorSym(ErrorSym) {}
122 returnK ==
Other.K && ErrorSym ==
Other.ErrorSym;
124 boolisAllocated()
const{
returnK == Kind::Allocated; }
125 boolmaybeAllocated()
const{
returnK == Kind::MaybeAllocated; }
126 bool isReleased()
const{
returnK == Kind::Released; }
127 boolisEscaped()
const{
returnK == Kind::Escaped; }
128 boolisUnowned()
const{
returnK == Kind::Unowned; }
130 staticHandleState getMaybeAllocated(
SymbolRefErrorSym) {
131 returnHandleState(Kind::MaybeAllocated, ErrorSym);
133 staticHandleState getAllocated(
ProgramStateRefState, HandleState S) {
134assert(S.maybeAllocated());
135assert(State->getConstraintManager()
136.isNull(State, S.getErrorSym())
138 returnHandleState(Kind::Allocated,
nullptr);
140 staticHandleState getReleased() {
141 returnHandleState(Kind::Released,
nullptr);
143 staticHandleState getEscaped() {
144 returnHandleState(Kind::Escaped,
nullptr);
146 staticHandleState getUnowned() {
147 returnHandleState(Kind::Unowned,
nullptr);
150 SymbolRefgetErrorSym()
const{
returnErrorSym; }
152 voidProfile(llvm::FoldingSetNodeID &ID)
const{
153 ID.AddInteger(
static_cast<int>(K));
154 ID.AddPointer(ErrorSym);
157LLVM_DUMP_METHOD
void dump(raw_ostream &OS)
const{
163 CASE(Kind::MaybeAllocated)
164 CASE(Kind::Allocated)
165 CASE(Kind::Released)
170OS <<
" ErrorSym: ";
175LLVM_DUMP_METHOD
void dump()
const{
dump(llvm::errs()); }
178template<
typenameAttr>
static boolhasFuchsiaAttr(
const Decl*
D) {
182template<
typenameAttr>
static boolhasFuchsiaUnownedAttr(
const Decl*
D) {
184 D->
getAttr<
Attr>()->getHandleType() ==
"FuchsiaUnowned";
187classFuchsiaHandleChecker
188:
public Checker<check::PostCall, check::PreCall, check::DeadSymbols,
189check::PointerEscape, eval::Assume> {
190 BugTypeLeakBugType{
this,
"Fuchsia handle leak",
"Fuchsia Handle Error",
192 BugTypeDoubleReleaseBugType{
this,
"Fuchsia handle double release",
193 "Fuchsia Handle Error"};
194 BugTypeUseAfterReleaseBugType{
this,
"Fuchsia handle use after release",
195 "Fuchsia Handle Error"};
197 this,
"Fuchsia handle release of unowned handle",
"Fuchsia Handle Error"};
204 boolAssumption)
const;
224StringRef Msg)
const;
227 const char*Sep)
const override;
238 if(!State->get<HStateMap>(Sym))
239N = N->getFirstPred();
243State = N->getState();
244 if(!State->get<HStateMap>(Sym)) {
245 constHandleState *HState = Pred->
getState()->get<HStateMap>(Sym);
246 if(HState && (HState->isAllocated() || HState->maybeAllocated()))
256classFuchsiaHandleSymbolVisitor final :
public SymbolVisitor{
259 if(
const auto*HandleType = S->getType()->getAs<
TypedefType>())
260 if(HandleType->getDecl()->getName() == HandleTypeName)
261Symbols.push_back(S);
276 intPtrToHandleLevel = 0;
284FuchsiaHandleSymbolVisitor Visitor;
285State->scanReachableSymbols(Arg, Visitor);
286 returnVisitor.GetSymbols();
289 if(HandleType->getDecl()->getName() != HandleTypeName)
291 if(PtrToHandleLevel > 1)
295 if(PtrToHandleLevel == 0) {
303assert(PtrToHandleLevel == 1);
304 if(std::optional<Loc> ArgLoc = Arg.
getAs<
Loc>()) {
305 SymbolRefSym = State->getSVal(*ArgLoc).getAsSymbol();
317voidFuchsiaHandleChecker::checkPreCall(
const CallEvent&
Call,
320 const FunctionDecl*FuncDecl = dyn_cast_or_null<FunctionDecl>(
Call.getDecl());
324 for(
unsignedArg = 0; Arg <
Call.getNumArgs(); ++Arg) {
326State = State->set<HStateMap>(Handle, HandleState::getEscaped());
328 C.addTransition(State);
332 for(
unsignedArg = 0; Arg <
Call.getNumArgs(); ++Arg) {
340 if(hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
341hasFuchsiaAttr<AcquireHandleAttr>(PVD))
345 constHandleState *HState = State->get<HStateMap>(Handle);
346 if(!HState || HState->isEscaped())
349 if(hasFuchsiaAttr<UseHandleAttr>(PVD) ||
351 if(HState->isReleased()) {
352reportUseAfterFree(Handle,
Call.getArgSourceRange(Arg),
C);
358 C.addTransition(State);
361voidFuchsiaHandleChecker::checkPostCall(
const CallEvent&
Call,
363 const FunctionDecl*FuncDecl = dyn_cast_or_null<FunctionDecl>(
Call.getDecl());
373std::vector<std::function<std::string(
BugReport& BR)>> Notes;
376 if(TypeDefTy->getDecl()->getName() == ErrorTypeName)
377ResultSymbol =
Call.getReturnValue().getAsSymbol();
380 if(hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
382Notes.push_back([RetSym, FuncDecl](
BugReport&BR) -> std::string {
384 if(PathBR->getInterestingnessKind(RetSym)) {
386llvm::raw_string_ostream OS(SBuf);
388<<
"' returns an open handle";
394State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(
nullptr));
395}
else if(hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) {
398Notes.push_back([RetSym, FuncDecl](
BugReport&BR) -> std::string {
400 if(PathBR->getInterestingnessKind(RetSym)) {
402llvm::raw_string_ostream OS(SBuf);
404<<
"' returns an unowned handle";
409State = State->set<HStateMap>(RetSym, HandleState::getUnowned());
412 for(
unsignedArg = 0; Arg <
Call.getNumArgs(); ++Arg) {
421 constHandleState *HState = State->get<HStateMap>(Handle);
422 if(HState && HState->isEscaped())
424 if(hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
425 if(HState && HState->isReleased()) {
426reportDoubleRelease(Handle,
Call.getArgSourceRange(Arg),
C);
428}
else if(HState && HState->isUnowned()) {
429reportUnownedRelease(Handle,
Call.getArgSourceRange(Arg),
C);
432Notes.push_back([Handle, ParamDiagIdx](
BugReport&BR) -> std::string {
434 if(PathBR->getInterestingnessKind(Handle)) {
436llvm::raw_string_ostream OS(SBuf);
437OS <<
"Handle released through "<< ParamDiagIdx
438<< llvm::getOrdinalSuffix(ParamDiagIdx) <<
" parameter";
443State = State->set<HStateMap>(Handle, HandleState::getReleased());
445}
else if(hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
446Notes.push_back([Handle, ParamDiagIdx](
BugReport&BR) -> std::string {
448 if(PathBR->getInterestingnessKind(Handle)) {
450llvm::raw_string_ostream OS(SBuf);
451OS <<
"Handle allocated through "<< ParamDiagIdx
452<< llvm::getOrdinalSuffix(ParamDiagIdx) <<
" parameter";
457State = State->set<HStateMap>(
458Handle, HandleState::getMaybeAllocated(ResultSymbol));
459}
else if(hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)) {
460Notes.push_back([Handle, ParamDiagIdx](
BugReport&BR) -> std::string {
462 if(PathBR->getInterestingnessKind(Handle)) {
464llvm::raw_string_ostream OS(SBuf);
465OS <<
"Unowned handle allocated through "<< ParamDiagIdx
466<< llvm::getOrdinalSuffix(ParamDiagIdx) <<
" parameter";
471State = State->set<HStateMap>(Handle, HandleState::getUnowned());
472}
else if(!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
479State = State->set<HStateMap>(Handle, HandleState::getEscaped());
484 if(!Notes.empty()) {
485 T=
C.getNoteTag([
this, Notes{std::move(Notes)}](
487 if(&BR.
getBugType() != &UseAfterReleaseBugType &&
489&BR.
getBugType() != &DoubleReleaseBugType &&
492 for(
auto&
Note: Notes) {
500 C.addTransition(State,
T);
503voidFuchsiaHandleChecker::checkDeadSymbols(
SymbolReaper&SymReaper,
507HStateMapTy TrackedHandles = State->get<HStateMap>();
508 for(
auto&CurItem : TrackedHandles) {
509 SymbolRefErrorSym = CurItem.second.getErrorSym();
514 if(!SymReaper.
isDead(CurItem.first) ||
515(ErrorSym && !SymReaper.
isDead(ErrorSym)))
517 if(CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
518LeakedSyms.push_back(CurItem.first);
519State = State->remove<HStateMap>(CurItem.first);
523 if(!LeakedSyms.empty())
524N = reportLeaks(LeakedSyms,
C, N);
526 C.addTransition(State, N);
541 boolAssumption)
const{
544HStateMapTy TrackedHandles = State->get<HStateMap>();
545 for(
auto&CurItem : TrackedHandles) {
549State = State->remove<HStateMap>(CurItem.first);
551 SymbolRefErrorSym = CurItem.second.getErrorSym();
557 if(CurItem.second.maybeAllocated())
558State = State->set<HStateMap>(
559CurItem.first, HandleState::getAllocated(State, CurItem.second));
562 if(CurItem.second.maybeAllocated())
563State = State->remove<HStateMap>(CurItem.first);
573 Call? dyn_cast_or_null<FunctionDecl>(
Call->getDecl()) : nullptr;
575llvm::DenseSet<SymbolRef> UnEscaped;
580 for(
unsignedArg = 0; Arg <
Call->getNumArgs(); ++Arg) {
587 if(hasFuchsiaAttr<UseHandleAttr>(PVD) ||
588hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
589UnEscaped.insert(Handle);
597 for(
autoI : State->get<HStateMap>()) {
598 if(Escaped.count(I.first) && !UnEscaped.count(I.first))
599State = State->set<HStateMap>(I.first, HandleState::getEscaped());
600 if(
const auto*SD = dyn_cast<SymbolDerived>(I.first)) {
601 autoParentSym = SD->getParentSymbol();
602 if(Escaped.count(ParentSym))
603State = State->set<HStateMap>(I.first, HandleState::getEscaped());
613 ExplodedNode*ErrNode =
C.generateNonFatalErrorNode(
C.getState(), Pred);
614 for(
SymbolRefLeakedHandle : LeakedHandles) {
615reportBug(LeakedHandle, ErrNode,
C,
nullptr, LeakBugType,
616 "Potential leak of handle");
621voidFuchsiaHandleChecker::reportDoubleRelease(
SymbolRefHandleSym,
625reportBug(HandleSym, ErrNode,
C, &
Range, DoubleReleaseBugType,
626 "Releasing a previously released handle");
629voidFuchsiaHandleChecker::reportUnownedRelease(
SymbolRefHandleSym,
633reportBug(HandleSym, ErrNode,
C, &
Range, ReleaseUnownedBugType,
634 "Releasing an unowned handle");
637voidFuchsiaHandleChecker::reportUseAfterFree(
SymbolRefHandleSym,
641reportBug(HandleSym, ErrNode,
C, &
Range, UseAfterReleaseBugType,
642 "Using a previously released handle");
652std::unique_ptr<PathSensitiveBugReport> R;
653 if(
Type.isSuppressOnSink()) {
657assert(S &&
"Statement cannot be null.");
662R = std::make_unique<PathSensitiveBugReport>(
663 Type, Msg, ErrorNode, LocUsedForUniqueing,
668R = std::make_unique<PathSensitiveBugReport>(
Type, Msg, ErrorNode);
670R->addRange(*
Range);
671R->markInteresting(Sym);
672 C.emitReport(std::move(R));
679boolento::shouldRegisterFuchsiaHandleChecker(
const CheckerManager&mgr) {
683voidFuchsiaHandleChecker::printState(raw_ostream &Out,
ProgramStateRefState,
684 const char*NL,
const char*Sep)
const{
686HStateMapTy StateMap = State->get<HStateMap>();
688 if(!StateMap.isEmpty()) {
689Out << Sep <<
"FuchsiaHandleChecker :"<< NL;
690 for(
const auto&[Sym, HandleState] : StateMap) {
693HandleState.dump(Out);
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
static const ExplodedNode * getAcquireSite(const ExplodedNode *N, SymbolRef Sym, CheckerContext &Ctx)
static SmallVector< SymbolRef, 1024 > getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State)
Returns the symbols extracted from the argument or empty vector if it cannot be found.
static bool isReleased(SymbolRef Sym, CheckerContext &C)
Check if the memory associated with this symbol was released.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
C Language Family Type Representation.
Attr - This represents one attribute.
Decl - This represents one declaration (or definition), e.g.
Represents a function declaration or definition.
const ParmVarDecl * getParamDecl(unsigned i) const
QualType getReturnType() const
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
const Decl * getDecl() const
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Represents a parameter to a function.
unsigned getFunctionScopeIndex() const
Returns the index of this parameter in its prototype or method scope.
A (possibly-)qualified type.
A trivial tuple used to represent a source range.
Stmt - This represents one statement.
The base class of the type hierarchy.
bool isStructureType() const
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
bool isReferenceType() const
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
bool isAnyPointerType() const
const T * getAs() const
Member-template getAs<specific type>'.
This class provides an interface through which checkers can create individual bug reports.
const BugType & getBugType() const
Represents an abstract call to a function or method along a particular path.
virtual void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const
See CheckerManager::runCheckersForPrintState.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
bool isConstrainedFalse() const
Return true if the constraint is perfectly constrained to 'false'.
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
ConditionTruthVal isNull(ProgramStateRef State, SymbolRef Sym)
Convenience method to query the state to see if a symbol is null or not null, or if neither assumptio...
const ProgramStateRef & getState() const
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
const LocationContext * getLocationContext() const
ExplodedNode * getFirstPred()
The tag upon which the TagVisitor reacts.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
A Range represents the closed range [from, to].
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
virtual void dumpToStream(raw_ostream &os) const
A class responsible for cleaning up unused symbols.
bool isDead(SymbolRef sym)
Returns whether or not a symbol has been confirmed dead.
virtual bool VisitSymbol(SymbolRef sym)=0
A visitor method invoked by ProgramStateManager::scanReachableSymbols.
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
@ PSK_DirectEscapeOnCall
The pointer has been passed to a function call directly.
@ PSK_IndirectEscapeOnCall
The pointer has been passed to a function indirectly.
@ PSK_EscapeOutParameters
Escape for a new symbol that was generated into a region that the analyzer cannot follow during a con...
llvm::DenseSet< SymbolRef > InvalidatedSymbols
The JSON file list parser is used to communicate input to InstallAPI.
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
const FunctionProtoType * T
@ Other
Other implicit parameter.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4