aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp270
1 files changed, 169 insertions, 101 deletions
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index 856463a981..0f456ea8d7 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -37,6 +37,8 @@
#include "llvm/ADT/StringExtras.h"
#include <cstdarg>
+#include "AllocationDiagnostics.h"
+
using namespace clang;
using namespace ento;
using llvm::StrInStrNoCase;
@@ -324,7 +326,7 @@ void RefVal::print(raw_ostream &Out) const {
break;
case RefVal::ErrorOverAutorelease:
- Out << "Over autoreleased";
+ Out << "Over-autoreleased";
break;
case RefVal::ErrorReturnedNotOwned:
@@ -782,6 +784,10 @@ public:
const RetainSummary *getStandardMethodSummary(const ObjCMethodDecl *MD,
Selector S, QualType RetTy);
+ /// Determine if there is a special return effect for this function or method.
+ Optional<RetEffect> getRetEffectFromAnnotations(QualType RetTy,
+ const Decl *D);
+
void updateSummaryFromAnnotations(const RetainSummary *&Summ,
const ObjCMethodDecl *MD);
@@ -1110,12 +1116,14 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) {
// correctly.
ScratchArgs = AF.add(ScratchArgs, 12, StopTracking);
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
- } else if (FName == "dispatch_set_context") {
+ } else if (FName == "dispatch_set_context" ||
+ FName == "xpc_connection_set_context") {
// <rdar://problem/11059275> - The analyzer currently doesn't have
// a good way to reason about the finalizer function for libdispatch.
// If we pass a context object that is memory managed, stop tracking it.
+ // <rdar://problem/13783514> - Same problem, but for XPC.
// FIXME: this hack should possibly go away once we can handle
- // libdispatch finalizers.
+ // libdispatch and XPC finalizers.
ScratchArgs = AF.add(ScratchArgs, 1, StopTracking);
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
} else if (FName.startswith("NSLog")) {
@@ -1271,6 +1279,30 @@ RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) {
// Summary creation for Selectors.
//===----------------------------------------------------------------------===//
+Optional<RetEffect>
+RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy,
+ const Decl *D) {
+ if (cocoa::isCocoaObjectRef(RetTy)) {
+ if (D->getAttr<NSReturnsRetainedAttr>())
+ return ObjCAllocRetE;
+
+ if (D->getAttr<NSReturnsNotRetainedAttr>() ||
+ D->getAttr<NSReturnsAutoreleasedAttr>())
+ return RetEffect::MakeNotOwned(RetEffect::ObjC);
+
+ } else if (!RetTy->isPointerType()) {
+ return None;
+ }
+
+ if (D->getAttr<CFReturnsRetainedAttr>())
+ return RetEffect::MakeOwned(RetEffect::CF, true);
+
+ if (D->getAttr<CFReturnsNotRetainedAttr>())
+ return RetEffect::MakeNotOwned(RetEffect::CF);
+
+ return None;
+}
+
void
RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
const FunctionDecl *FD) {
@@ -1285,40 +1317,15 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
for (FunctionDecl::param_const_iterator pi = FD->param_begin(),
pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) {
const ParmVarDecl *pd = *pi;
- if (pd->getAttr<NSConsumedAttr>()) {
- if (!GCEnabled) {
- Template->addArg(AF, parm_idx, DecRef);
- }
- } else if (pd->getAttr<CFConsumedAttr>()) {
+ if (pd->getAttr<NSConsumedAttr>())
+ Template->addArg(AF, parm_idx, DecRefMsg);
+ else if (pd->getAttr<CFConsumedAttr>())
Template->addArg(AF, parm_idx, DecRef);
- }
}
QualType RetTy = FD->getResultType();
-
- // Determine if there is a special return effect for this method.
- if (cocoa::isCocoaObjectRef(RetTy)) {
- if (FD->getAttr<NSReturnsRetainedAttr>()) {
- Template->setRetEffect(ObjCAllocRetE);
- }
- else if (FD->getAttr<CFReturnsRetainedAttr>()) {
- Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
- }
- else if (FD->getAttr<NSReturnsNotRetainedAttr>() ||
- FD->getAttr<NSReturnsAutoreleasedAttr>()) {
- Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC));
- }
- else if (FD->getAttr<CFReturnsNotRetainedAttr>())
- Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
- }
- else if (RetTy->getAs<PointerType>()) {
- if (FD->getAttr<CFReturnsRetainedAttr>()) {
- Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
- }
- else if (FD->getAttr<CFReturnsNotRetainedAttr>()) {
- Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
- }
- }
+ if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, FD))
+ Template->setRetEffect(*RetE);
}
void
@@ -1329,13 +1336,10 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
assert(Summ && "Must have a valid summary to add annotations to");
RetainSummaryTemplate Template(Summ, *this);
- bool isTrackedLoc = false;
// Effects on the receiver.
- if (MD->getAttr<NSConsumesSelfAttr>()) {
- if (!GCEnabled)
- Template->setReceiverEffect(DecRefMsg);
- }
+ if (MD->getAttr<NSConsumesSelfAttr>())
+ Template->setReceiverEffect(DecRefMsg);
// Effects on the parameters.
unsigned parm_idx = 0;
@@ -1343,38 +1347,16 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
pi=MD->param_begin(), pe=MD->param_end();
pi != pe; ++pi, ++parm_idx) {
const ParmVarDecl *pd = *pi;
- if (pd->getAttr<NSConsumedAttr>()) {
- if (!GCEnabled)
- Template->addArg(AF, parm_idx, DecRef);
- }
- else if(pd->getAttr<CFConsumedAttr>()) {
+ if (pd->getAttr<NSConsumedAttr>())
+ Template->addArg(AF, parm_idx, DecRefMsg);
+ else if (pd->getAttr<CFConsumedAttr>()) {
Template->addArg(AF, parm_idx, DecRef);
}
}
- // Determine if there is a special return effect for this method.
- if (cocoa::isCocoaObjectRef(MD->getResultType())) {
- if (MD->getAttr<NSReturnsRetainedAttr>()) {
- Template->setRetEffect(ObjCAllocRetE);
- return;
- }
- if (MD->getAttr<NSReturnsNotRetainedAttr>() ||
- MD->getAttr<NSReturnsAutoreleasedAttr>()) {
- Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC));
- return;
- }
-
- isTrackedLoc = true;
- } else {
- isTrackedLoc = MD->getResultType()->getAs<PointerType>() != NULL;
- }
-
- if (isTrackedLoc) {
- if (MD->getAttr<CFReturnsRetainedAttr>())
- Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
- else if (MD->getAttr<CFReturnsNotRetainedAttr>())
- Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
- }
+ QualType RetTy = MD->getResultType();
+ if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, MD))
+ Template->setRetEffect(*RetE);
}
const RetainSummary *
@@ -1682,10 +1664,10 @@ namespace {
class OverAutorelease : public CFRefBug {
public:
OverAutorelease()
- : CFRefBug("Object sent -autorelease too many times") {}
+ : CFRefBug("Object autoreleased too many times") {}
const char *getDescription() const {
- return "Object sent -autorelease too many times";
+ return "Object autoreleased too many times";
}
};
@@ -1795,11 +1777,11 @@ namespace {
class CFRefLeakReport : public CFRefReport {
const MemRegion* AllocBinding;
-
public:
CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled,
const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
- CheckerContext &Ctx);
+ CheckerContext &Ctx,
+ bool IncludeAllocationLine);
PathDiagnosticLocation getLocation(const SourceManager &SM) const {
assert(Location.isValid());
@@ -2070,7 +2052,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
return 0;
assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
- os << "Object sent -autorelease message";
+ os << "Object autoreleased";
break;
}
@@ -2156,32 +2138,86 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
// Find the first node in the current function context that referred to the
// tracked symbol and the memory location that value was stored to. Note, the
// value is only reported if the allocation occurred in the same function as
-// the leak.
-static std::pair<const ExplodedNode*,const MemRegion*>
+// the leak. The function can also return a location context, which should be
+// treated as interesting.
+struct AllocationInfo {
+ const ExplodedNode* N;
+ const MemRegion *R;
+ const LocationContext *InterestingMethodContext;
+ AllocationInfo(const ExplodedNode *InN,
+ const MemRegion *InR,
+ const LocationContext *InInterestingMethodContext) :
+ N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
+};
+
+static AllocationInfo
GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
SymbolRef Sym) {
- const ExplodedNode *Last = N;
+ const ExplodedNode *AllocationNode = N;
+ const ExplodedNode *AllocationNodeInCurrentContext = N;
const MemRegion* FirstBinding = 0;
const LocationContext *LeakContext = N->getLocationContext();
+ // The location context of the init method called on the leaked object, if
+ // available.
+ const LocationContext *InitMethodContext = 0;
+
while (N) {
ProgramStateRef St = N->getState();
+ const LocationContext *NContext = N->getLocationContext();
if (!getRefBinding(St, Sym))
break;
StoreManager::FindUniqueBinding FB(Sym);
StateMgr.iterBindings(St, FB);
- if (FB) FirstBinding = FB.getRegion();
-
- // Allocation node, is the last node in the current context in which the
- // symbol was tracked.
- if (N->getLocationContext() == LeakContext)
- Last = N;
+
+ if (FB) {
+ const MemRegion *R = FB.getRegion();
+ const VarRegion *VR = R->getBaseRegion()->getAs<VarRegion>();
+ // Do not show local variables belonging to a function other than
+ // where the error is reported.
+ if (!VR || VR->getStackFrame() == LeakContext->getCurrentStackFrame())
+ FirstBinding = R;
+ }
+
+ // AllocationNode is the last node in which the symbol was tracked.
+ AllocationNode = N;
+
+ // AllocationNodeInCurrentContext, is the last node in the current context
+ // in which the symbol was tracked.
+ if (NContext == LeakContext)
+ AllocationNodeInCurrentContext = N;
+
+ // Find the last init that was called on the given symbol and store the
+ // init method's location context.
+ if (!InitMethodContext)
+ if (Optional<CallEnter> CEP = N->getLocation().getAs<CallEnter>()) {
+ const Stmt *CE = CEP->getCallExpr();
+ if (const ObjCMessageExpr *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
+ const Stmt *RecExpr = ME->getInstanceReceiver();
+ if (RecExpr) {
+ SVal RecV = St->getSVal(RecExpr, NContext);
+ if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
+ InitMethodContext = CEP->getCalleeContext();
+ }
+ }
+ }
N = N->pred_empty() ? NULL : *(N->pred_begin());
}
+ // If we are reporting a leak of the object that was allocated with alloc,
+ // mark its init method as interesting.
+ const LocationContext *InterestingMethodContext = 0;
+ if (InitMethodContext) {
+ const ProgramPoint AllocPP = AllocationNode->getLocation();
+ if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
+ if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
+ if (ME->getMethodFamily() == OMF_alloc)
+ InterestingMethodContext = InitMethodContext;
+ }
+
// If allocation happened in a function different from the leak node context,
// do not report the binding.
assert(N && "Could not find allocation node");
@@ -2189,7 +2225,9 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
FirstBinding = 0;
}
- return std::make_pair(Last, FirstBinding);
+ return AllocationInfo(AllocationNodeInCurrentContext,
+ FirstBinding,
+ InterestingMethodContext);
}
PathDiagnosticPiece*
@@ -2212,12 +2250,12 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
// We are reporting a leak. Walk up the graph to get to the first node where
// the symbol appeared, and also get the first VarDecl that tracked object
// is stored to.
- const ExplodedNode *AllocNode = 0;
- const MemRegion* FirstBinding = 0;
-
- llvm::tie(AllocNode, FirstBinding) =
+ AllocationInfo AllocI =
GetAllocationSite(BRC.getStateManager(), EndN, Sym);
+ const MemRegion* FirstBinding = AllocI.R;
+ BR.markInteresting(AllocI.InterestingMethodContext);
+
SourceManager& SM = BRC.getSourceManager();
// Compute an actual location for the leak. Sometimes a leak doesn't
@@ -2289,8 +2327,9 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
bool GCEnabled, const SummaryLogTy &Log,
ExplodedNode *n, SymbolRef sym,
- CheckerContext &Ctx)
-: CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) {
+ CheckerContext &Ctx,
+ bool IncludeAllocationLine)
+ : CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) {
// Most bug reports are cached at the location where they occurred.
// With leaks, we want to unique them by the location where they were
@@ -2304,9 +2343,13 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
const SourceManager& SMgr = Ctx.getSourceManager();
- llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding.
+ AllocationInfo AllocI =
GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
+ AllocNode = AllocI.N;
+ AllocBinding = AllocI.R;
+ markInteresting(AllocI.InterestingMethodContext);
+
// Get the SourceLocation for the allocation site.
// FIXME: This will crash the analyzer if an allocation comes from an
// implicit call. (Currently there are no such allocations in Cocoa, though.)
@@ -2317,8 +2360,17 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
else
AllocStmt = P.castAs<PostStmt>().getStmt();
assert(AllocStmt && "All allocations must come from explicit calls");
- Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
- n->getLocationContext());
+
+ PathDiagnosticLocation AllocLocation =
+ PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
+ AllocNode->getLocationContext());
+ Location = AllocLocation;
+
+ // Set uniqieing info, which will be used for unique the bug reports. The
+ // leaks should be uniqued on the allocation site.
+ UniqueingLocation = AllocLocation;
+ UniqueingDecl = AllocNode->getLocationContext()->getDecl();
+
// Fill in the description of the bug.
Description.clear();
llvm::raw_string_ostream os(Description);
@@ -2327,9 +2379,13 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
os << "(when using garbage collection) ";
os << "of an object";
- // FIXME: AllocBinding doesn't get populated for RegionStore yet.
- if (AllocBinding)
+ if (AllocBinding) {
os << " stored into '" << AllocBinding->getString() << '\'';
+ if (IncludeAllocationLine) {
+ FullSourceLoc SL(AllocStmt->getLocStart(), Ctx.getSourceManager());
+ os << " (allocated on line " << SL.getSpellingLineNumber() << ")";
+ }
+ }
addVisitor(new CFRefLeakReportVisitor(sym, GCEnabled, Log));
}
@@ -2370,8 +2426,14 @@ class RetainCountChecker
mutable SummaryLogTy SummaryLog;
mutable bool ShouldResetSummaryLog;
+ /// Optional setting to indicate if leak reports should include
+ /// the allocation line.
+ mutable bool IncludeAllocationLine;
+
public:
- RetainCountChecker() : ShouldResetSummaryLog(false) {}
+ RetainCountChecker(AnalyzerOptions &AO)
+ : ShouldResetSummaryLog(false),
+ IncludeAllocationLine(shouldIncludeAllocationSiteInLeakDiagnostics(AO)) {}
virtual ~RetainCountChecker() {
DeleteContainerSeconds(DeadSymbolTags);
@@ -3316,7 +3378,8 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
CFRefReport *report =
new CFRefLeakReport(*getLeakAtReturnBug(LOpts, GCEnabled),
LOpts, GCEnabled, SummaryLog,
- N, Sym, C);
+ N, Sym, C, IncludeAllocationLine);
+
C.emitReport(report);
}
}
@@ -3502,10 +3565,12 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
if (N) {
SmallString<128> sbuf;
llvm::raw_svector_ostream os(sbuf);
- os << "Object over-autoreleased: object was sent -autorelease ";
+ os << "Object was autoreleased ";
if (V.getAutoreleaseCount() > 1)
- os << V.getAutoreleaseCount() << " times ";
- os << "but the object has a +" << V.getCount() << " retain count";
+ os << V.getAutoreleaseCount() << " times but the object ";
+ else
+ os << "but ";
+ os << "has a +" << V.getCount() << " retain count";
if (!overAutorelease)
overAutorelease.reset(new OverAutorelease());
@@ -3556,7 +3621,8 @@ RetainCountChecker::processLeaks(ProgramStateRef state,
assert(BT && "BugType not initialized.");
CFRefLeakReport *report = new CFRefLeakReport(*BT, LOpts, GCEnabled,
- SummaryLog, N, *I, Ctx);
+ SummaryLog, N, *I, Ctx,
+ IncludeAllocationLine);
Ctx.emitReport(report);
}
}
@@ -3661,8 +3727,10 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
RefBindingsTy B = State->get<RefBindings>();
- if (!B.isEmpty())
- Out << Sep << NL;
+ if (B.isEmpty())
+ return;
+
+ Out << Sep << NL;
for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
Out << I->first << " : ";
@@ -3676,6 +3744,6 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
//===----------------------------------------------------------------------===//
void ento::registerRetainCountChecker(CheckerManager &Mgr) {
- Mgr.registerChecker<RetainCountChecker>();
+ Mgr.registerChecker<RetainCountChecker>(Mgr.getAnalyzerOptions());
}