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")
189 const Expr *
This = ME->getBase()->IgnoreParenImpCasts();
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 {
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());
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, FirstBinding,
713 InterestingMethodContext);
721 return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
736 AllocationInfo AllocI = GetAllocationSite(BRC.
getStateManager(), EndN, Sym);
738 const MemRegion* FirstBinding = AllocI.R;
744 llvm::raw_string_ostream os(sbuf);
746 os <<
"Object leaked: ";
749 if (RegionDescription) {
750 os <<
"object allocated and stored into '" << *RegionDescription <<
'\'';
766 os << (isa<ObjCMethodDecl>(D) ?
" is returned from a method " 767 :
" is returned from a function ");
769 if (D->
hasAttr<CFReturnsNotRetainedAttr>()) {
770 os <<
"that is annotated as CF_RETURNS_NOT_RETAINED";
771 }
else if (D->
hasAttr<NSReturnsNotRetainedAttr>()) {
772 os <<
"that is annotated as NS_RETURNS_NOT_RETAINED";
773 }
else if (D->
hasAttr<OSReturnsNotRetainedAttr>()) {
774 os <<
"that is annotated as OS_RETURNS_NOT_RETAINED";
778 os <<
"managed by Automatic Reference Counting";
780 os <<
"whose name ('" << MD->getSelector().getAsString()
781 <<
"') does not start with " 782 "'copy', 'mutableCopy', 'alloc' or 'new'." 783 " This violates the naming convention rules" 784 " given in the Memory Management Guide for Cocoa";
790 os <<
"whose name ('" << *FD
791 <<
"') does not contain 'Copy' or 'Create'. This violates the " 793 " convention rules given in the Memory Management Guide for " 798 os <<
"whose name ('" << FuncName
799 <<
"') starts with '" << StringRef(FuncName).substr(0, 3) <<
"'";
804 os <<
" is not referenced later in this execution path and has a retain " 805 "count of +" << RV->getCount();
808 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
816 addVisitor(std::make_unique<RefCountReportVisitor>(sym));
824 addVisitor(std::make_unique<RefCountReportVisitor>(sym));
835 const Decl *PDecl = Region->getDecl();
836 if (PDecl && isa<ParmVarDecl>(PDecl)) {
839 Location = ParamLocation;
840 UniqueingLocation = ParamLocation;
860 AllocationInfo AllocI =
863 AllocNode = AllocI.N;
864 AllocBinding = AllocI.R;
865 markInteresting(AllocI.InterestingMethodContext);
871 AllocStmt = AllocNode->getStmtForDiagnostics();
874 AllocBinding =
nullptr;
880 AllocNode->getLocationContext());
881 Location = AllocLocation;
885 UniqueingLocation = AllocLocation;
886 UniqueingDecl = AllocNode->getLocationContext()->getDecl();
890 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
892 llvm::raw_string_ostream os(Description);
893 os <<
"Potential leak of an object";
896 if (RegionDescription) {
897 os <<
" stored into '" << *RegionDescription <<
'\'';
908 : RefCountReport(D, LOpts, n, sym,
true) {
910 deriveAllocLocation(Ctx, sym);
912 deriveParamLocation(Ctx, sym);
914 createDescription(Ctx);
916 addVisitor(std::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.
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.
ASTContext & getASTContext() const
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>'.
bool This(InterpState &S, CodePtr OpPC)
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
void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind=bugreporter::TrackingKind::Thorough)
Marks a symbol as interesting.
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).
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
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.
const SourceManager & getSourceManager()
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)
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)
const SourceManager & getSourceManager() const
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.
ProgramStateManager & getStateManager() const
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
const ProgramPointTag * getTag() const
const Decl * getDecl() const
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.
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 ...
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...
const LocationContext * getLocationContext() const
const LangOptions & getLangOpts() const
This class handles loading and caching of source files into memory.
static bool isNumericLiteralExpression(const Expr *E)
QualType getPointeeType() const
Gets the type pointed to by this ObjC pointer.