blob: 4309603862a10bfaf370d1234d1365e30c6dc13f [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,
George Karpenkov717c4c02019-01-10 18:15:17 +000046 bool DeallocSent) {
George Karpenkovb3303d72018-11-30 02:17:05 +000047 // Get the previous type state.
48 RefVal PrevV = *PrevT;
49
50 // Specially handle -dealloc.
George Karpenkov717c4c02019-01-10 18:15:17 +000051 if (DeallocSent) {
George Karpenkovb3303d72018-11-30 02:17:05 +000052 // 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 Karpenkov3c1e0662019-01-10 18:28:10 +0000159 os << " returns ";
160
George Karpenkov7e3016d2019-01-10 18:13:46 +0000161 if (CurrV.getObjKind() == ObjKind::CF) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000162 os << "a Core Foundation object of type '"
George Karpenkov4f64b382019-01-10 18:15:57 +0000163 << Sym->getType().getAsString() << "' with a ";
George Karpenkov7e3016d2019-01-10 18:13:46 +0000164 } else if (CurrV.getObjKind() == ObjKind::OS) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000165 os << "an OSObject of type '" << getPrettyTypeName(Sym->getType())
George Karpenkov4f64b382019-01-10 18:15:57 +0000166 << "' with a ";
George Karpenkov7e3016d2019-01-10 18:13:46 +0000167 } else if (CurrV.getObjKind() == ObjKind::Generalized) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000168 os << "an object of type '" << Sym->getType().getAsString()
George Karpenkov4f64b382019-01-10 18:15:57 +0000169 << "' with a ";
George Karpenkov62db8862018-11-30 02:18:23 +0000170 } else {
George Karpenkov7e3016d2019-01-10 18:13:46 +0000171 assert(CurrV.getObjKind() == ObjKind::ObjC);
George Karpenkov62db8862018-11-30 02:18:23 +0000172 QualType T = Sym->getType();
173 if (!isa<ObjCObjectPointerType>(T)) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000174 os << "an Objective-C object with a ";
George Karpenkov62db8862018-11-30 02:18:23 +0000175 } else {
176 const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);
George Karpenkov3c1e0662019-01-10 18:28:10 +0000177 os << "an instance of " << PT->getPointeeType().getAsString()
George Karpenkov62db8862018-11-30 02:18:23 +0000178 << " with a ";
179 }
180 }
181
182 if (CurrV.isOwned()) {
183 os << "+1 retain count";
184 } else {
185 assert(CurrV.isNotOwned());
186 os << "+0 retain count";
187 }
188}
189
190namespace clang {
191namespace ento {
192namespace retaincountchecker {
193
George Karpenkov0bb17c42019-01-10 18:16:25 +0000194class RefCountReportVisitor : public BugReporterVisitor {
George Karpenkov62db8862018-11-30 02:18:23 +0000195protected:
196 SymbolRef Sym;
George Karpenkov62db8862018-11-30 02:18:23 +0000197
198public:
George Karpenkov0bb17c42019-01-10 18:16:25 +0000199 RefCountReportVisitor(SymbolRef sym) : Sym(sym) {}
George Karpenkov62db8862018-11-30 02:18:23 +0000200
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
George Karpenkov0bb17c42019-01-10 18:16:25 +0000216class RefLeakReportVisitor : public RefCountReportVisitor {
George Karpenkov62db8862018-11-30 02:18:23 +0000217public:
George Karpenkov0bb17c42019-01-10 18:16:25 +0000218 RefLeakReportVisitor(SymbolRef sym) : RefCountReportVisitor(sym) {}
George Karpenkov62db8862018-11-30 02:18:23 +0000219
220 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
221 const ExplodedNode *N,
222 BugReport &BR) override;
223};
224
225} // end namespace retaincountchecker
226} // end namespace ento
227} // end namespace clang
228
George Karpenkovff014862018-12-11 01:13:40 +0000229
230/// Find the first node with the parent stack frame.
231static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) {
232 const StackFrameContext *SC = Pred->getStackFrame();
233 if (SC->inTopFrame())
234 return nullptr;
235 const StackFrameContext *PC = SC->getParent()->getStackFrame();
236 if (!PC)
237 return nullptr;
238
239 const ExplodedNode *N = Pred;
240 while (N && N->getStackFrame() != PC) {
241 N = N->getFirstPred();
242 }
243 return N;
244}
245
246
247/// Insert a diagnostic piece at function exit
248/// if a function parameter is annotated as "os_consumed",
249/// but it does not actually consume the reference.
250static std::shared_ptr<PathDiagnosticEventPiece>
251annotateConsumedSummaryMismatch(const ExplodedNode *N,
252 CallExitBegin &CallExitLoc,
253 const SourceManager &SM,
254 CallEventManager &CEMgr) {
255
256 const ExplodedNode *CN = getCalleeNode(N);
257 if (!CN)
258 return nullptr;
259
260 CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState());
261
262 std::string sbuf;
263 llvm::raw_string_ostream os(sbuf);
264 ArrayRef<const ParmVarDecl *> Parameters = Call->parameters();
265 for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {
266 const ParmVarDecl *PVD = Parameters[I];
267
268 if (!PVD->hasAttr<OSConsumedAttr>())
George Karpenkovf5085322018-12-21 02:16:23 +0000269 continue;
George Karpenkovff014862018-12-11 01:13:40 +0000270
271 if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
272 const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR);
273 const RefVal *CountAtExit = getRefBinding(N->getState(), SR);
274
275 if (!CountBeforeCall || !CountAtExit)
276 continue;
277
278 unsigned CountBefore = CountBeforeCall->getCount();
279 unsigned CountAfter = CountAtExit->getCount();
280
281 bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
282 if (!AsExpected) {
283 os << "Parameter '";
284 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
285 /*Qualified=*/false);
George Karpenkov79f03402018-12-21 19:13:28 +0000286 os << "' is marked as consuming, but the function did not consume "
George Karpenkovff014862018-12-11 01:13:40 +0000287 << "the reference\n";
288 }
289 }
290 }
291
292 if (os.str().empty())
293 return nullptr;
294
295 // FIXME: remove the code duplication with NoStoreFuncVisitor.
296 PathDiagnosticLocation L;
297 if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) {
298 L = PathDiagnosticLocation::createBegin(RS, SM, N->getLocationContext());
299 } else {
300 L = PathDiagnosticLocation(
301 Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM);
302 }
303
304 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
305}
306
George Karpenkov70c2ee32018-08-17 21:41:07 +0000307std::shared_ptr<PathDiagnosticPiece>
George Karpenkov0bb17c42019-01-10 18:16:25 +0000308RefCountReportVisitor::VisitNode(const ExplodedNode *N,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000309 BugReporterContext &BRC, BugReport &BR) {
George Karpenkov717c4c02019-01-10 18:15:17 +0000310
George Karpenkovff014862018-12-11 01:13:40 +0000311 const SourceManager &SM = BRC.getSourceManager();
312 CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
George Karpenkovf5085322018-12-21 02:16:23 +0000313 if (auto CE = N->getLocationAs<CallExitBegin>())
George Karpenkovff014862018-12-11 01:13:40 +0000314 if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
315 return PD;
George Karpenkovff014862018-12-11 01:13:40 +0000316
George Karpenkov70c2ee32018-08-17 21:41:07 +0000317 // FIXME: We will eventually need to handle non-statement-based events
318 // (__attribute__((cleanup))).
319 if (!N->getLocation().getAs<StmtPoint>())
320 return nullptr;
321
322 // Check if the type state has changed.
George Karpenkov62db8862018-11-30 02:18:23 +0000323 const ExplodedNode *PrevNode = N->getFirstPred();
324 ProgramStateRef PrevSt = PrevNode->getState();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000325 ProgramStateRef CurrSt = N->getState();
326 const LocationContext *LCtx = N->getLocationContext();
327
328 const RefVal* CurrT = getRefBinding(CurrSt, Sym);
329 if (!CurrT) return nullptr;
330
331 const RefVal &CurrV = *CurrT;
332 const RefVal *PrevT = getRefBinding(PrevSt, Sym);
333
334 // Create a string buffer to constain all the useful things we want
335 // to tell the user.
336 std::string sbuf;
337 llvm::raw_string_ostream os(sbuf);
338
339 // This is the allocation site since the previous node had no bindings
340 // for this symbol.
341 if (!PrevT) {
342 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
343
344 if (isa<ObjCIvarRefExpr>(S) &&
345 isSynthesizedAccessor(LCtx->getStackFrame())) {
346 S = LCtx->getStackFrame()->getCallSite();
347 }
348
349 if (isa<ObjCArrayLiteral>(S)) {
350 os << "NSArray literal is an object with a +0 retain count";
George Karpenkovb3303d72018-11-30 02:17:05 +0000351 } else if (isa<ObjCDictionaryLiteral>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000352 os << "NSDictionary literal is an object with a +0 retain count";
George Karpenkovb3303d72018-11-30 02:17:05 +0000353 } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000354 if (isNumericLiteralExpression(BL->getSubExpr()))
355 os << "NSNumber literal is an object with a +0 retain count";
356 else {
357 const ObjCInterfaceDecl *BoxClass = nullptr;
358 if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
359 BoxClass = Method->getClassInterface();
360
361 // We should always be able to find the boxing class interface,
362 // but consider this future-proofing.
George Karpenkovb3303d72018-11-30 02:17:05 +0000363 if (BoxClass) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000364 os << *BoxClass << " b";
George Karpenkovb3303d72018-11-30 02:17:05 +0000365 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000366 os << "B";
George Karpenkovb3303d72018-11-30 02:17:05 +0000367 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000368
369 os << "oxed expression produces an object with a +0 retain count";
370 }
George Karpenkovb3303d72018-11-30 02:17:05 +0000371 } else if (isa<ObjCIvarRefExpr>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000372 os << "Object loaded from instance variable";
George Karpenkovb3303d72018-11-30 02:17:05 +0000373 } else {
George Karpenkov62db8862018-11-30 02:18:23 +0000374 generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000375 }
376
George Karpenkovff014862018-12-11 01:13:40 +0000377 PathDiagnosticLocation Pos(S, SM, N->getLocationContext());
George Karpenkov70c2ee32018-08-17 21:41:07 +0000378 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
379 }
380
381 // Gather up the effects that were performed on the object at this
382 // program point
George Karpenkov717c4c02019-01-10 18:15:17 +0000383 bool DeallocSent = false;
384
385 if (N->getLocation().getTag() &&
386 N->getLocation().getTag()->getTagDescription().contains(
387 RetainCountChecker::DeallocTagDescription)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000388 // We only have summaries attached to nodes after evaluating CallExpr and
389 // ObjCMessageExprs.
390 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
391
392 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
393 // Iterate through the parameter expressions and see if the symbol
394 // was ever passed as an argument.
395 unsigned i = 0;
396
George Karpenkovb3303d72018-11-30 02:17:05 +0000397 for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000398
399 // Retrieve the value of the argument. Is it the symbol
400 // we are interested in?
401 if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)
402 continue;
403
404 // We have an argument. Get the effect!
George Karpenkov717c4c02019-01-10 18:15:17 +0000405 DeallocSent = true;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000406 }
407 } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
408 if (const Expr *receiver = ME->getInstanceReceiver()) {
409 if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
410 .getAsLocSymbol() == Sym) {
411 // The symbol we are tracking is the receiver.
George Karpenkov717c4c02019-01-10 18:15:17 +0000412 DeallocSent = true;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000413 }
414 }
415 }
416 }
417
George Karpenkov717c4c02019-01-10 18:15:17 +0000418 if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent))
George Karpenkovb3303d72018-11-30 02:17:05 +0000419 return nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000420
421 if (os.str().empty())
422 return nullptr; // We have nothing to say!
423
424 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
425 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
426 N->getLocationContext());
427 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
428
429 // Add the range by scanning the children of the statement for any bindings
430 // to Sym.
431 for (const Stmt *Child : S->children())
432 if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))
433 if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {
434 P->addRange(Exp->getSourceRange());
435 break;
436 }
437
438 return std::move(P);
439}
440
441static Optional<std::string> describeRegion(const MemRegion *MR) {
442 if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
443 return std::string(VR->getDecl()->getName());
444 // Once we support more storage locations for bindings,
445 // this would need to be improved.
446 return None;
447}
448
449namespace {
450// Find the first node in the current function context that referred to the
451// tracked symbol and the memory location that value was stored to. Note, the
452// value is only reported if the allocation occurred in the same function as
453// the leak. The function can also return a location context, which should be
454// treated as interesting.
455struct AllocationInfo {
456 const ExplodedNode* N;
457 const MemRegion *R;
458 const LocationContext *InterestingMethodContext;
459 AllocationInfo(const ExplodedNode *InN,
460 const MemRegion *InR,
461 const LocationContext *InInterestingMethodContext) :
462 N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
463};
464} // end anonymous namespace
465
George Karpenkova1c3bb82018-11-30 02:17:31 +0000466static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
467 const ExplodedNode *N, SymbolRef Sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000468 const ExplodedNode *AllocationNode = N;
469 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
470 const MemRegion *FirstBinding = nullptr;
471 const LocationContext *LeakContext = N->getLocationContext();
472
473 // The location context of the init method called on the leaked object, if
474 // available.
475 const LocationContext *InitMethodContext = nullptr;
476
477 while (N) {
478 ProgramStateRef St = N->getState();
479 const LocationContext *NContext = N->getLocationContext();
480
481 if (!getRefBinding(St, Sym))
482 break;
483
484 StoreManager::FindUniqueBinding FB(Sym);
485 StateMgr.iterBindings(St, FB);
486
487 if (FB) {
488 const MemRegion *R = FB.getRegion();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000489 // Do not show local variables belonging to a function other than
490 // where the error is reported.
George Karpenkov79ed11c2018-12-11 01:13:20 +0000491 if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace()))
492 if (MR->getStackFrame() == LeakContext->getStackFrame())
493 FirstBinding = R;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000494 }
495
496 // AllocationNode is the last node in which the symbol was tracked.
497 AllocationNode = N;
498
499 // AllocationNodeInCurrentContext, is the last node in the current or
500 // parent context in which the symbol was tracked.
501 //
Raphael Isemannb23ccec2018-12-10 12:37:46 +0000502 // Note that the allocation site might be in the parent context. For example,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000503 // the case where an allocation happens in a block that captures a reference
504 // to it and that reference is overwritten/dropped by another call to
505 // the block.
506 if (NContext == LeakContext || NContext->isParentOf(LeakContext))
507 AllocationNodeInCurrentOrParentContext = N;
508
509 // Find the last init that was called on the given symbol and store the
510 // init method's location context.
511 if (!InitMethodContext)
George Karpenkova1c3bb82018-11-30 02:17:31 +0000512 if (auto CEP = N->getLocation().getAs<CallEnter>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000513 const Stmt *CE = CEP->getCallExpr();
George Karpenkova1c3bb82018-11-30 02:17:31 +0000514 if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000515 const Stmt *RecExpr = ME->getInstanceReceiver();
516 if (RecExpr) {
517 SVal RecV = St->getSVal(RecExpr, NContext);
518 if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
519 InitMethodContext = CEP->getCalleeContext();
520 }
521 }
522 }
523
George Karpenkova1c3bb82018-11-30 02:17:31 +0000524 N = N->getFirstPred();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000525 }
526
527 // If we are reporting a leak of the object that was allocated with alloc,
528 // mark its init method as interesting.
529 const LocationContext *InterestingMethodContext = nullptr;
530 if (InitMethodContext) {
531 const ProgramPoint AllocPP = AllocationNode->getLocation();
532 if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
533 if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
534 if (ME->getMethodFamily() == OMF_alloc)
535 InterestingMethodContext = InitMethodContext;
536 }
537
538 // If allocation happened in a function different from the leak node context,
539 // do not report the binding.
540 assert(N && "Could not find allocation node");
George Karpenkova1c3bb82018-11-30 02:17:31 +0000541
542 if (AllocationNodeInCurrentOrParentContext &&
543 AllocationNodeInCurrentOrParentContext->getLocationContext() !=
544 LeakContext)
George Karpenkov70c2ee32018-08-17 21:41:07 +0000545 FirstBinding = nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000546
547 return AllocationInfo(AllocationNodeInCurrentOrParentContext,
548 FirstBinding,
549 InterestingMethodContext);
550}
551
552std::shared_ptr<PathDiagnosticPiece>
George Karpenkov0bb17c42019-01-10 18:16:25 +0000553RefCountReportVisitor::getEndPath(BugReporterContext &BRC,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000554 const ExplodedNode *EndN, BugReport &BR) {
555 BR.markInteresting(Sym);
556 return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
557}
558
559std::shared_ptr<PathDiagnosticPiece>
George Karpenkov0bb17c42019-01-10 18:16:25 +0000560RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000561 const ExplodedNode *EndN, BugReport &BR) {
562
563 // Tell the BugReporterContext to report cases when the tracked symbol is
564 // assigned to different variables, etc.
565 BR.markInteresting(Sym);
566
567 // We are reporting a leak. Walk up the graph to get to the first node where
568 // the symbol appeared, and also get the first VarDecl that tracked object
569 // is stored to.
George Karpenkova1c3bb82018-11-30 02:17:31 +0000570 AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000571
572 const MemRegion* FirstBinding = AllocI.R;
573 BR.markInteresting(AllocI.InterestingMethodContext);
574
575 SourceManager& SM = BRC.getSourceManager();
576
577 // Compute an actual location for the leak. Sometimes a leak doesn't
578 // occur at an actual statement (e.g., transition between blocks; end
579 // of function) so we need to walk the graph and compute a real location.
580 const ExplodedNode *LeakN = EndN;
581 PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM);
582
583 std::string sbuf;
584 llvm::raw_string_ostream os(sbuf);
585
586 os << "Object leaked: ";
587
588 Optional<std::string> RegionDescription = describeRegion(FirstBinding);
589 if (RegionDescription) {
590 os << "object allocated and stored into '" << *RegionDescription << '\'';
George Karpenkovb3303d72018-11-30 02:17:05 +0000591 } else {
George Karpenkov4f64b382019-01-10 18:15:57 +0000592 os << "allocated object of type '" << getPrettyTypeName(Sym->getType())
593 << "'";
George Karpenkovb3303d72018-11-30 02:17:05 +0000594 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000595
596 // Get the retain count.
597 const RefVal* RV = getRefBinding(EndN->getState(), Sym);
598 assert(RV);
599
600 if (RV->getKind() == RefVal::ErrorLeakReturned) {
601 // FIXME: Per comments in rdar://6320065, "create" only applies to CF
602 // objects. Only "copy", "alloc", "retain" and "new" transfer ownership
603 // to the caller for NS objects.
604 const Decl *D = &EndN->getCodeDecl();
605
606 os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
607 : " is returned from a function ");
608
George Karpenkova1c3bb82018-11-30 02:17:31 +0000609 if (D->hasAttr<CFReturnsNotRetainedAttr>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000610 os << "that is annotated as CF_RETURNS_NOT_RETAINED";
George Karpenkova1c3bb82018-11-30 02:17:31 +0000611 } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000612 os << "that is annotated as NS_RETURNS_NOT_RETAINED";
George Karpenkovb43772d2018-11-30 02:18:50 +0000613 } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {
614 os << "that is annotated as OS_RETURNS_NOT_RETAINED";
George Karpenkova1c3bb82018-11-30 02:17:31 +0000615 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000616 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
617 if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) {
618 os << "managed by Automatic Reference Counting";
619 } else {
620 os << "whose name ('" << MD->getSelector().getAsString()
621 << "') does not start with "
622 "'copy', 'mutableCopy', 'alloc' or 'new'."
623 " This violates the naming convention rules"
624 " given in the Memory Management Guide for Cocoa";
625 }
George Karpenkov6e9fd132018-08-22 01:17:09 +0000626 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000627 const FunctionDecl *FD = cast<FunctionDecl>(D);
628 os << "whose name ('" << *FD
629 << "') does not contain 'Copy' or 'Create'. This violates the naming"
630 " convention rules given in the Memory Management Guide for Core"
631 " Foundation";
632 }
633 }
George Karpenkovf893ea12018-11-30 02:17:44 +0000634 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000635 os << " is not referenced later in this execution path and has a retain "
636 "count of +" << RV->getCount();
George Karpenkovf893ea12018-11-30 02:17:44 +0000637 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000638
639 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
640}
641
George Karpenkov0bb17c42019-01-10 18:16:25 +0000642RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts,
643 ExplodedNode *n, SymbolRef sym,
644 bool registerVisitor)
George Karpenkov62db8862018-11-30 02:18:23 +0000645 : BugReport(D, D.getDescription(), n), Sym(sym) {
646 if (registerVisitor)
George Karpenkov0bb17c42019-01-10 18:16:25 +0000647 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
George Karpenkov62db8862018-11-30 02:18:23 +0000648}
649
George Karpenkov0bb17c42019-01-10 18:16:25 +0000650RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts,
651 ExplodedNode *n, SymbolRef sym,
652 StringRef endText)
George Karpenkov62db8862018-11-30 02:18:23 +0000653 : BugReport(D, D.getDescription(), endText, n) {
654
George Karpenkov0bb17c42019-01-10 18:16:25 +0000655 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
George Karpenkov62db8862018-11-30 02:18:23 +0000656}
657
George Karpenkov0bb17c42019-01-10 18:16:25 +0000658void RefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000659 const SourceManager& SMgr = Ctx.getSourceManager();
660
661 if (!sym->getOriginRegion())
662 return;
663
664 auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion());
665 if (Region) {
666 const Decl *PDecl = Region->getDecl();
667 if (PDecl && isa<ParmVarDecl>(PDecl)) {
George Karpenkov717c4c02019-01-10 18:15:17 +0000668 PathDiagnosticLocation ParamLocation =
669 PathDiagnosticLocation::create(PDecl, SMgr);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000670 Location = ParamLocation;
671 UniqueingLocation = ParamLocation;
672 UniqueingDecl = Ctx.getLocationContext()->getDecl();
673 }
674 }
675}
676
George Karpenkov0bb17c42019-01-10 18:16:25 +0000677void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx,
George Karpenkova1c3bb82018-11-30 02:17:31 +0000678 SymbolRef sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000679 // Most bug reports are cached at the location where they occurred.
680 // With leaks, we want to unique them by the location where they were
681 // allocated, and only report a single path. To do this, we need to find
682 // the allocation site of a piece of tracked memory, which we do via a
683 // call to GetAllocationSite. This will walk the ExplodedGraph backwards.
684 // Note that this is *not* the trimmed graph; we are guaranteed, however,
685 // that all ancestor nodes that represent the allocation site have the
686 // same SourceLocation.
687 const ExplodedNode *AllocNode = nullptr;
688
689 const SourceManager& SMgr = Ctx.getSourceManager();
690
691 AllocationInfo AllocI =
George Karpenkova1c3bb82018-11-30 02:17:31 +0000692 GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000693
694 AllocNode = AllocI.N;
695 AllocBinding = AllocI.R;
696 markInteresting(AllocI.InterestingMethodContext);
697
698 // Get the SourceLocation for the allocation site.
699 // FIXME: This will crash the analyzer if an allocation comes from an
700 // implicit call (ex: a destructor call).
701 // (Currently there are no such allocations in Cocoa, though.)
702 AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
703
704 if (!AllocStmt) {
705 AllocBinding = nullptr;
706 return;
707 }
708
709 PathDiagnosticLocation AllocLocation =
710 PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
711 AllocNode->getLocationContext());
712 Location = AllocLocation;
713
714 // Set uniqieing info, which will be used for unique the bug reports. The
715 // leaks should be uniqued on the allocation site.
716 UniqueingLocation = AllocLocation;
717 UniqueingDecl = AllocNode->getLocationContext()->getDecl();
718}
719
George Karpenkov0bb17c42019-01-10 18:16:25 +0000720void RefLeakReport::createDescription(CheckerContext &Ctx) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000721 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
722 Description.clear();
723 llvm::raw_string_ostream os(Description);
724 os << "Potential leak of an object";
725
726 Optional<std::string> RegionDescription = describeRegion(AllocBinding);
727 if (RegionDescription) {
728 os << " stored into '" << *RegionDescription << '\'';
George Karpenkovf893ea12018-11-30 02:17:44 +0000729 } else {
730
731 // If we can't figure out the name, just supply the type information.
George Karpenkov4f64b382019-01-10 18:15:57 +0000732 os << " of type '" << getPrettyTypeName(Sym->getType()) << "'";
George Karpenkov70c2ee32018-08-17 21:41:07 +0000733 }
734}
735
George Karpenkov0bb17c42019-01-10 18:16:25 +0000736RefLeakReport::RefLeakReport(RefCountBug &D, const LangOptions &LOpts,
737 ExplodedNode *n, SymbolRef sym,
738 CheckerContext &Ctx)
739 : RefCountReport(D, LOpts, n, sym, false) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000740
741 deriveAllocLocation(Ctx, sym);
742 if (!AllocBinding)
743 deriveParamLocation(Ctx, sym);
744
George Karpenkov936a9c92018-12-07 20:21:37 +0000745 createDescription(Ctx);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000746
George Karpenkov0bb17c42019-01-10 18:16:25 +0000747 addVisitor(llvm::make_unique<RefLeakReportVisitor>(sym));
George Karpenkov70c2ee32018-08-17 21:41:07 +0000748}