blob: 44cb7553c0d8da0dbc1fcc3abb730221b26cfaf2 [file] [log] [blame]
George Karpenkov70c2ee32018-08-17 21:41:07 +00001// RetainCountDiagnostics.cpp - Checks for leaks and other issues -*- C++ -*--//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines diagnostics for RetainCountChecker, which implements
11// a reference count checker for Core Foundation and Cocoa on (Mac OS X).
12//
13//===----------------------------------------------------------------------===//
14
15#include "RetainCountDiagnostics.h"
16#include "RetainCountChecker.h"
17
18using namespace clang;
19using namespace ento;
20using namespace retaincountchecker;
21
22static bool isNumericLiteralExpression(const Expr *E) {
23 // FIXME: This set of cases was copied from SemaExprObjC.
24 return isa<IntegerLiteral>(E) ||
25 isa<CharacterLiteral>(E) ||
26 isa<FloatingLiteral>(E) ||
27 isa<ObjCBoolLiteralExpr>(E) ||
28 isa<CXXBoolLiteralExpr>(E);
29}
30
George Karpenkovf893ea12018-11-30 02:17:44 +000031/// If type represents a pointer to CXXRecordDecl,
32/// and is not a typedef, return the decl name.
33/// Otherwise, return the serialization of type.
Haojian Wuceff7302018-11-30 09:23:01 +000034static std::string getPrettyTypeName(QualType QT) {
George Karpenkovf893ea12018-11-30 02:17:44 +000035 QualType PT = QT->getPointeeType();
36 if (!PT.isNull() && !QT->getAs<TypedefType>())
37 if (const auto *RD = PT->getAsCXXRecordDecl())
38 return RD->getName();
39 return QT.getAsString();
40}
41
George Karpenkovb3303d72018-11-30 02:17:05 +000042/// Write information about the type state change to {@code os},
43/// return whether the note should be generated.
44static bool shouldGenerateNote(llvm::raw_string_ostream &os,
45 const RefVal *PrevT, const RefVal &CurrV,
46 SmallVector<ArgEffect, 2> &AEffects) {
47 // Get the previous type state.
48 RefVal PrevV = *PrevT;
49
50 // Specially handle -dealloc.
51 if (std::find(AEffects.begin(), AEffects.end(), Dealloc) != AEffects.end()) {
52 // Determine if the object's reference count was pushed to zero.
53 assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");
54 // We may not have transitioned to 'release' if we hit an error.
55 // This case is handled elsewhere.
56 if (CurrV.getKind() == RefVal::Released) {
57 assert(CurrV.getCombinedCounts() == 0);
58 os << "Object released by directly sending the '-dealloc' message";
59 return true;
60 }
61 }
62
63 // Determine if the typestate has changed.
64 if (!PrevV.hasSameState(CurrV))
65 switch (CurrV.getKind()) {
66 case RefVal::Owned:
67 case RefVal::NotOwned:
68 if (PrevV.getCount() == CurrV.getCount()) {
69 // Did an autorelease message get sent?
70 if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())
71 return false;
72
73 assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
74 os << "Object autoreleased";
75 return true;
76 }
77
78 if (PrevV.getCount() > CurrV.getCount())
79 os << "Reference count decremented.";
80 else
81 os << "Reference count incremented.";
82
83 if (unsigned Count = CurrV.getCount())
84 os << " The object now has a +" << Count << " retain count.";
85
86 return true;
87
88 case RefVal::Released:
89 if (CurrV.getIvarAccessHistory() ==
90 RefVal::IvarAccessHistory::ReleasedAfterDirectAccess &&
91 CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) {
92 os << "Strong instance variable relinquished. ";
93 }
94 os << "Object released.";
95 return true;
96
97 case RefVal::ReturnedOwned:
98 // Autoreleases can be applied after marking a node ReturnedOwned.
99 if (CurrV.getAutoreleaseCount())
100 return false;
101
102 os << "Object returned to caller as an owning reference (single "
103 "retain count transferred to caller)";
104 return true;
105
106 case RefVal::ReturnedNotOwned:
107 os << "Object returned to caller with a +0 retain count";
108 return true;
109
110 default:
111 return false;
112 }
113 return true;
114}
115
George Karpenkovff014862018-12-11 01:13:40 +0000116static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
117 const LocationContext *LCtx,
118 const RefVal &CurrV, SymbolRef &Sym,
119 const Stmt *S,
120 llvm::raw_string_ostream &os) {
George Karpenkov62db8862018-11-30 02:18:23 +0000121 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
122 // Get the name of the callee (if it is available)
123 // from the tracked SVal.
124 SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
125 const FunctionDecl *FD = X.getAsFunctionDecl();
126
127 // If failed, try to get it from AST.
128 if (!FD)
129 FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
130
131 if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) {
132 os << "Call to method '" << MD->getQualifiedNameAsString() << '\'';
133 } else if (FD) {
134 os << "Call to function '" << FD->getQualifiedNameAsString() << '\'';
135 } else {
136 os << "function call";
137 }
George Karpenkovff014862018-12-11 01:13:40 +0000138 } else if (isa<CXXNewExpr>(S)) {
139 os << "Operator 'new'";
George Karpenkov62db8862018-11-30 02:18:23 +0000140 } else {
141 assert(isa<ObjCMessageExpr>(S));
142 CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
143 CallEventRef<ObjCMethodCall> Call =
144 Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
145
146 switch (Call->getMessageKind()) {
147 case OCM_Message:
148 os << "Method";
149 break;
150 case OCM_PropertyAccess:
151 os << "Property";
152 break;
153 case OCM_Subscript:
154 os << "Subscript";
155 break;
156 }
157 }
158
George Karpenkov7e3016d2019-01-10 18:13:46 +0000159 if (CurrV.getObjKind() == ObjKind::CF) {
George Karpenkov62db8862018-11-30 02:18:23 +0000160 os << " returns a Core Foundation object of type "
161 << Sym->getType().getAsString() << " with a ";
George Karpenkov7e3016d2019-01-10 18:13:46 +0000162 } else if (CurrV.getObjKind() == ObjKind::OS) {
George Karpenkov62db8862018-11-30 02:18:23 +0000163 os << " returns an OSObject of type " << getPrettyTypeName(Sym->getType())
164 << " with a ";
George Karpenkov7e3016d2019-01-10 18:13:46 +0000165 } else if (CurrV.getObjKind() == ObjKind::Generalized) {
George Karpenkov62db8862018-11-30 02:18:23 +0000166 os << " returns an object of type " << Sym->getType().getAsString()
167 << " with a ";
168 } else {
George Karpenkov7e3016d2019-01-10 18:13:46 +0000169 assert(CurrV.getObjKind() == ObjKind::ObjC);
George Karpenkov62db8862018-11-30 02:18:23 +0000170 QualType T = Sym->getType();
171 if (!isa<ObjCObjectPointerType>(T)) {
172 os << " returns an Objective-C object with a ";
173 } else {
174 const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);
175 os << " returns an instance of " << PT->getPointeeType().getAsString()
176 << " with a ";
177 }
178 }
179
180 if (CurrV.isOwned()) {
181 os << "+1 retain count";
182 } else {
183 assert(CurrV.isNotOwned());
184 os << "+0 retain count";
185 }
186}
187
188namespace clang {
189namespace ento {
190namespace retaincountchecker {
191
192class CFRefReportVisitor : public BugReporterVisitor {
193protected:
194 SymbolRef Sym;
195 const SummaryLogTy &SummaryLog;
196
197public:
198 CFRefReportVisitor(SymbolRef sym, const SummaryLogTy &log)
199 : Sym(sym), SummaryLog(log) {}
200
201 void Profile(llvm::FoldingSetNodeID &ID) const override {
202 static int x = 0;
203 ID.AddPointer(&x);
204 ID.AddPointer(Sym);
205 }
206
207 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
208 BugReporterContext &BRC,
209 BugReport &BR) override;
210
211 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
212 const ExplodedNode *N,
213 BugReport &BR) override;
214};
215
216class CFRefLeakReportVisitor : public CFRefReportVisitor {
217public:
218 CFRefLeakReportVisitor(SymbolRef sym,
219 const SummaryLogTy &log)
220 : CFRefReportVisitor(sym, log) {}
221
222 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
223 const ExplodedNode *N,
224 BugReport &BR) override;
225};
226
227} // end namespace retaincountchecker
228} // end namespace ento
229} // end namespace clang
230
George Karpenkovff014862018-12-11 01:13:40 +0000231
232/// Find the first node with the parent stack frame.
233static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) {
234 const StackFrameContext *SC = Pred->getStackFrame();
235 if (SC->inTopFrame())
236 return nullptr;
237 const StackFrameContext *PC = SC->getParent()->getStackFrame();
238 if (!PC)
239 return nullptr;
240
241 const ExplodedNode *N = Pred;
242 while (N && N->getStackFrame() != PC) {
243 N = N->getFirstPred();
244 }
245 return N;
246}
247
248
249/// Insert a diagnostic piece at function exit
250/// if a function parameter is annotated as "os_consumed",
251/// but it does not actually consume the reference.
252static std::shared_ptr<PathDiagnosticEventPiece>
253annotateConsumedSummaryMismatch(const ExplodedNode *N,
254 CallExitBegin &CallExitLoc,
255 const SourceManager &SM,
256 CallEventManager &CEMgr) {
257
258 const ExplodedNode *CN = getCalleeNode(N);
259 if (!CN)
260 return nullptr;
261
262 CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState());
263
264 std::string sbuf;
265 llvm::raw_string_ostream os(sbuf);
266 ArrayRef<const ParmVarDecl *> Parameters = Call->parameters();
267 for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {
268 const ParmVarDecl *PVD = Parameters[I];
269
270 if (!PVD->hasAttr<OSConsumedAttr>())
George Karpenkovf5085322018-12-21 02:16:23 +0000271 continue;
George Karpenkovff014862018-12-11 01:13:40 +0000272
273 if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
274 const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR);
275 const RefVal *CountAtExit = getRefBinding(N->getState(), SR);
276
277 if (!CountBeforeCall || !CountAtExit)
278 continue;
279
280 unsigned CountBefore = CountBeforeCall->getCount();
281 unsigned CountAfter = CountAtExit->getCount();
282
283 bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
284 if (!AsExpected) {
285 os << "Parameter '";
286 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
287 /*Qualified=*/false);
George Karpenkov79f03402018-12-21 19:13:28 +0000288 os << "' is marked as consuming, but the function did not consume "
George Karpenkovff014862018-12-11 01:13:40 +0000289 << "the reference\n";
290 }
291 }
292 }
293
294 if (os.str().empty())
295 return nullptr;
296
297 // FIXME: remove the code duplication with NoStoreFuncVisitor.
298 PathDiagnosticLocation L;
299 if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) {
300 L = PathDiagnosticLocation::createBegin(RS, SM, N->getLocationContext());
301 } else {
302 L = PathDiagnosticLocation(
303 Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM);
304 }
305
306 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
307}
308
George Karpenkov70c2ee32018-08-17 21:41:07 +0000309std::shared_ptr<PathDiagnosticPiece>
George Karpenkovc82d4572018-09-28 18:49:41 +0000310CFRefReportVisitor::VisitNode(const ExplodedNode *N,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000311 BugReporterContext &BRC, BugReport &BR) {
George Karpenkovff014862018-12-11 01:13:40 +0000312 const SourceManager &SM = BRC.getSourceManager();
313 CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
George Karpenkovf5085322018-12-21 02:16:23 +0000314 if (auto CE = N->getLocationAs<CallExitBegin>())
George Karpenkovff014862018-12-11 01:13:40 +0000315 if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
316 return PD;
George Karpenkovff014862018-12-11 01:13:40 +0000317
George Karpenkov70c2ee32018-08-17 21:41:07 +0000318 // FIXME: We will eventually need to handle non-statement-based events
319 // (__attribute__((cleanup))).
320 if (!N->getLocation().getAs<StmtPoint>())
321 return nullptr;
322
323 // Check if the type state has changed.
George Karpenkov62db8862018-11-30 02:18:23 +0000324 const ExplodedNode *PrevNode = N->getFirstPred();
325 ProgramStateRef PrevSt = PrevNode->getState();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000326 ProgramStateRef CurrSt = N->getState();
327 const LocationContext *LCtx = N->getLocationContext();
328
329 const RefVal* CurrT = getRefBinding(CurrSt, Sym);
330 if (!CurrT) return nullptr;
331
332 const RefVal &CurrV = *CurrT;
333 const RefVal *PrevT = getRefBinding(PrevSt, Sym);
334
335 // Create a string buffer to constain all the useful things we want
336 // to tell the user.
337 std::string sbuf;
338 llvm::raw_string_ostream os(sbuf);
339
340 // This is the allocation site since the previous node had no bindings
341 // for this symbol.
342 if (!PrevT) {
343 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
344
345 if (isa<ObjCIvarRefExpr>(S) &&
346 isSynthesizedAccessor(LCtx->getStackFrame())) {
347 S = LCtx->getStackFrame()->getCallSite();
348 }
349
350 if (isa<ObjCArrayLiteral>(S)) {
351 os << "NSArray literal is an object with a +0 retain count";
George Karpenkovb3303d72018-11-30 02:17:05 +0000352 } else if (isa<ObjCDictionaryLiteral>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000353 os << "NSDictionary literal is an object with a +0 retain count";
George Karpenkovb3303d72018-11-30 02:17:05 +0000354 } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000355 if (isNumericLiteralExpression(BL->getSubExpr()))
356 os << "NSNumber literal is an object with a +0 retain count";
357 else {
358 const ObjCInterfaceDecl *BoxClass = nullptr;
359 if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
360 BoxClass = Method->getClassInterface();
361
362 // We should always be able to find the boxing class interface,
363 // but consider this future-proofing.
George Karpenkovb3303d72018-11-30 02:17:05 +0000364 if (BoxClass) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000365 os << *BoxClass << " b";
George Karpenkovb3303d72018-11-30 02:17:05 +0000366 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000367 os << "B";
George Karpenkovb3303d72018-11-30 02:17:05 +0000368 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000369
370 os << "oxed expression produces an object with a +0 retain count";
371 }
George Karpenkovb3303d72018-11-30 02:17:05 +0000372 } else if (isa<ObjCIvarRefExpr>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000373 os << "Object loaded from instance variable";
George Karpenkovb3303d72018-11-30 02:17:05 +0000374 } else {
George Karpenkov62db8862018-11-30 02:18:23 +0000375 generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000376 }
377
George Karpenkovff014862018-12-11 01:13:40 +0000378 PathDiagnosticLocation Pos(S, SM, N->getLocationContext());
George Karpenkov70c2ee32018-08-17 21:41:07 +0000379 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
380 }
381
382 // Gather up the effects that were performed on the object at this
383 // program point
384 SmallVector<ArgEffect, 2> AEffects;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000385 const ExplodedNode *OrigNode = BRC.getNodeResolver().getOriginalNode(N);
386 if (const RetainSummary *Summ = SummaryLog.lookup(OrigNode)) {
387 // We only have summaries attached to nodes after evaluating CallExpr and
388 // ObjCMessageExprs.
389 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
390
391 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
392 // Iterate through the parameter expressions and see if the symbol
393 // was ever passed as an argument.
394 unsigned i = 0;
395
George Karpenkovb3303d72018-11-30 02:17:05 +0000396 for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000397
398 // Retrieve the value of the argument. Is it the symbol
399 // we are interested in?
400 if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)
401 continue;
402
403 // We have an argument. Get the effect!
404 AEffects.push_back(Summ->getArg(i));
405 }
406 } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
407 if (const Expr *receiver = ME->getInstanceReceiver()) {
408 if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
409 .getAsLocSymbol() == Sym) {
410 // The symbol we are tracking is the receiver.
411 AEffects.push_back(Summ->getReceiverEffect());
412 }
413 }
414 }
415 }
416
George Karpenkovb3303d72018-11-30 02:17:05 +0000417 if (!shouldGenerateNote(os, PrevT, CurrV, AEffects))
418 return nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000419
420 if (os.str().empty())
421 return nullptr; // We have nothing to say!
422
423 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
424 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
425 N->getLocationContext());
426 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
427
428 // Add the range by scanning the children of the statement for any bindings
429 // to Sym.
430 for (const Stmt *Child : S->children())
431 if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))
432 if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {
433 P->addRange(Exp->getSourceRange());
434 break;
435 }
436
437 return std::move(P);
438}
439
440static Optional<std::string> describeRegion(const MemRegion *MR) {
441 if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
442 return std::string(VR->getDecl()->getName());
443 // Once we support more storage locations for bindings,
444 // this would need to be improved.
445 return None;
446}
447
448namespace {
449// Find the first node in the current function context that referred to the
450// tracked symbol and the memory location that value was stored to. Note, the
451// value is only reported if the allocation occurred in the same function as
452// the leak. The function can also return a location context, which should be
453// treated as interesting.
454struct AllocationInfo {
455 const ExplodedNode* N;
456 const MemRegion *R;
457 const LocationContext *InterestingMethodContext;
458 AllocationInfo(const ExplodedNode *InN,
459 const MemRegion *InR,
460 const LocationContext *InInterestingMethodContext) :
461 N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
462};
463} // end anonymous namespace
464
George Karpenkova1c3bb82018-11-30 02:17:31 +0000465static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
466 const ExplodedNode *N, SymbolRef Sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000467 const ExplodedNode *AllocationNode = N;
468 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
469 const MemRegion *FirstBinding = nullptr;
470 const LocationContext *LeakContext = N->getLocationContext();
471
472 // The location context of the init method called on the leaked object, if
473 // available.
474 const LocationContext *InitMethodContext = nullptr;
475
476 while (N) {
477 ProgramStateRef St = N->getState();
478 const LocationContext *NContext = N->getLocationContext();
479
480 if (!getRefBinding(St, Sym))
481 break;
482
483 StoreManager::FindUniqueBinding FB(Sym);
484 StateMgr.iterBindings(St, FB);
485
486 if (FB) {
487 const MemRegion *R = FB.getRegion();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000488 // Do not show local variables belonging to a function other than
489 // where the error is reported.
George Karpenkov79ed11c2018-12-11 01:13:20 +0000490 if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace()))
491 if (MR->getStackFrame() == LeakContext->getStackFrame())
492 FirstBinding = R;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000493 }
494
495 // AllocationNode is the last node in which the symbol was tracked.
496 AllocationNode = N;
497
498 // AllocationNodeInCurrentContext, is the last node in the current or
499 // parent context in which the symbol was tracked.
500 //
Raphael Isemannb23ccec2018-12-10 12:37:46 +0000501 // Note that the allocation site might be in the parent context. For example,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000502 // the case where an allocation happens in a block that captures a reference
503 // to it and that reference is overwritten/dropped by another call to
504 // the block.
505 if (NContext == LeakContext || NContext->isParentOf(LeakContext))
506 AllocationNodeInCurrentOrParentContext = N;
507
508 // Find the last init that was called on the given symbol and store the
509 // init method's location context.
510 if (!InitMethodContext)
George Karpenkova1c3bb82018-11-30 02:17:31 +0000511 if (auto CEP = N->getLocation().getAs<CallEnter>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000512 const Stmt *CE = CEP->getCallExpr();
George Karpenkova1c3bb82018-11-30 02:17:31 +0000513 if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000514 const Stmt *RecExpr = ME->getInstanceReceiver();
515 if (RecExpr) {
516 SVal RecV = St->getSVal(RecExpr, NContext);
517 if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
518 InitMethodContext = CEP->getCalleeContext();
519 }
520 }
521 }
522
George Karpenkova1c3bb82018-11-30 02:17:31 +0000523 N = N->getFirstPred();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000524 }
525
526 // If we are reporting a leak of the object that was allocated with alloc,
527 // mark its init method as interesting.
528 const LocationContext *InterestingMethodContext = nullptr;
529 if (InitMethodContext) {
530 const ProgramPoint AllocPP = AllocationNode->getLocation();
531 if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
532 if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
533 if (ME->getMethodFamily() == OMF_alloc)
534 InterestingMethodContext = InitMethodContext;
535 }
536
537 // If allocation happened in a function different from the leak node context,
538 // do not report the binding.
539 assert(N && "Could not find allocation node");
George Karpenkova1c3bb82018-11-30 02:17:31 +0000540
541 if (AllocationNodeInCurrentOrParentContext &&
542 AllocationNodeInCurrentOrParentContext->getLocationContext() !=
543 LeakContext)
George Karpenkov70c2ee32018-08-17 21:41:07 +0000544 FirstBinding = nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000545
546 return AllocationInfo(AllocationNodeInCurrentOrParentContext,
547 FirstBinding,
548 InterestingMethodContext);
549}
550
551std::shared_ptr<PathDiagnosticPiece>
552CFRefReportVisitor::getEndPath(BugReporterContext &BRC,
553 const ExplodedNode *EndN, BugReport &BR) {
554 BR.markInteresting(Sym);
555 return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
556}
557
558std::shared_ptr<PathDiagnosticPiece>
559CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
560 const ExplodedNode *EndN, BugReport &BR) {
561
562 // Tell the BugReporterContext to report cases when the tracked symbol is
563 // assigned to different variables, etc.
564 BR.markInteresting(Sym);
565
566 // We are reporting a leak. Walk up the graph to get to the first node where
567 // the symbol appeared, and also get the first VarDecl that tracked object
568 // is stored to.
George Karpenkova1c3bb82018-11-30 02:17:31 +0000569 AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000570
571 const MemRegion* FirstBinding = AllocI.R;
572 BR.markInteresting(AllocI.InterestingMethodContext);
573
574 SourceManager& SM = BRC.getSourceManager();
575
576 // Compute an actual location for the leak. Sometimes a leak doesn't
577 // occur at an actual statement (e.g., transition between blocks; end
578 // of function) so we need to walk the graph and compute a real location.
579 const ExplodedNode *LeakN = EndN;
580 PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM);
581
582 std::string sbuf;
583 llvm::raw_string_ostream os(sbuf);
584
585 os << "Object leaked: ";
586
587 Optional<std::string> RegionDescription = describeRegion(FirstBinding);
588 if (RegionDescription) {
589 os << "object allocated and stored into '" << *RegionDescription << '\'';
George Karpenkovb3303d72018-11-30 02:17:05 +0000590 } else {
George Karpenkovf893ea12018-11-30 02:17:44 +0000591 os << "allocated object of type " << getPrettyTypeName(Sym->getType());
George Karpenkovb3303d72018-11-30 02:17:05 +0000592 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000593
594 // Get the retain count.
595 const RefVal* RV = getRefBinding(EndN->getState(), Sym);
596 assert(RV);
597
598 if (RV->getKind() == RefVal::ErrorLeakReturned) {
599 // FIXME: Per comments in rdar://6320065, "create" only applies to CF
600 // objects. Only "copy", "alloc", "retain" and "new" transfer ownership
601 // to the caller for NS objects.
602 const Decl *D = &EndN->getCodeDecl();
603
604 os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
605 : " is returned from a function ");
606
George Karpenkova1c3bb82018-11-30 02:17:31 +0000607 if (D->hasAttr<CFReturnsNotRetainedAttr>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000608 os << "that is annotated as CF_RETURNS_NOT_RETAINED";
George Karpenkova1c3bb82018-11-30 02:17:31 +0000609 } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000610 os << "that is annotated as NS_RETURNS_NOT_RETAINED";
George Karpenkovb43772d2018-11-30 02:18:50 +0000611 } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {
612 os << "that is annotated as OS_RETURNS_NOT_RETAINED";
George Karpenkova1c3bb82018-11-30 02:17:31 +0000613 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000614 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
615 if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) {
616 os << "managed by Automatic Reference Counting";
617 } else {
618 os << "whose name ('" << MD->getSelector().getAsString()
619 << "') does not start with "
620 "'copy', 'mutableCopy', 'alloc' or 'new'."
621 " This violates the naming convention rules"
622 " given in the Memory Management Guide for Cocoa";
623 }
George Karpenkov6e9fd132018-08-22 01:17:09 +0000624 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000625 const FunctionDecl *FD = cast<FunctionDecl>(D);
626 os << "whose name ('" << *FD
627 << "') does not contain 'Copy' or 'Create'. This violates the naming"
628 " convention rules given in the Memory Management Guide for Core"
629 " Foundation";
630 }
631 }
George Karpenkovf893ea12018-11-30 02:17:44 +0000632 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000633 os << " is not referenced later in this execution path and has a retain "
634 "count of +" << RV->getCount();
George Karpenkovf893ea12018-11-30 02:17:44 +0000635 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000636
637 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
638}
639
George Karpenkov62db8862018-11-30 02:18:23 +0000640CFRefReport::CFRefReport(CFRefBug &D, const LangOptions &LOpts,
641 const SummaryLogTy &Log, ExplodedNode *n,
642 SymbolRef sym, bool registerVisitor)
643 : BugReport(D, D.getDescription(), n), Sym(sym) {
644 if (registerVisitor)
645 addVisitor(llvm::make_unique<CFRefReportVisitor>(sym, Log));
646}
647
648CFRefReport::CFRefReport(CFRefBug &D, const LangOptions &LOpts,
649 const SummaryLogTy &Log, ExplodedNode *n,
650 SymbolRef sym, StringRef endText)
651 : BugReport(D, D.getDescription(), endText, n) {
652
653 addVisitor(llvm::make_unique<CFRefReportVisitor>(sym, Log));
654}
655
George Karpenkov70c2ee32018-08-17 21:41:07 +0000656void CFRefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) {
657 const SourceManager& SMgr = Ctx.getSourceManager();
658
659 if (!sym->getOriginRegion())
660 return;
661
662 auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion());
663 if (Region) {
664 const Decl *PDecl = Region->getDecl();
665 if (PDecl && isa<ParmVarDecl>(PDecl)) {
666 PathDiagnosticLocation ParamLocation = PathDiagnosticLocation::create(PDecl, SMgr);
667 Location = ParamLocation;
668 UniqueingLocation = ParamLocation;
669 UniqueingDecl = Ctx.getLocationContext()->getDecl();
670 }
671 }
672}
673
George Karpenkova1c3bb82018-11-30 02:17:31 +0000674void CFRefLeakReport::deriveAllocLocation(CheckerContext &Ctx,
675 SymbolRef sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000676 // Most bug reports are cached at the location where they occurred.
677 // With leaks, we want to unique them by the location where they were
678 // allocated, and only report a single path. To do this, we need to find
679 // the allocation site of a piece of tracked memory, which we do via a
680 // call to GetAllocationSite. This will walk the ExplodedGraph backwards.
681 // Note that this is *not* the trimmed graph; we are guaranteed, however,
682 // that all ancestor nodes that represent the allocation site have the
683 // same SourceLocation.
684 const ExplodedNode *AllocNode = nullptr;
685
686 const SourceManager& SMgr = Ctx.getSourceManager();
687
688 AllocationInfo AllocI =
George Karpenkova1c3bb82018-11-30 02:17:31 +0000689 GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000690
691 AllocNode = AllocI.N;
692 AllocBinding = AllocI.R;
693 markInteresting(AllocI.InterestingMethodContext);
694
695 // Get the SourceLocation for the allocation site.
696 // FIXME: This will crash the analyzer if an allocation comes from an
697 // implicit call (ex: a destructor call).
698 // (Currently there are no such allocations in Cocoa, though.)
699 AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
700
701 if (!AllocStmt) {
702 AllocBinding = nullptr;
703 return;
704 }
705
706 PathDiagnosticLocation AllocLocation =
707 PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
708 AllocNode->getLocationContext());
709 Location = AllocLocation;
710
711 // Set uniqieing info, which will be used for unique the bug reports. The
712 // leaks should be uniqued on the allocation site.
713 UniqueingLocation = AllocLocation;
714 UniqueingDecl = AllocNode->getLocationContext()->getDecl();
715}
716
George Karpenkov936a9c92018-12-07 20:21:37 +0000717void CFRefLeakReport::createDescription(CheckerContext &Ctx) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000718 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
719 Description.clear();
720 llvm::raw_string_ostream os(Description);
721 os << "Potential leak of an object";
722
723 Optional<std::string> RegionDescription = describeRegion(AllocBinding);
724 if (RegionDescription) {
725 os << " stored into '" << *RegionDescription << '\'';
George Karpenkovf893ea12018-11-30 02:17:44 +0000726 } else {
727
728 // If we can't figure out the name, just supply the type information.
729 os << " of type " << getPrettyTypeName(Sym->getType());
George Karpenkov70c2ee32018-08-17 21:41:07 +0000730 }
731}
732
733CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
734 const SummaryLogTy &Log,
735 ExplodedNode *n, SymbolRef sym,
George Karpenkov936a9c92018-12-07 20:21:37 +0000736 CheckerContext &Ctx)
George Karpenkov70c2ee32018-08-17 21:41:07 +0000737 : CFRefReport(D, LOpts, Log, n, sym, false) {
738
739 deriveAllocLocation(Ctx, sym);
740 if (!AllocBinding)
741 deriveParamLocation(Ctx, sym);
742
George Karpenkov936a9c92018-12-07 20:21:37 +0000743 createDescription(Ctx);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000744
745 addVisitor(llvm::make_unique<CFRefLeakReportVisitor>(sym, Log));
746}