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