Go to the documentation of this file.
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/SmallVector.h"
19 using namespace clang;
21 using namespace retaincountchecker;
26 return "Use-after-release";
30 return "-dealloc sent to non-exclusively owned object";
32 return "freeing non-exclusively owned object";
34 return "Object autoreleased too many times";
36 return "Method should return an owned object";
40 return "Leak of returned object";
42 llvm_unreachable(
"Unknown RefCountBugKind");
48 return "Reference-counted object is used after it is released";
50 return "Incorrect decrement of the reference count of an object that is "
51 "not owned at this point by the caller";
53 return "-dealloc sent to object that may be referenced elsewhere";
55 return "'free' called on an object that may be referenced elsewhere";
57 return "Object autoreleased too many times";
59 return "Object with a +0 retain count returned to caller where a +1 "
60 "(owning) retain count is expected";
65 llvm_unreachable(
"Unknown RefCountBugKind");
70 BT == LeakWithinFunction ||
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()).getAsSymbol() == 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();
204 if (
const auto *CE = dyn_cast<CallExpr>(S))
214 llvm::raw_string_ostream &os) {
216 if (
const CallExpr *CE = dyn_cast<CallExpr>(S)) {
219 SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
224 FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
226 if (
const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) {
227 os <<
"Call to method '" << MD->getQualifiedNameAsString() <<
'\'';
231 os <<
"function call";
233 }
else if (isa<CXXNewExpr>(S)) {
234 os <<
"Operator 'new'";
236 assert(isa<ObjCMessageExpr>(S));
240 switch (Call->getMessageKind()) {
264 os <<
"a Core Foundation object of type '" << Sym->
getType() <<
"' with a ";
269 os <<
"an object of type '" << Sym->
getType() <<
"' with a ";
273 if (!isa<ObjCObjectPointerType>(T)) {
274 os <<
"an Objective-C object with a ";
281 if (CurrV.isOwned()) {
282 os <<
"+1 retain count";
284 assert(CurrV.isNotOwned());
285 os <<
"+0 retain count";
289 os <<
" into an out parameter '";
290 const ParmVarDecl *PVD = (*CE)->parameters()[*Idx];
295 QualType RT = (*CE)->getResultType();
297 SVal RV = (*CE)->getReturnValue();
298 if (CurrSt->isNull(RV).isConstrainedTrue()) {
299 os <<
" (assuming the call returns zero)";
300 }
else if (CurrSt->isNonNull(RV).isConstrainedTrue()) {
301 os <<
" (assuming the call returns non-zero)";
310 namespace retaincountchecker {
319 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
372 static std::shared_ptr<PathDiagnosticEventPiece>
385 llvm::raw_string_ostream os(sbuf);
387 for (
unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {
390 if (!PVD->
hasAttr<OSConsumedAttr>())
393 if (
SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
397 if (!CountBeforeCall || !CountAtExit)
400 unsigned CountBefore = CountBeforeCall->getCount();
401 unsigned CountAfter = CountAtExit->getCount();
403 bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
408 os <<
"' is marked as consuming, but the function did not consume "
409 <<
"the reference\n";
414 if (os.str().empty())
418 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
422 static std::shared_ptr<PathDiagnosticEventPiece>
436 const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion());
437 const auto *PVD = cast<ParmVarDecl>(VR->getDecl());
441 llvm::raw_string_ostream os(
s);
442 os <<
"Parameter '" << PVD->getDeclName() <<
"' starts at +";
443 if (CurrT->getCount() == 1) {
444 os <<
"1, as it is marked as consuming";
446 assert(CurrT->getCount() == 0);
449 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
485 const RefVal &CurrV = *CurrT;
491 llvm::raw_string_ostream os(sbuf);
494 os <<
"Object is now not exclusively owned";
496 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
504 if (isa<ObjCIvarRefExpr>(S) &&
509 if (isa<ObjCArrayLiteral>(S)) {
510 os <<
"NSArray literal is an object with a +0 retain count";
511 }
else if (isa<ObjCDictionaryLiteral>(S)) {
512 os <<
"NSDictionary literal is an object with a +0 retain count";
513 }
else if (
const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
515 os <<
"NSNumber literal is an object with a +0 retain count";
519 BoxClass = Method->getClassInterface();
524 os << *BoxClass <<
" b";
529 os <<
"oxed expression produces an object with a +0 retain count";
531 }
else if (isa<ObjCIvarRefExpr>(S)) {
532 os <<
"Object loaded from instance variable";
538 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
543 bool DeallocSent =
false;
548 os <<
"Assuming dynamic cast returns null due to type mismatch";
556 if (
const CallExpr *CE = dyn_cast<CallExpr>(S)) {
561 for (
auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) {
565 if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() !=
Sym)
572 if (
const Expr *receiver = ME->getInstanceReceiver()) {
573 if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
574 .getAsLocSymbol() ==
Sym) {
585 if (os.str().empty())
591 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
595 for (
const Stmt *Child : S->children())
596 if (
const Expr *Exp = dyn_cast_or_null<Expr>(Child))
597 if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() ==
Sym) {
598 P->addRange(Exp->getSourceRange());
606 if (
const auto *VR = dyn_cast_or_null<VarRegion>(MR))
621 : Sym(Sym), Result(ToFill) {}
626 if (!SymV || SymV != Sym)
629 if (isa<NonParamVarRegion>(R))
630 Result.emplace_back(R, Val);
640 while (Result.empty() &&
Node) {
654 struct AllocationInfo {
661 N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
668 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
705 if (NContext == LeakContext || NContext->
isParentOf(LeakContext))
706 AllocationNodeInCurrentOrParentContext = N;
710 if (!InitMethodContext)
712 const Stmt *CE = CEP->getCallExpr();
713 if (
const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
714 const Stmt *RecExpr = ME->getInstanceReceiver();
716 SVal RecV = St->getSVal(RecExpr, NContext);
718 InitMethodContext = CEP->getCalleeContext();
729 if (InitMethodContext) {
734 InterestingMethodContext = InitMethodContext;
739 assert(N &&
"Could not find allocation node");
741 if (AllocationNodeInCurrentOrParentContext &&
744 FirstBinding =
nullptr;
746 return AllocationInfo(AllocationNodeInCurrentOrParentContext, FirstBinding,
747 InterestingMethodContext);
770 llvm::raw_string_ostream os(sbuf);
772 os <<
"Object leaked: ";
775 if (RegionDescription) {
776 os <<
"object allocated and stored into '" << *RegionDescription <<
'\'';
792 os << (isa<ObjCMethodDecl>(D) ?
" is returned from a method "
793 :
" is returned from a function ");
795 if (D->
hasAttr<CFReturnsNotRetainedAttr>()) {
796 os <<
"that is annotated as CF_RETURNS_NOT_RETAINED";
797 }
else if (D->
hasAttr<NSReturnsNotRetainedAttr>()) {
798 os <<
"that is annotated as NS_RETURNS_NOT_RETAINED";
799 }
else if (D->
hasAttr<OSReturnsNotRetainedAttr>()) {
800 os <<
"that is annotated as OS_RETURNS_NOT_RETAINED";
804 os <<
"managed by Automatic Reference Counting";
806 os <<
"whose name ('" << MD->getSelector().getAsString()
807 <<
"') does not start with "
808 "'copy', 'mutableCopy', 'alloc' or 'new'."
809 " This violates the naming convention rules"
810 " given in the Memory Management Guide for Cocoa";
816 os <<
"whose name ('" << *FD
817 <<
"') does not contain 'Copy' or 'Create'. This violates the "
819 " convention rules given in the Memory Management Guide for "
824 os <<
"whose name ('" << FuncName <<
"') starts with '"
825 << StringRef(FuncName).substr(0, 3) <<
"'";
830 os <<
" is not referenced later in this execution path and has a retain "
835 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
843 addVisitor<RefCountReportVisitor>(sym);
851 addVisitor<RefCountReportVisitor>(sym);
862 const Decl *PDecl = Region->getDecl();
863 if (isa_and_nonnull<ParmVarDecl>(PDecl)) {
866 Location = ParamLocation;
886 AllocationInfo AllocI =
889 AllocNode = AllocI.N;
890 AllocFirstBinding = AllocI.R;
900 AllocFirstBinding =
nullptr;
906 Location = AllocLocation;
918 os <<
"Potential leak of an object";
922 if (RegionDescription) {
923 os <<
" stored into '" << *RegionDescription <<
'\'';
933 if (!AllocFirstBinding)
939 if (
Node->getState()->getSVal(AllocFirstBinding).getAsSymbol() ==
Sym) {
941 AllocBindingToReport = AllocFirstBinding;
964 if (!AllVarBindings.empty() &&
965 llvm::count_if(AllVarBindings,
966 [
this](
const std::pair<const MemRegion *, SVal> Binding) {
967 return Binding.first == AllocFirstBinding;
970 AllocBindingToReport = AllVarBindings[0].first;
982 AllocBindingToReport, *
this);
984 AllocBindingToReport = AllocFirstBinding;
993 deriveAllocLocation(Ctx);
994 findBindingToReport(Ctx, N);
996 if (!AllocFirstBinding)
997 deriveParamLocation(Ctx);
999 createDescription(Ctx);
1001 addVisitor<RefLeakReportVisitor>(
Sym, AllocBindingToReport);
Represents a point when we start the call exit sequence (for inlined call).
Represents an ObjC class declaration.
bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *R, SVal Val) override
void Profile(llvm::FoldingSetNodeID &ID) const override
static std::shared_ptr< PathDiagnosticEventPiece > annotateConsumedSummaryMismatch(const ExplodedNode *N, CallExitBegin &CallExitLoc, const SourceManager &SM, CallEventManager &CEMgr)
Insert a diagnostic piece at function exit if a function parameter is annotated as "os_consumed",...
ASTContext & getASTContext() const LLVM_READONLY
static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, const LocationContext *LCtx, const RefVal &CurrV, SymbolRef &Sym, const Stmt *S, llvm::raw_string_ostream &os)
PathDiagnosticLocation UniqueingLocation
Reports with different uniqueing locations are considered to be different for the purposes of dedupli...
static const ExplodedNode * getCalleeNode(const ExplodedNode *Pred)
Find the first node with the parent stack frame.
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, a 'new', or a 'delete' call.
A boolean literal, per ([C++ lex.bool] Boolean literals).
@ CF
Indicates that the tracked object is a CF object.
const LocationContext * getLocationContext() const
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
const StackFrameContext * getStackFrame() const
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
static const CheckerProgramPointTag & getDeallocSentTag()
static Optional< std::string > describeRegion(const MemRegion *MR)
ObjKind
Determines the object kind of a tracked object.
ProgramPoints can be "tagged" as representing points specific to a given analysis entity.
const LocationContext * getLocationContext() const
static std::string findAllocatedObjectName(const Stmt *S, QualType QT)
A (possibly-)qualified type.
CallEventRef< ObjCMethodCall > getObjCMethodCall(const ObjCMessageExpr *E, ProgramStateRef State, const LocationContext *LCtx)
const StackFrameContext * getStackFrame() const
Represents a parameter to a function.
QualType getPointeeType() const
Gets the type pointed to by this ObjC pointer.
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
static bool isNumericLiteralExpression(const Expr *E)
It represents a stack frame of the call stack (based on CallEvent).
RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, CheckerContext &Ctx)
BugReporterVisitors are used to add custom diagnostics along a path.
virtual void getNameForDiagnostic(raw_ostream &OS, const PrintingPolicy &Policy, bool Qualified) const
Appends a human-readable name for this declaration into the given stream.
const void * Store
Store - This opaque type encapsulates an immutable mapping from locations to values.
This class handles loading and caching of source files into memory.
const RefVal * getRefBinding(ProgramStateRef State, SymbolRef Sym)
PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, const ExplodedNode *N, PathSensitiveBugReport &BR) override
Provide custom definition for the final diagnostic piece on the path - the piece, which is displayed ...
ObjCBoolLiteralExpr - Objective-C Boolean Literal.
MemRegion - The root abstract class for all memory regions.
Represents a single basic block in a source-level CFG.
bool inTopFrame() const override
SymbolRef getAsLocSymbol(bool IncludeBaseRegions=false) const
If this SVal is a location and wraps a symbol, return that SymbolRef.
const ProgramStateRef & getState() const
RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, bool isLeak=false)
static Optional< unsigned > findArgIdxOfSymbol(ProgramStateRef CurrSt, const LocationContext *LCtx, SymbolRef &Sym, Optional< CallEventRef<>> CE)
Finds argument index of the out paramter in the call S corresponding to the symbol Sym.
virtual QualType getType() const =0
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
virtual const MemRegion * getOriginRegion() const
Find the region from which this symbol originates.
unsigned getCount() const
const SourceManager & getSourceManager() const
Optional< T > getAs() const
Convert to the specified ProgramPoint type, returning None if this ProgramPoint is not of the desired...
const ProgramPointTag * getTag() const
const T * getAs() const
Member-template getAs<specific type>'.
const ExplodedNode * getErrorNode() const
bool isParentOf(const LocationContext *LC) const
bool isa(CodeGen::Address addr)
T castAs() const
Convert to the specified ProgramPoint type, asserting that this ProgramPoint is of the desired type.
static Optional< std::string > findMetaClassAlloc(const Expr *Callee)
This wrapper is used to ensure that only StringRefs originating from the CheckerRegistry are used as ...
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
const char *const MemoryRefCount
static bool shouldGenerateNote(llvm::raw_string_ostream &os, const RefVal *PrevT, const RefVal &CurrV, bool DeallocSent)
Write information about the type state change to os, return whether the note should be generated.
ProgramStateManager & getStateManager()
const SourceManager & getSourceManager()
CallEventRef getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State)
Gets an outside caller given a callee context.
Represents an SVal that is guaranteed to not be UnknownVal.
RefCountBugKind getBugType() const
IRgen optimization opportunities The common pattern of short x
PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &BR) override
Return a diagnostic piece which should be associated with the given node.
An expression that sends a message to the given Objective-C object or class.
Bindings getAllVarBindingsForSymbol(ProgramStateManager &Manager, const ExplodedNode *Node, SymbolRef Sym)
RefLeakReportVisitor(SymbolRef Sym, const MemRegion *LastBinding)
const MemRegion * getRegion()
const LocationContext * getParent() const
It might return null.
Represents a pointer to an Objective C object.
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...
@ ReleasedAfterDirectAccess
const LLVM_ATTRIBUTE_RETURNS_NONNULL MemSpaceRegion * getMemorySpace() const
Manages the lifetime of CallEvent objects.
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
CallEventManager & getCallEventManager()
static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, const ExplodedNode *N, SymbolRef Sym)
const clang::PrintingPolicy & getPrintingPolicy() const
ExplodedNode * getFirstPred()
bool isNull() const
Return true if this QualType doesn't point to a type yet.
@ Generalized
Indicates that the tracked object is a generalized object.
Decl - This represents one declaration (or definition), e.g.
const Stmt * getCallSite() const
VarBindingsCollector(SymbolRef Sym, Bindings &ToFill)
@ ObjC
Indicates that the tracked object is an Objective-C object.
void trackStoredValue(KnownSVal V, const MemRegion *R, PathSensitiveBugReport &Report, TrackingOptions Opts={}, const StackFrameContext *Origin=nullptr)
Track how the value got stored into the given region and where it came from.
std::string getQualifiedNameAsString() const
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...
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, const ExplodedNode *N, PathSensitiveBugReport &BR) override
Provide custom definition for the final diagnostic piece on the path - the piece, which is displayed ...
ObjCMethodDecl - Represents an instance or class method declaration.
static std::string getPrettyTypeName(QualType QT)
If type represents a pointer to CXXRecordDecl, and is not a typedef, return the decl name.
bool This(InterpState &S, CodePtr OpPC)
static PathDiagnosticPieceRef getDefaultEndPath(const BugReporterContext &BRC, const ExplodedNode *N, const PathSensitiveBugReport &BR)
Generates the default final diagnostic piece.
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
const Decl * UniqueingDecl
Optional< T > getLocationAs() const &
Stmt - This represents one statement.
ObjKind getObjKind() const
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
__device__ __2f16 float bool s
void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind=bugreporter::TrackingKind::Thorough)
Marks a symbol as interesting.
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
Represents a point when we begin processing an inlined call.
RefCountBug(CheckerNameRef Checker, RefCountBugKind BT)
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
const Decl & getCodeDecl() const
This represents one expression.
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
RefCountReportVisitor(SymbolRef sym)
ASTContext & getASTContext() const
static std::shared_ptr< PathDiagnosticEventPiece > annotateStartParameter(const ExplodedNode *N, SymbolRef Sym, const SourceManager &SM)
Annotate the parameter at the analysis entry point.
ProgramStateManager & getStateManager() const
const BugType & getBugType() const
const Decl * getDecl() const
Represents a function declaration or definition.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
static const CheckerProgramPointTag & getCastFailTag()
const LangOptions & getLangOpts() const
void iterBindings(ProgramStateRef state, StoreManager::BindingsHandler &F)
StringRef getDescription() const
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
DeclContext * getDeclContext()