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