17 using namespace clang;
24 return "Use-after-release";
28 return "-dealloc sent to non-exclusively owned object";
30 return "freeing non-exclusively owned object";
32 return "Object autoreleased too many times";
34 return "Method should return an owned object";
38 return "Leak of returned object";
40 llvm_unreachable(
"Unknown RefCountBugType");
46 return "Reference-counted object is used after it is released";
48 return "Incorrect decrement of the reference count of an object that is " 49 "not owned at this point by the caller";
51 return "-dealloc sent to object that may be referenced elsewhere";
53 return "'free' called on an object that may be referenced elsewhere";
55 return "Object autoreleased too many times";
57 return "Object with a +0 retain count returned to caller where a +1 " 58 "(owning) retain count is expected";
63 llvm_unreachable(
"Unknown RefCountBugType");
69 BT(BT), Checker(Checker) {}
73 return isa<IntegerLiteral>(E) ||
74 isa<CharacterLiteral>(E) ||
75 isa<FloatingLiteral>(E) ||
76 isa<ObjCBoolLiteralExpr>(E) ||
77 isa<CXXBoolLiteralExpr>(E);
93 static bool shouldGenerateNote(llvm::raw_string_ostream &os,
98 RefVal PrevV = *PrevT;
103 assert(!PrevV.hasSameState(CurrV) &&
"The state should have changed.");
107 assert(CurrV.getCombinedCounts() == 0);
108 os <<
"Object released by directly sending the '-dealloc' message";
114 if (!PrevV.hasSameState(CurrV))
115 switch (CurrV.getKind()) {
118 if (PrevV.getCount() == CurrV.getCount()) {
120 if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())
123 assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
124 os <<
"Object autoreleased";
128 if (PrevV.getCount() > CurrV.getCount())
129 os <<
"Reference count decremented.";
131 os <<
"Reference count incremented.";
133 if (
unsigned Count = CurrV.getCount())
134 os <<
" The object now has a +" << Count <<
" retain count.";
139 if (CurrV.getIvarAccessHistory() ==
141 CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) {
142 os <<
"Strong instance variable relinquished. ";
144 os <<
"Object released.";
149 if (CurrV.getAutoreleaseCount())
152 os <<
"Object returned to caller as an owning reference (single " 153 "retain count transferred to caller)";
157 os <<
"Object returned to caller with a +0 retain count";
176 for (
unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++)
177 if (
const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())
178 if (
const auto *TR = dyn_cast<TypedValueRegion>(MR))
179 if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymExpr() == Sym)
186 if (
const auto *ME = dyn_cast<MemberExpr>(Callee)) {
187 if (ME->getMemberDecl()->getNameAsString() !=
"alloc")
190 if (
const auto *DRE = dyn_cast<DeclRefExpr>(This)) {
195 if (
const auto *RD = dyn_cast<CXXRecordDecl>(VD->
getDeclContext()))
196 return RD->getNameAsString();
203 static std::string findAllocatedObjectName(
const Stmt *S,
QualType QT) {
204 if (
const auto *CE = dyn_cast<CallExpr>(S))
205 if (
auto Out = findMetaClassAlloc(CE->getCallee()))
214 llvm::raw_string_ostream &os) {
216 if (
const CallExpr *CE = dyn_cast<CallExpr>(S)) {
219 SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
226 if (
const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) {
231 os <<
"function call";
233 }
else if (isa<CXXNewExpr>(S)) {
234 os <<
"Operator 'new'";
236 assert(isa<ObjCMessageExpr>(S));
240 switch (Call->getMessageKind()) {
254 auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE);
264 os <<
"a Core Foundation object of type '" 267 os <<
"an OSObject of type '" << findAllocatedObjectName(S, Sym->
getType())
275 if (!isa<ObjCObjectPointerType>(T)) {
276 os <<
"an Objective-C object with a ";
284 if (CurrV.isOwned()) {
285 os <<
"+1 retain count";
287 assert(CurrV.isNotOwned());
288 os <<
"+0 retain count";
292 os <<
" into an out parameter '";
293 const ParmVarDecl *PVD = (*CE)->parameters()[*Idx];
298 QualType RT = (*CE)->getResultType();
300 SVal RV = (*CE)->getReturnValue();
301 if (CurrSt->isNull(RV).isConstrainedTrue()) {
302 os <<
" (assuming the call returns zero)";
303 }
else if (CurrSt->isNonNull(RV).isConstrainedTrue()) {
304 os <<
" (assuming the call returns non-zero)";
320 RefCountReportVisitor(
SymbolRef sym) : Sym(sym) {}
322 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
328 std::shared_ptr<PathDiagnosticPiece> VisitNode(
const ExplodedNode *N,
337 class RefLeakReportVisitor :
public RefCountReportVisitor {
339 RefLeakReportVisitor(
SymbolRef sym) : RefCountReportVisitor(sym) {}
371 static std::shared_ptr<PathDiagnosticEventPiece>
384 llvm::raw_string_ostream os(sbuf);
386 for (
unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {
389 if (!PVD->
hasAttr<OSConsumedAttr>())
392 if (
SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
396 if (!CountBeforeCall || !CountAtExit)
399 unsigned CountBefore = CountBeforeCall->
getCount();
400 unsigned CountAfter = CountAtExit->getCount();
402 bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
407 os <<
"' is marked as consuming, but the function did not consume " 408 <<
"the reference\n";
413 if (os.str().empty())
417 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
421 static std::shared_ptr<PathDiagnosticEventPiece>
435 const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion());
436 const auto *PVD = cast<ParmVarDecl>(VR->getDecl());
440 llvm::raw_string_ostream os(s);
441 os <<
"Parameter '" << PVD->getNameAsString() <<
"' starts at +";
442 if (CurrT->getCount() == 1) {
443 os <<
"1, as it is marked as consuming";
445 assert(CurrT->getCount() == 0);
448 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
451 std::shared_ptr<PathDiagnosticPiece>
457 static_cast<const RetainCountChecker *
>(BT.getChecker());
465 if (
auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
468 if (
auto PD = annotateStartParameter(N, Sym, SM))
486 const RefVal &CurrV = *CurrT;
492 llvm::raw_string_ostream os(sbuf);
494 if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) {
495 os <<
"Object is now not exclusively owned";
497 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
505 if (isa<ObjCIvarRefExpr>(S) &&
510 if (isa<ObjCArrayLiteral>(S)) {
511 os <<
"NSArray literal is an object with a +0 retain count";
512 }
else if (isa<ObjCDictionaryLiteral>(S)) {
513 os <<
"NSDictionary literal is an object with a +0 retain count";
514 }
else if (
const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
516 os <<
"NSNumber literal is an object with a +0 retain count";
520 BoxClass = Method->getClassInterface();
525 os << *BoxClass <<
" b";
530 os <<
"oxed expression produces an object with a +0 retain count";
532 }
else if (isa<ObjCIvarRefExpr>(S)) {
533 os <<
"Object loaded from instance variable";
535 generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os);
539 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
544 bool DeallocSent =
false;
548 if (Tag == &
Checker->getCastFailTag()) {
549 os <<
"Assuming dynamic cast returns null due to type mismatch";
552 if (Tag == &
Checker->getDeallocSentTag()) {
557 if (
const CallExpr *CE = dyn_cast<CallExpr>(S)) {
562 for (
auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++
i) {
566 if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)
573 if (
const Expr *receiver = ME->getInstanceReceiver()) {
574 if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
575 .getAsLocSymbol() == Sym) {
583 if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent))
586 if (os.str().empty())
592 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
597 if (
const Expr *Exp = dyn_cast_or_null<Expr>(Child))
598 if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {
599 P->addRange(Exp->getSourceRange());
607 if (
const auto *VR = dyn_cast_or_null<VarRegion>(MR))
608 return std::string(VR->getDecl()->getName());
620 struct AllocationInfo {
627 N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
634 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
671 if (NContext == LeakContext || NContext->
isParentOf(LeakContext))
672 AllocationNodeInCurrentOrParentContext = N;
676 if (!InitMethodContext)
678 const Stmt *CE = CEP->getCallExpr();
679 if (
const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
680 const Stmt *RecExpr = ME->getInstanceReceiver();
682 SVal RecV = St->getSVal(RecExpr, NContext);
684 InitMethodContext = CEP->getCalleeContext();
695 if (InitMethodContext) {
700 InterestingMethodContext = InitMethodContext;
705 assert(N &&
"Could not find allocation node");
707 if (AllocationNodeInCurrentOrParentContext &&
710 FirstBinding =
nullptr;
712 return AllocationInfo(AllocationNodeInCurrentOrParentContext,
714 InterestingMethodContext);
717 std::shared_ptr<PathDiagnosticPiece>
721 return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
724 std::shared_ptr<PathDiagnosticPiece>
735 AllocationInfo AllocI = GetAllocationSite(BRC.
getStateManager(), EndN, Sym);
737 const MemRegion* FirstBinding = AllocI.R;
749 llvm::raw_string_ostream os(sbuf);
751 os <<
"Object leaked: ";
754 if (RegionDescription) {
755 os <<
"object allocated and stored into '" << *RegionDescription <<
'\'';
771 os << (isa<ObjCMethodDecl>(D) ?
" is returned from a method " 772 :
" is returned from a function ");
774 if (D->
hasAttr<CFReturnsNotRetainedAttr>()) {
775 os <<
"that is annotated as CF_RETURNS_NOT_RETAINED";
776 }
else if (D->
hasAttr<NSReturnsNotRetainedAttr>()) {
777 os <<
"that is annotated as NS_RETURNS_NOT_RETAINED";
778 }
else if (D->
hasAttr<OSReturnsNotRetainedAttr>()) {
779 os <<
"that is annotated as OS_RETURNS_NOT_RETAINED";
783 os <<
"managed by Automatic Reference Counting";
785 os <<
"whose name ('" << MD->getSelector().getAsString()
786 <<
"') does not start with " 787 "'copy', 'mutableCopy', 'alloc' or 'new'." 788 " This violates the naming convention rules" 789 " given in the Memory Management Guide for Cocoa";
795 os <<
"whose name ('" << *FD
796 <<
"') does not contain 'Copy' or 'Create'. This violates the " 798 " convention rules given in the Memory Management Guide for " 803 os <<
"whose name ('" << FuncName
804 <<
"') starts with '" << StringRef(FuncName).substr(0, 3) <<
"'";
809 os <<
" is not referenced later in this execution path and has a retain " 810 "count of +" << RV->getCount();
813 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
819 :
BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) {
821 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
827 :
BugReport(D, D.getDescription(), endText, n) {
829 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
840 const Decl *PDecl = Region->getDecl();
841 if (PDecl && isa<ParmVarDecl>(PDecl)) {
844 Location = ParamLocation;
845 UniqueingLocation = ParamLocation;
865 AllocationInfo AllocI =
868 AllocNode = AllocI.N;
869 AllocBinding = AllocI.R;
870 markInteresting(AllocI.InterestingMethodContext);
879 AllocBinding =
nullptr;
885 AllocNode->getLocationContext());
886 Location = AllocLocation;
890 UniqueingLocation = AllocLocation;
891 UniqueingDecl = AllocNode->getLocationContext()->getDecl();
895 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
897 llvm::raw_string_ostream os(Description);
898 os <<
"Potential leak of an object";
901 if (RegionDescription) {
902 os <<
" stored into '" << *RegionDescription <<
'\'';
913 : RefCountReport(D, LOpts, n, sym,
true) {
915 deriveAllocLocation(Ctx, sym);
917 deriveParamLocation(Ctx, sym);
919 createDescription(Ctx);
921 addVisitor(llvm::make_unique<RefLeakReportVisitor>(sym));
Indicates that the tracked object is a generalized object.
Indicates that the tracked object is a CF object.
Represents a function declaration or definition.
ObjKind
Determines the object kind of a tracked object.
A (possibly-)qualified type.
MemRegion - The root abstract class for all memory regions.
ASTContext & getASTContext()
Stmt - This represents one statement.
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee...
Decl - This represents one declaration (or definition), e.g.
Represents a point when we begin processing an inlined call.
Manages the lifetime of CallEvent objects.
const ProgramStateRef & getState() const
const T * getAs() const
Member-template getAs<specific type>'.
const Decl & getCodeDecl() const
ObjCMethodDecl - Represents an instance or class method declaration.
virtual const MemRegion * getOriginRegion() const
Find the region from which this symbol originates.
Represents a parameter to a function.
unsigned getCount() const
bool isParentOf(const LocationContext *LC) const
const MemRegion * getRegion()
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Optional< T > getLocationAs() const LLVM_LVALUE_FUNCTION
Represents a point when we start the call exit sequence (for inlined call).
const BugType & getBugType() const
const char *const MemoryRefCount
ProgramStateManager & getStateManager()
const LocationContext * getLocationContext() const
const clang::PrintingPolicy & getPrintingPolicy() const
const LocationContext * getParent() const
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
ExplodedNode * getFirstPred()
Represents an ObjC class declaration.
const MemSpaceRegion * getMemorySpace() const
static std::string getPrettyTypeName(QualType QT)
If type represents a pointer to CXXRecordDecl, and is not a typedef, return the decl name...
virtual QualType getType() const =0
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
const Stmt * getCallSite() const
bool isSynthesizedAccessor(const StackFrameContext *SFC)
Returns true if this stack frame is for an Objective-C method that is a property getter or setter who...
Represents a single basic block in a source-level CFG.
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
This represents one expression.
CallEventRef getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State)
Gets an outside caller given a callee context.
DeclContext * getDeclContext()
bool inTopFrame() const override
Return true if the current LocationContext has no caller context.
An expression that sends a message to the given Objective-C object or class.
RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, bool isLeak=false)
void markInteresting(SymbolRef sym)
CallEventRef< ObjCMethodCall > getObjCMethodCall(const ObjCMessageExpr *E, ProgramStateRef State, const LocationContext *LCtx)
bool isNull() const
Return true if this QualType doesn't point to a type yet.
RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, CheckerContext &Ctx)
static const Stmt * getStmt(const ExplodedNode *N)
Given an exploded node, retrieve the statement that should be used for the diagnostic location...
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
T castAs() const
Convert to the specified ProgramPoint type, asserting that this ProgramPoint is of the desired type...
ProgramPoints can be "tagged" as representing points specific to a given analysis entity...
Indicates that the tracked object is an Objective-C object.
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
CallEventManager & getCallEventManager()
ASTContext & getASTContext() const LLVM_READONLY
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
ObjCBoxedExpr - used for generalized expression boxing.
CallEventRef getCall(const Stmt *S, ProgramStateRef State, const LocationContext *LC)
Gets a call event for a function call, Objective-C method call, or a 'new' call.
Dataflow Directional Tag Classes.
virtual void getNameForDiagnostic(raw_ostream &OS, const PrintingPolicy &Policy, bool Qualified) const
Appends a human-readable name for this declaration into the given stream.
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
const ProgramPointTag * getTag() const
const Decl * getDecl() const
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Represents a pointer to an Objective C object.
const FunctionDecl * getAsFunctionDecl() const
getAsFunctionDecl - If this SVal is a MemRegionVal and wraps a CodeTextRegion wrapping a FunctionDecl...
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
ProgramStateManager & getStateManager()
const RefVal * getRefBinding(ProgramStateRef State, SymbolRef Sym)
const StackFrameContext * getStackFrame() const
StringRef getDescription() const
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
SourceManager & getSourceManager()
static PathDiagnosticLocation createEndOfPath(const ExplodedNode *N, const SourceManager &SM)
Create a location corresponding to the next valid ExplodedNode as end of path location.
RefCountBug(const CheckerBase *checker, RefCountBugType BT)
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
std::string getQualifiedNameAsString() const
void iterBindings(ProgramStateRef state, StoreManager::BindingsHandler &F)
const StackFrameContext * getStackFrame() const
Optional< T > getAs() const
Convert to the specified ProgramPoint type, returning None if this ProgramPoint is not of the desired...
This class provides an interface through which checkers can create individual bug reports...
const LocationContext * getLocationContext() const
const LangOptions & getLangOpts() const
This class handles loading and caching of source files into memory.
SourceManager & getSourceManager()
static bool isNumericLiteralExpression(const Expr *E)
QualType getPointeeType() const
Gets the type pointed to by this ObjC pointer.