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 
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 std::shared_ptr<PathDiagnosticPiece>
32 CFRefReportVisitor::VisitNode(const ExplodedNode *N,
33                               BugReporterContext &BRC, BugReport &BR) {
34   // FIXME: We will eventually need to handle non-statement-based events
35   // (__attribute__((cleanup))).
36   if (!N->getLocation().getAs<StmtPoint>())
37     return nullptr;
38 
39   // Check if the type state has changed.
40   ProgramStateRef PrevSt = N->getFirstPred()->getState();
41   ProgramStateRef CurrSt = N->getState();
42   const LocationContext *LCtx = N->getLocationContext();
43 
44   const RefVal* CurrT = getRefBinding(CurrSt, Sym);
45   if (!CurrT) return nullptr;
46 
47   const RefVal &CurrV = *CurrT;
48   const RefVal *PrevT = getRefBinding(PrevSt, Sym);
49 
50   // Create a string buffer to constain all the useful things we want
51   // to tell the user.
52   std::string sbuf;
53   llvm::raw_string_ostream os(sbuf);
54 
55   // This is the allocation site since the previous node had no bindings
56   // for this symbol.
57   if (!PrevT) {
58     const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
59 
60     if (isa<ObjCIvarRefExpr>(S) &&
61         isSynthesizedAccessor(LCtx->getStackFrame())) {
62       S = LCtx->getStackFrame()->getCallSite();
63     }
64 
65     if (isa<ObjCArrayLiteral>(S)) {
66       os << "NSArray literal is an object with a +0 retain count";
67     }
68     else if (isa<ObjCDictionaryLiteral>(S)) {
69       os << "NSDictionary literal is an object with a +0 retain count";
70     }
71     else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
72       if (isNumericLiteralExpression(BL->getSubExpr()))
73         os << "NSNumber literal is an object with a +0 retain count";
74       else {
75         const ObjCInterfaceDecl *BoxClass = nullptr;
76         if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
77           BoxClass = Method->getClassInterface();
78 
79         // We should always be able to find the boxing class interface,
80         // but consider this future-proofing.
81         if (BoxClass)
82           os << *BoxClass << " b";
83         else
84           os << "B";
85 
86         os << "oxed expression produces an object with a +0 retain count";
87       }
88     }
89     else if (isa<ObjCIvarRefExpr>(S)) {
90       os << "Object loaded from instance variable";
91     }
92     else {
93       if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
94         // Get the name of the callee (if it is available).
95         SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
96         if (const FunctionDecl *FD = X.getAsFunctionDecl())
97           os << "Call to function '" << *FD << '\'';
98         else
99           os << "function call";
100       }
101       else {
102         assert(isa<ObjCMessageExpr>(S));
103         CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
104         CallEventRef<ObjCMethodCall> Call
105           = Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
106 
107         switch (Call->getMessageKind()) {
108         case OCM_Message:
109           os << "Method";
110           break;
111         case OCM_PropertyAccess:
112           os << "Property";
113           break;
114         case OCM_Subscript:
115           os << "Subscript";
116           break;
117         }
118       }
119 
120       if (CurrV.getObjKind() == RetEffect::CF) {
121         os << " returns a Core Foundation object of type "
122            << Sym->getType().getAsString() << " with a ";
123       } else if (CurrV.getObjKind() == RetEffect::OS) {
124         os << " returns an OSObject of type "
125            << Sym->getType().getAsString() << " with a ";
126       } else if (CurrV.getObjKind() == RetEffect::Generalized) {
127         os << " returns an object of type " << Sym->getType().getAsString()
128            << " with a ";
129       } else {
130         assert (CurrV.getObjKind() == RetEffect::ObjC);
131         QualType T = Sym->getType();
132         if (!isa<ObjCObjectPointerType>(T)) {
133           os << " returns an Objective-C object with a ";
134         } else {
135           const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);
136           os << " returns an instance of "
137              << PT->getPointeeType().getAsString() << " with a ";
138         }
139       }
140 
141       if (CurrV.isOwned()) {
142         os << "+1 retain count";
143       } else {
144         assert (CurrV.isNotOwned());
145         os << "+0 retain count";
146       }
147     }
148 
149     PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
150                                   N->getLocationContext());
151     return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
152   }
153 
154   // Gather up the effects that were performed on the object at this
155   // program point
156   SmallVector<ArgEffect, 2> AEffects;
157 
158   const ExplodedNode *OrigNode = BRC.getNodeResolver().getOriginalNode(N);
159   if (const RetainSummary *Summ = SummaryLog.lookup(OrigNode)) {
160     // We only have summaries attached to nodes after evaluating CallExpr and
161     // ObjCMessageExprs.
162     const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
163 
164     if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
165       // Iterate through the parameter expressions and see if the symbol
166       // was ever passed as an argument.
167       unsigned i = 0;
168 
169       for (CallExpr::const_arg_iterator AI=CE->arg_begin(), AE=CE->arg_end();
170            AI!=AE; ++AI, ++i) {
171 
172         // Retrieve the value of the argument.  Is it the symbol
173         // we are interested in?
174         if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)
175           continue;
176 
177         // We have an argument.  Get the effect!
178         AEffects.push_back(Summ->getArg(i));
179       }
180     } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
181       if (const Expr *receiver = ME->getInstanceReceiver()) {
182         if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
183               .getAsLocSymbol() == Sym) {
184           // The symbol we are tracking is the receiver.
185           AEffects.push_back(Summ->getReceiverEffect());
186         }
187       }
188     }
189   }
190 
191   do {
192     // Get the previous type state.
193     RefVal PrevV = *PrevT;
194 
195     // Specially handle -dealloc.
196     if (std::find(AEffects.begin(), AEffects.end(), Dealloc) !=
197                           AEffects.end()) {
198       // Determine if the object's reference count was pushed to zero.
199       assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");
200       // We may not have transitioned to 'release' if we hit an error.
201       // This case is handled elsewhere.
202       if (CurrV.getKind() == RefVal::Released) {
203         assert(CurrV.getCombinedCounts() == 0);
204         os << "Object released by directly sending the '-dealloc' message";
205         break;
206       }
207     }
208 
209     // Determine if the typestate has changed.
210     if (!PrevV.hasSameState(CurrV))
211       switch (CurrV.getKind()) {
212         case RefVal::Owned:
213         case RefVal::NotOwned:
214           if (PrevV.getCount() == CurrV.getCount()) {
215             // Did an autorelease message get sent?
216             if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())
217               return nullptr;
218 
219             assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
220             os << "Object autoreleased";
221             break;
222           }
223 
224           if (PrevV.getCount() > CurrV.getCount())
225             os << "Reference count decremented.";
226           else
227             os << "Reference count incremented.";
228 
229           if (unsigned Count = CurrV.getCount())
230             os << " The object now has a +" << Count << " retain count.";
231 
232           break;
233 
234         case RefVal::Released:
235           if (CurrV.getIvarAccessHistory() ==
236                 RefVal::IvarAccessHistory::ReleasedAfterDirectAccess &&
237               CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) {
238             os << "Strong instance variable relinquished. ";
239           }
240           os << "Object released.";
241           break;
242 
243         case RefVal::ReturnedOwned:
244           // Autoreleases can be applied after marking a node ReturnedOwned.
245           if (CurrV.getAutoreleaseCount())
246             return nullptr;
247 
248           os << "Object returned to caller as an owning reference (single "
249                 "retain count transferred to caller)";
250           break;
251 
252         case RefVal::ReturnedNotOwned:
253           os << "Object returned to caller with a +0 retain count";
254           break;
255 
256         default:
257           return nullptr;
258       }
259   } while (0);
260 
261   if (os.str().empty())
262     return nullptr; // We have nothing to say!
263 
264   const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
265   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
266                                 N->getLocationContext());
267   auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
268 
269   // Add the range by scanning the children of the statement for any bindings
270   // to Sym.
271   for (const Stmt *Child : S->children())
272     if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))
273       if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {
274         P->addRange(Exp->getSourceRange());
275         break;
276       }
277 
278   return std::move(P);
279 }
280 
281 static Optional<std::string> describeRegion(const MemRegion *MR) {
282   if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
283     return std::string(VR->getDecl()->getName());
284   // Once we support more storage locations for bindings,
285   // this would need to be improved.
286   return None;
287 }
288 
289 namespace {
290 // Find the first node in the current function context that referred to the
291 // tracked symbol and the memory location that value was stored to. Note, the
292 // value is only reported if the allocation occurred in the same function as
293 // the leak. The function can also return a location context, which should be
294 // treated as interesting.
295 struct AllocationInfo {
296   const ExplodedNode* N;
297   const MemRegion *R;
298   const LocationContext *InterestingMethodContext;
299   AllocationInfo(const ExplodedNode *InN,
300                  const MemRegion *InR,
301                  const LocationContext *InInterestingMethodContext) :
302     N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
303 };
304 } // end anonymous namespace
305 
306 static AllocationInfo
307 GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
308                   SymbolRef Sym) {
309   const ExplodedNode *AllocationNode = N;
310   const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
311   const MemRegion *FirstBinding = nullptr;
312   const LocationContext *LeakContext = N->getLocationContext();
313 
314   // The location context of the init method called on the leaked object, if
315   // available.
316   const LocationContext *InitMethodContext = nullptr;
317 
318   while (N) {
319     ProgramStateRef St = N->getState();
320     const LocationContext *NContext = N->getLocationContext();
321 
322     if (!getRefBinding(St, Sym))
323       break;
324 
325     StoreManager::FindUniqueBinding FB(Sym);
326     StateMgr.iterBindings(St, FB);
327 
328     if (FB) {
329       const MemRegion *R = FB.getRegion();
330       const VarRegion *VR = R->getBaseRegion()->getAs<VarRegion>();
331       // Do not show local variables belonging to a function other than
332       // where the error is reported.
333       if (!VR || VR->getStackFrame() == LeakContext->getStackFrame())
334         FirstBinding = R;
335     }
336 
337     // AllocationNode is the last node in which the symbol was tracked.
338     AllocationNode = N;
339 
340     // AllocationNodeInCurrentContext, is the last node in the current or
341     // parent context in which the symbol was tracked.
342     //
343     // Note that the allocation site might be in the parent conext. For example,
344     // the case where an allocation happens in a block that captures a reference
345     // to it and that reference is overwritten/dropped by another call to
346     // the block.
347     if (NContext == LeakContext || NContext->isParentOf(LeakContext))
348       AllocationNodeInCurrentOrParentContext = N;
349 
350     // Find the last init that was called on the given symbol and store the
351     // init method's location context.
352     if (!InitMethodContext)
353       if (Optional<CallEnter> CEP = N->getLocation().getAs<CallEnter>()) {
354         const Stmt *CE = CEP->getCallExpr();
355         if (const ObjCMessageExpr *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
356           const Stmt *RecExpr = ME->getInstanceReceiver();
357           if (RecExpr) {
358             SVal RecV = St->getSVal(RecExpr, NContext);
359             if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
360               InitMethodContext = CEP->getCalleeContext();
361           }
362         }
363       }
364 
365     N = N->pred_empty() ? nullptr : *(N->pred_begin());
366   }
367 
368   // If we are reporting a leak of the object that was allocated with alloc,
369   // mark its init method as interesting.
370   const LocationContext *InterestingMethodContext = nullptr;
371   if (InitMethodContext) {
372     const ProgramPoint AllocPP = AllocationNode->getLocation();
373     if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
374       if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
375         if (ME->getMethodFamily() == OMF_alloc)
376           InterestingMethodContext = InitMethodContext;
377   }
378 
379   // If allocation happened in a function different from the leak node context,
380   // do not report the binding.
381   assert(N && "Could not find allocation node");
382   if (N->getLocationContext() != LeakContext) {
383     FirstBinding = nullptr;
384   }
385 
386   return AllocationInfo(AllocationNodeInCurrentOrParentContext,
387                         FirstBinding,
388                         InterestingMethodContext);
389 }
390 
391 std::shared_ptr<PathDiagnosticPiece>
392 CFRefReportVisitor::getEndPath(BugReporterContext &BRC,
393                                const ExplodedNode *EndN, BugReport &BR) {
394   BR.markInteresting(Sym);
395   return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
396 }
397 
398 std::shared_ptr<PathDiagnosticPiece>
399 CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
400                                    const ExplodedNode *EndN, BugReport &BR) {
401 
402   // Tell the BugReporterContext to report cases when the tracked symbol is
403   // assigned to different variables, etc.
404   BR.markInteresting(Sym);
405 
406   // We are reporting a leak.  Walk up the graph to get to the first node where
407   // the symbol appeared, and also get the first VarDecl that tracked object
408   // is stored to.
409   AllocationInfo AllocI =
410     GetAllocationSite(BRC.getStateManager(), EndN, Sym);
411 
412   const MemRegion* FirstBinding = AllocI.R;
413   BR.markInteresting(AllocI.InterestingMethodContext);
414 
415   SourceManager& SM = BRC.getSourceManager();
416 
417   // Compute an actual location for the leak.  Sometimes a leak doesn't
418   // occur at an actual statement (e.g., transition between blocks; end
419   // of function) so we need to walk the graph and compute a real location.
420   const ExplodedNode *LeakN = EndN;
421   PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM);
422 
423   std::string sbuf;
424   llvm::raw_string_ostream os(sbuf);
425 
426   os << "Object leaked: ";
427 
428   Optional<std::string> RegionDescription = describeRegion(FirstBinding);
429   if (RegionDescription) {
430     os << "object allocated and stored into '" << *RegionDescription << '\'';
431   }
432   else
433     os << "allocated object";
434 
435   // Get the retain count.
436   const RefVal* RV = getRefBinding(EndN->getState(), Sym);
437   assert(RV);
438 
439   if (RV->getKind() == RefVal::ErrorLeakReturned) {
440     // FIXME: Per comments in rdar://6320065, "create" only applies to CF
441     // objects.  Only "copy", "alloc", "retain" and "new" transfer ownership
442     // to the caller for NS objects.
443     const Decl *D = &EndN->getCodeDecl();
444 
445     os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
446                                   : " is returned from a function ");
447 
448     if (D->hasAttr<CFReturnsNotRetainedAttr>())
449       os << "that is annotated as CF_RETURNS_NOT_RETAINED";
450     else if (D->hasAttr<NSReturnsNotRetainedAttr>())
451       os << "that is annotated as NS_RETURNS_NOT_RETAINED";
452     else {
453       if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
454         if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) {
455           os << "managed by Automatic Reference Counting";
456         } else {
457           os << "whose name ('" << MD->getSelector().getAsString()
458              << "') does not start with "
459                 "'copy', 'mutableCopy', 'alloc' or 'new'."
460                 "  This violates the naming convention rules"
461                 " given in the Memory Management Guide for Cocoa";
462         }
463       } else {
464         const FunctionDecl *FD = cast<FunctionDecl>(D);
465         os << "whose name ('" << *FD
466            << "') does not contain 'Copy' or 'Create'.  This violates the naming"
467               " convention rules given in the Memory Management Guide for Core"
468               " Foundation";
469       }
470     }
471   }
472   else
473     os << " is not referenced later in this execution path and has a retain "
474           "count of +" << RV->getCount();
475 
476   return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
477 }
478 
479 void CFRefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) {
480   const SourceManager& SMgr = Ctx.getSourceManager();
481 
482   if (!sym->getOriginRegion())
483     return;
484 
485   auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion());
486   if (Region) {
487     const Decl *PDecl = Region->getDecl();
488     if (PDecl && isa<ParmVarDecl>(PDecl)) {
489       PathDiagnosticLocation ParamLocation = PathDiagnosticLocation::create(PDecl, SMgr);
490       Location = ParamLocation;
491       UniqueingLocation = ParamLocation;
492       UniqueingDecl = Ctx.getLocationContext()->getDecl();
493     }
494   }
495 }
496 
497 void CFRefLeakReport::deriveAllocLocation(CheckerContext &Ctx,SymbolRef sym) {
498   // Most bug reports are cached at the location where they occurred.
499   // With leaks, we want to unique them by the location where they were
500   // allocated, and only report a single path.  To do this, we need to find
501   // the allocation site of a piece of tracked memory, which we do via a
502   // call to GetAllocationSite.  This will walk the ExplodedGraph backwards.
503   // Note that this is *not* the trimmed graph; we are guaranteed, however,
504   // that all ancestor nodes that represent the allocation site have the
505   // same SourceLocation.
506   const ExplodedNode *AllocNode = nullptr;
507 
508   const SourceManager& SMgr = Ctx.getSourceManager();
509 
510   AllocationInfo AllocI =
511     GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
512 
513   AllocNode = AllocI.N;
514   AllocBinding = AllocI.R;
515   markInteresting(AllocI.InterestingMethodContext);
516 
517   // Get the SourceLocation for the allocation site.
518   // FIXME: This will crash the analyzer if an allocation comes from an
519   // implicit call (ex: a destructor call).
520   // (Currently there are no such allocations in Cocoa, though.)
521   AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
522 
523   if (!AllocStmt) {
524     AllocBinding = nullptr;
525     return;
526   }
527 
528   PathDiagnosticLocation AllocLocation =
529     PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
530                                         AllocNode->getLocationContext());
531   Location = AllocLocation;
532 
533   // Set uniqieing info, which will be used for unique the bug reports. The
534   // leaks should be uniqued on the allocation site.
535   UniqueingLocation = AllocLocation;
536   UniqueingDecl = AllocNode->getLocationContext()->getDecl();
537 }
538 
539 void CFRefLeakReport::createDescription(CheckerContext &Ctx,
540                                         bool IncludeAllocationLine) {
541   assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
542   Description.clear();
543   llvm::raw_string_ostream os(Description);
544   os << "Potential leak of an object";
545 
546   Optional<std::string> RegionDescription = describeRegion(AllocBinding);
547   if (RegionDescription) {
548     os << " stored into '" << *RegionDescription << '\'';
549     if (IncludeAllocationLine) {
550       FullSourceLoc SL(AllocStmt->getBeginLoc(), Ctx.getSourceManager());
551       os << " (allocated on line " << SL.getSpellingLineNumber() << ")";
552     }
553   }
554 }
555 
556 CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
557                                  const SummaryLogTy &Log,
558                                  ExplodedNode *n, SymbolRef sym,
559                                  CheckerContext &Ctx,
560                                  bool IncludeAllocationLine)
561   : CFRefReport(D, LOpts, Log, n, sym, false) {
562 
563   deriveAllocLocation(Ctx, sym);
564   if (!AllocBinding)
565     deriveParamLocation(Ctx, sym);
566 
567   createDescription(Ctx, IncludeAllocationLine);
568 
569   addVisitor(llvm::make_unique<CFRefLeakReportVisitor>(sym, Log));
570 }
571