1 // 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
18 using namespace clang;
19 using namespace ento;
20 using namespace retaincountchecker;
21
isNumericLiteralExpression(const Expr * E)22 static 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
31 /// If type represents a pointer to CXXRecordDecl,
32 /// and is not a typedef, return the decl name.
33 /// Otherwise, return the serialization of type.
getPrettyTypeName(QualType QT)34 static std::string getPrettyTypeName(QualType QT) {
35 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
42 /// Write information about the type state change to {@code os},
43 /// return whether the note should be generated.
shouldGenerateNote(llvm::raw_string_ostream & os,const RefVal * PrevT,const RefVal & CurrV,bool DeallocSent)44 static bool shouldGenerateNote(llvm::raw_string_ostream &os,
45 const RefVal *PrevT, const RefVal &CurrV,
46 bool DeallocSent) {
47 // Get the previous type state.
48 RefVal PrevV = *PrevT;
49
50 // Specially handle -dealloc.
51 if (DeallocSent) {
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
116 /// Finds argument index of the out paramter in the call {@code S}
117 /// corresponding to the symbol {@code Sym}.
118 /// If none found, returns None.
findArgIdxOfSymbol(ProgramStateRef CurrSt,const LocationContext * LCtx,SymbolRef & Sym,Optional<CallEventRef<>> CE)119 static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
120 const LocationContext *LCtx,
121 SymbolRef &Sym,
122 Optional<CallEventRef<>> CE) {
123 if (!CE)
124 return None;
125
126 for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++)
127 if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())
128 if (const auto *TR = dyn_cast<TypedValueRegion>(MR))
129 if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymExpr() == Sym)
130 return Idx;
131
132 return None;
133 }
134
generateDiagnosticsForCallLike(ProgramStateRef CurrSt,const LocationContext * LCtx,const RefVal & CurrV,SymbolRef & Sym,const Stmt * S,llvm::raw_string_ostream & os)135 static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
136 const LocationContext *LCtx,
137 const RefVal &CurrV, SymbolRef &Sym,
138 const Stmt *S,
139 llvm::raw_string_ostream &os) {
140 CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
141 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
142 // Get the name of the callee (if it is available)
143 // from the tracked SVal.
144 SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
145 const FunctionDecl *FD = X.getAsFunctionDecl();
146
147 // If failed, try to get it from AST.
148 if (!FD)
149 FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
150
151 if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) {
152 os << "Call to method '" << MD->getQualifiedNameAsString() << '\'';
153 } else if (FD) {
154 os << "Call to function '" << FD->getQualifiedNameAsString() << '\'';
155 } else {
156 os << "function call";
157 }
158 } else if (isa<CXXNewExpr>(S)) {
159 os << "Operator 'new'";
160 } else {
161 assert(isa<ObjCMessageExpr>(S));
162 CallEventRef<ObjCMethodCall> Call =
163 Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
164
165 switch (Call->getMessageKind()) {
166 case OCM_Message:
167 os << "Method";
168 break;
169 case OCM_PropertyAccess:
170 os << "Property";
171 break;
172 case OCM_Subscript:
173 os << "Subscript";
174 break;
175 }
176 }
177
178 Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx);
179 auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE);
180
181 // If index is not found, we assume that the symbol was returned.
182 if (!Idx) {
183 os << " returns ";
184 } else {
185 os << " writes ";
186 }
187
188 if (CurrV.getObjKind() == ObjKind::CF) {
189 os << "a Core Foundation object of type '"
190 << Sym->getType().getAsString() << "' with a ";
191 } else if (CurrV.getObjKind() == ObjKind::OS) {
192 os << "an OSObject of type '" << getPrettyTypeName(Sym->getType())
193 << "' with a ";
194 } else if (CurrV.getObjKind() == ObjKind::Generalized) {
195 os << "an object of type '" << Sym->getType().getAsString()
196 << "' with a ";
197 } else {
198 assert(CurrV.getObjKind() == ObjKind::ObjC);
199 QualType T = Sym->getType();
200 if (!isa<ObjCObjectPointerType>(T)) {
201 os << "an Objective-C object with a ";
202 } else {
203 const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);
204 os << "an instance of " << PT->getPointeeType().getAsString()
205 << " with a ";
206 }
207 }
208
209 if (CurrV.isOwned()) {
210 os << "+1 retain count";
211 } else {
212 assert(CurrV.isNotOwned());
213 os << "+0 retain count";
214 }
215
216 if (Idx) {
217 os << " into an out parameter '";
218 const ParmVarDecl *PVD = (*CE)->parameters()[*Idx];
219 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
220 /*Qualified=*/false);
221 os << "'";
222
223 QualType RT = (*CE)->getResultType();
224 if (!RT.isNull() && !RT->isVoidType()) {
225 SVal RV = (*CE)->getReturnValue();
226 if (CurrSt->isNull(RV).isConstrainedTrue()) {
227 os << " (assuming the call returns zero)";
228 } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) {
229 os << " (assuming the call returns non-zero)";
230 }
231
232 }
233 }
234 }
235
236 namespace clang {
237 namespace ento {
238 namespace retaincountchecker {
239
240 class RefCountReportVisitor : public BugReporterVisitor {
241 protected:
242 SymbolRef Sym;
243
244 public:
RefCountReportVisitor(SymbolRef sym)245 RefCountReportVisitor(SymbolRef sym) : Sym(sym) {}
246
Profile(llvm::FoldingSetNodeID & ID) const247 void Profile(llvm::FoldingSetNodeID &ID) const override {
248 static int x = 0;
249 ID.AddPointer(&x);
250 ID.AddPointer(Sym);
251 }
252
253 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
254 BugReporterContext &BRC,
255 BugReport &BR) override;
256
257 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
258 const ExplodedNode *N,
259 BugReport &BR) override;
260 };
261
262 class RefLeakReportVisitor : public RefCountReportVisitor {
263 public:
RefLeakReportVisitor(SymbolRef sym)264 RefLeakReportVisitor(SymbolRef sym) : RefCountReportVisitor(sym) {}
265
266 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
267 const ExplodedNode *N,
268 BugReport &BR) override;
269 };
270
271 } // end namespace retaincountchecker
272 } // end namespace ento
273 } // end namespace clang
274
275
276 /// Find the first node with the parent stack frame.
getCalleeNode(const ExplodedNode * Pred)277 static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) {
278 const StackFrameContext *SC = Pred->getStackFrame();
279 if (SC->inTopFrame())
280 return nullptr;
281 const StackFrameContext *PC = SC->getParent()->getStackFrame();
282 if (!PC)
283 return nullptr;
284
285 const ExplodedNode *N = Pred;
286 while (N && N->getStackFrame() != PC) {
287 N = N->getFirstPred();
288 }
289 return N;
290 }
291
292
293 /// Insert a diagnostic piece at function exit
294 /// if a function parameter is annotated as "os_consumed",
295 /// but it does not actually consume the reference.
296 static std::shared_ptr<PathDiagnosticEventPiece>
annotateConsumedSummaryMismatch(const ExplodedNode * N,CallExitBegin & CallExitLoc,const SourceManager & SM,CallEventManager & CEMgr)297 annotateConsumedSummaryMismatch(const ExplodedNode *N,
298 CallExitBegin &CallExitLoc,
299 const SourceManager &SM,
300 CallEventManager &CEMgr) {
301
302 const ExplodedNode *CN = getCalleeNode(N);
303 if (!CN)
304 return nullptr;
305
306 CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState());
307
308 std::string sbuf;
309 llvm::raw_string_ostream os(sbuf);
310 ArrayRef<const ParmVarDecl *> Parameters = Call->parameters();
311 for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {
312 const ParmVarDecl *PVD = Parameters[I];
313
314 if (!PVD->hasAttr<OSConsumedAttr>())
315 continue;
316
317 if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
318 const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR);
319 const RefVal *CountAtExit = getRefBinding(N->getState(), SR);
320
321 if (!CountBeforeCall || !CountAtExit)
322 continue;
323
324 unsigned CountBefore = CountBeforeCall->getCount();
325 unsigned CountAfter = CountAtExit->getCount();
326
327 bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
328 if (!AsExpected) {
329 os << "Parameter '";
330 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
331 /*Qualified=*/false);
332 os << "' is marked as consuming, but the function did not consume "
333 << "the reference\n";
334 }
335 }
336 }
337
338 if (os.str().empty())
339 return nullptr;
340
341 // FIXME: remove the code duplication with NoStoreFuncVisitor.
342 PathDiagnosticLocation L;
343 if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) {
344 L = PathDiagnosticLocation::createBegin(RS, SM, N->getLocationContext());
345 } else {
346 L = PathDiagnosticLocation(
347 Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM);
348 }
349
350 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
351 }
352
353 std::shared_ptr<PathDiagnosticPiece>
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,BugReport & BR)354 RefCountReportVisitor::VisitNode(const ExplodedNode *N,
355 BugReporterContext &BRC, BugReport &BR) {
356
357 const SourceManager &SM = BRC.getSourceManager();
358 CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
359 if (auto CE = N->getLocationAs<CallExitBegin>())
360 if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
361 return PD;
362
363 // FIXME: We will eventually need to handle non-statement-based events
364 // (__attribute__((cleanup))).
365 if (!N->getLocation().getAs<StmtPoint>())
366 return nullptr;
367
368 // Check if the type state has changed.
369 const ExplodedNode *PrevNode = N->getFirstPred();
370 ProgramStateRef PrevSt = PrevNode->getState();
371 ProgramStateRef CurrSt = N->getState();
372 const LocationContext *LCtx = N->getLocationContext();
373
374 const RefVal* CurrT = getRefBinding(CurrSt, Sym);
375 if (!CurrT) return nullptr;
376
377 const RefVal &CurrV = *CurrT;
378 const RefVal *PrevT = getRefBinding(PrevSt, Sym);
379
380 // Create a string buffer to constain all the useful things we want
381 // to tell the user.
382 std::string sbuf;
383 llvm::raw_string_ostream os(sbuf);
384
385 // This is the allocation site since the previous node had no bindings
386 // for this symbol.
387 if (!PrevT) {
388 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
389
390 if (isa<ObjCIvarRefExpr>(S) &&
391 isSynthesizedAccessor(LCtx->getStackFrame())) {
392 S = LCtx->getStackFrame()->getCallSite();
393 }
394
395 if (isa<ObjCArrayLiteral>(S)) {
396 os << "NSArray literal is an object with a +0 retain count";
397 } else if (isa<ObjCDictionaryLiteral>(S)) {
398 os << "NSDictionary literal is an object with a +0 retain count";
399 } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
400 if (isNumericLiteralExpression(BL->getSubExpr()))
401 os << "NSNumber literal is an object with a +0 retain count";
402 else {
403 const ObjCInterfaceDecl *BoxClass = nullptr;
404 if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
405 BoxClass = Method->getClassInterface();
406
407 // We should always be able to find the boxing class interface,
408 // but consider this future-proofing.
409 if (BoxClass) {
410 os << *BoxClass << " b";
411 } else {
412 os << "B";
413 }
414
415 os << "oxed expression produces an object with a +0 retain count";
416 }
417 } else if (isa<ObjCIvarRefExpr>(S)) {
418 os << "Object loaded from instance variable";
419 } else {
420 generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os);
421 }
422
423 PathDiagnosticLocation Pos(S, SM, N->getLocationContext());
424 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
425 }
426
427 // Gather up the effects that were performed on the object at this
428 // program point
429 bool DeallocSent = false;
430
431 if (N->getLocation().getTag() &&
432 N->getLocation().getTag()->getTagDescription().contains(
433 RetainCountChecker::DeallocTagDescription)) {
434 // We only have summaries attached to nodes after evaluating CallExpr and
435 // ObjCMessageExprs.
436 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
437
438 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
439 // Iterate through the parameter expressions and see if the symbol
440 // was ever passed as an argument.
441 unsigned i = 0;
442
443 for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) {
444
445 // Retrieve the value of the argument. Is it the symbol
446 // we are interested in?
447 if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)
448 continue;
449
450 // We have an argument. Get the effect!
451 DeallocSent = true;
452 }
453 } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
454 if (const Expr *receiver = ME->getInstanceReceiver()) {
455 if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
456 .getAsLocSymbol() == Sym) {
457 // The symbol we are tracking is the receiver.
458 DeallocSent = true;
459 }
460 }
461 }
462 }
463
464 if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent))
465 return nullptr;
466
467 if (os.str().empty())
468 return nullptr; // We have nothing to say!
469
470 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
471 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
472 N->getLocationContext());
473 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
474
475 // Add the range by scanning the children of the statement for any bindings
476 // to Sym.
477 for (const Stmt *Child : S->children())
478 if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))
479 if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {
480 P->addRange(Exp->getSourceRange());
481 break;
482 }
483
484 return std::move(P);
485 }
486
describeRegion(const MemRegion * MR)487 static Optional<std::string> describeRegion(const MemRegion *MR) {
488 if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
489 return std::string(VR->getDecl()->getName());
490 // Once we support more storage locations for bindings,
491 // this would need to be improved.
492 return None;
493 }
494
495 namespace {
496 // Find the first node in the current function context that referred to the
497 // tracked symbol and the memory location that value was stored to. Note, the
498 // value is only reported if the allocation occurred in the same function as
499 // the leak. The function can also return a location context, which should be
500 // treated as interesting.
501 struct AllocationInfo {
502 const ExplodedNode* N;
503 const MemRegion *R;
504 const LocationContext *InterestingMethodContext;
AllocationInfo__anon5d927bb10111::AllocationInfo505 AllocationInfo(const ExplodedNode *InN,
506 const MemRegion *InR,
507 const LocationContext *InInterestingMethodContext) :
508 N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
509 };
510 } // end anonymous namespace
511
GetAllocationSite(ProgramStateManager & StateMgr,const ExplodedNode * N,SymbolRef Sym)512 static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
513 const ExplodedNode *N, SymbolRef Sym) {
514 const ExplodedNode *AllocationNode = N;
515 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
516 const MemRegion *FirstBinding = nullptr;
517 const LocationContext *LeakContext = N->getLocationContext();
518
519 // The location context of the init method called on the leaked object, if
520 // available.
521 const LocationContext *InitMethodContext = nullptr;
522
523 while (N) {
524 ProgramStateRef St = N->getState();
525 const LocationContext *NContext = N->getLocationContext();
526
527 if (!getRefBinding(St, Sym))
528 break;
529
530 StoreManager::FindUniqueBinding FB(Sym);
531 StateMgr.iterBindings(St, FB);
532
533 if (FB) {
534 const MemRegion *R = FB.getRegion();
535 // Do not show local variables belonging to a function other than
536 // where the error is reported.
537 if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace()))
538 if (MR->getStackFrame() == LeakContext->getStackFrame())
539 FirstBinding = R;
540 }
541
542 // AllocationNode is the last node in which the symbol was tracked.
543 AllocationNode = N;
544
545 // AllocationNodeInCurrentContext, is the last node in the current or
546 // parent context in which the symbol was tracked.
547 //
548 // Note that the allocation site might be in the parent context. For example,
549 // the case where an allocation happens in a block that captures a reference
550 // to it and that reference is overwritten/dropped by another call to
551 // the block.
552 if (NContext == LeakContext || NContext->isParentOf(LeakContext))
553 AllocationNodeInCurrentOrParentContext = N;
554
555 // Find the last init that was called on the given symbol and store the
556 // init method's location context.
557 if (!InitMethodContext)
558 if (auto CEP = N->getLocation().getAs<CallEnter>()) {
559 const Stmt *CE = CEP->getCallExpr();
560 if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
561 const Stmt *RecExpr = ME->getInstanceReceiver();
562 if (RecExpr) {
563 SVal RecV = St->getSVal(RecExpr, NContext);
564 if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
565 InitMethodContext = CEP->getCalleeContext();
566 }
567 }
568 }
569
570 N = N->getFirstPred();
571 }
572
573 // If we are reporting a leak of the object that was allocated with alloc,
574 // mark its init method as interesting.
575 const LocationContext *InterestingMethodContext = nullptr;
576 if (InitMethodContext) {
577 const ProgramPoint AllocPP = AllocationNode->getLocation();
578 if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
579 if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
580 if (ME->getMethodFamily() == OMF_alloc)
581 InterestingMethodContext = InitMethodContext;
582 }
583
584 // If allocation happened in a function different from the leak node context,
585 // do not report the binding.
586 assert(N && "Could not find allocation node");
587
588 if (AllocationNodeInCurrentOrParentContext &&
589 AllocationNodeInCurrentOrParentContext->getLocationContext() !=
590 LeakContext)
591 FirstBinding = nullptr;
592
593 return AllocationInfo(AllocationNodeInCurrentOrParentContext,
594 FirstBinding,
595 InterestingMethodContext);
596 }
597
598 std::shared_ptr<PathDiagnosticPiece>
getEndPath(BugReporterContext & BRC,const ExplodedNode * EndN,BugReport & BR)599 RefCountReportVisitor::getEndPath(BugReporterContext &BRC,
600 const ExplodedNode *EndN, BugReport &BR) {
601 BR.markInteresting(Sym);
602 return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
603 }
604
605 std::shared_ptr<PathDiagnosticPiece>
getEndPath(BugReporterContext & BRC,const ExplodedNode * EndN,BugReport & BR)606 RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
607 const ExplodedNode *EndN, BugReport &BR) {
608
609 // Tell the BugReporterContext to report cases when the tracked symbol is
610 // assigned to different variables, etc.
611 BR.markInteresting(Sym);
612
613 // We are reporting a leak. Walk up the graph to get to the first node where
614 // the symbol appeared, and also get the first VarDecl that tracked object
615 // is stored to.
616 AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym);
617
618 const MemRegion* FirstBinding = AllocI.R;
619 BR.markInteresting(AllocI.InterestingMethodContext);
620
621 SourceManager& SM = BRC.getSourceManager();
622
623 // Compute an actual location for the leak. Sometimes a leak doesn't
624 // occur at an actual statement (e.g., transition between blocks; end
625 // of function) so we need to walk the graph and compute a real location.
626 const ExplodedNode *LeakN = EndN;
627 PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM);
628
629 std::string sbuf;
630 llvm::raw_string_ostream os(sbuf);
631
632 os << "Object leaked: ";
633
634 Optional<std::string> RegionDescription = describeRegion(FirstBinding);
635 if (RegionDescription) {
636 os << "object allocated and stored into '" << *RegionDescription << '\'';
637 } else {
638 os << "allocated object of type '" << getPrettyTypeName(Sym->getType())
639 << "'";
640 }
641
642 // Get the retain count.
643 const RefVal* RV = getRefBinding(EndN->getState(), Sym);
644 assert(RV);
645
646 if (RV->getKind() == RefVal::ErrorLeakReturned) {
647 // FIXME: Per comments in rdar://6320065, "create" only applies to CF
648 // objects. Only "copy", "alloc", "retain" and "new" transfer ownership
649 // to the caller for NS objects.
650 const Decl *D = &EndN->getCodeDecl();
651
652 os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
653 : " is returned from a function ");
654
655 if (D->hasAttr<CFReturnsNotRetainedAttr>()) {
656 os << "that is annotated as CF_RETURNS_NOT_RETAINED";
657 } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) {
658 os << "that is annotated as NS_RETURNS_NOT_RETAINED";
659 } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {
660 os << "that is annotated as OS_RETURNS_NOT_RETAINED";
661 } else {
662 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
663 if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) {
664 os << "managed by Automatic Reference Counting";
665 } else {
666 os << "whose name ('" << MD->getSelector().getAsString()
667 << "') does not start with "
668 "'copy', 'mutableCopy', 'alloc' or 'new'."
669 " This violates the naming convention rules"
670 " given in the Memory Management Guide for Cocoa";
671 }
672 } else {
673 const FunctionDecl *FD = cast<FunctionDecl>(D);
674 os << "whose name ('" << *FD
675 << "') does not contain 'Copy' or 'Create'. This violates the naming"
676 " convention rules given in the Memory Management Guide for Core"
677 " Foundation";
678 }
679 }
680 } else {
681 os << " is not referenced later in this execution path and has a retain "
682 "count of +" << RV->getCount();
683 }
684
685 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
686 }
687
RefCountReport(RefCountBug & D,const LangOptions & LOpts,ExplodedNode * n,SymbolRef sym,bool registerVisitor)688 RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts,
689 ExplodedNode *n, SymbolRef sym,
690 bool registerVisitor)
691 : BugReport(D, D.getDescription(), n), Sym(sym) {
692 if (registerVisitor)
693 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
694 }
695
RefCountReport(RefCountBug & D,const LangOptions & LOpts,ExplodedNode * n,SymbolRef sym,StringRef endText)696 RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts,
697 ExplodedNode *n, SymbolRef sym,
698 StringRef endText)
699 : BugReport(D, D.getDescription(), endText, n) {
700
701 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
702 }
703
deriveParamLocation(CheckerContext & Ctx,SymbolRef sym)704 void RefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) {
705 const SourceManager& SMgr = Ctx.getSourceManager();
706
707 if (!sym->getOriginRegion())
708 return;
709
710 auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion());
711 if (Region) {
712 const Decl *PDecl = Region->getDecl();
713 if (PDecl && isa<ParmVarDecl>(PDecl)) {
714 PathDiagnosticLocation ParamLocation =
715 PathDiagnosticLocation::create(PDecl, SMgr);
716 Location = ParamLocation;
717 UniqueingLocation = ParamLocation;
718 UniqueingDecl = Ctx.getLocationContext()->getDecl();
719 }
720 }
721 }
722
deriveAllocLocation(CheckerContext & Ctx,SymbolRef sym)723 void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx,
724 SymbolRef sym) {
725 // Most bug reports are cached at the location where they occurred.
726 // With leaks, we want to unique them by the location where they were
727 // allocated, and only report a single path. To do this, we need to find
728 // the allocation site of a piece of tracked memory, which we do via a
729 // call to GetAllocationSite. This will walk the ExplodedGraph backwards.
730 // Note that this is *not* the trimmed graph; we are guaranteed, however,
731 // that all ancestor nodes that represent the allocation site have the
732 // same SourceLocation.
733 const ExplodedNode *AllocNode = nullptr;
734
735 const SourceManager& SMgr = Ctx.getSourceManager();
736
737 AllocationInfo AllocI =
738 GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
739
740 AllocNode = AllocI.N;
741 AllocBinding = AllocI.R;
742 markInteresting(AllocI.InterestingMethodContext);
743
744 // Get the SourceLocation for the allocation site.
745 // FIXME: This will crash the analyzer if an allocation comes from an
746 // implicit call (ex: a destructor call).
747 // (Currently there are no such allocations in Cocoa, though.)
748 AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
749
750 if (!AllocStmt) {
751 AllocBinding = nullptr;
752 return;
753 }
754
755 PathDiagnosticLocation AllocLocation =
756 PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
757 AllocNode->getLocationContext());
758 Location = AllocLocation;
759
760 // Set uniqieing info, which will be used for unique the bug reports. The
761 // leaks should be uniqued on the allocation site.
762 UniqueingLocation = AllocLocation;
763 UniqueingDecl = AllocNode->getLocationContext()->getDecl();
764 }
765
createDescription(CheckerContext & Ctx)766 void RefLeakReport::createDescription(CheckerContext &Ctx) {
767 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
768 Description.clear();
769 llvm::raw_string_ostream os(Description);
770 os << "Potential leak of an object";
771
772 Optional<std::string> RegionDescription = describeRegion(AllocBinding);
773 if (RegionDescription) {
774 os << " stored into '" << *RegionDescription << '\'';
775 } else {
776
777 // If we can't figure out the name, just supply the type information.
778 os << " of type '" << getPrettyTypeName(Sym->getType()) << "'";
779 }
780 }
781
RefLeakReport(RefCountBug & D,const LangOptions & LOpts,ExplodedNode * n,SymbolRef sym,CheckerContext & Ctx)782 RefLeakReport::RefLeakReport(RefCountBug &D, const LangOptions &LOpts,
783 ExplodedNode *n, SymbolRef sym,
784 CheckerContext &Ctx)
785 : RefCountReport(D, LOpts, n, sym, false) {
786
787 deriveAllocLocation(Ctx, sym);
788 if (!AllocBinding)
789 deriveParamLocation(Ctx, sym);
790
791 createDescription(Ctx);
792
793 addVisitor(llvm::make_unique<RefLeakReportVisitor>(sym));
794 }
795