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