blob: 0366910870feeb8e7c7e47c73d86a0e924a41975 [file] [log] [blame]
George Karpenkov70c2ee32018-08-17 21:41:07 +00001// RetainCountDiagnostics.cpp - Checks for leaks and other issues -*- C++ -*--//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
George Karpenkov70c2ee32018-08-17 21:41:07 +00006//
7//===----------------------------------------------------------------------===//
8//
9// This file defines diagnostics for RetainCountChecker, which implements
10// a reference count checker for Core Foundation and Cocoa on (Mac OS X).
11//
12//===----------------------------------------------------------------------===//
13
14#include "RetainCountDiagnostics.h"
15#include "RetainCountChecker.h"
16
17using namespace clang;
18using namespace ento;
19using namespace retaincountchecker;
20
George Karpenkov2c2d0b62019-01-18 19:24:55 +000021StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) {
22 switch (BT) {
23 case UseAfterRelease:
24 return "Use-after-release";
25 case ReleaseNotOwned:
26 return "Bad release";
27 case DeallocNotOwned:
28 return "-dealloc sent to non-exclusively owned object";
29 case FreeNotOwned:
30 return "freeing non-exclusively owned object";
31 case OverAutorelease:
32 return "Object autoreleased too many times";
33 case ReturnNotOwnedForOwned:
34 return "Method should return an owned object";
35 case LeakWithinFunction:
36 return "Leak";
37 case LeakAtReturn:
38 return "Leak of returned object";
39 }
Simon Pilgrimc6368062019-01-18 20:40:35 +000040 llvm_unreachable("Unknown RefCountBugType");
George Karpenkov2c2d0b62019-01-18 19:24:55 +000041}
42
43StringRef RefCountBug::getDescription() const {
44 switch (BT) {
45 case UseAfterRelease:
46 return "Reference-counted object is used after it is released";
47 case ReleaseNotOwned:
48 return "Incorrect decrement of the reference count of an object that is "
49 "not owned at this point by the caller";
50 case DeallocNotOwned:
51 return "-dealloc sent to object that may be referenced elsewhere";
52 case FreeNotOwned:
53 return "'free' called on an object that may be referenced elsewhere";
54 case OverAutorelease:
55 return "Object autoreleased too many times";
56 case ReturnNotOwnedForOwned:
57 return "Object with a +0 retain count returned to caller where a +1 "
58 "(owning) retain count is expected";
59 case LeakWithinFunction:
60 case LeakAtReturn:
61 return "";
62 }
Simon Pilgrimc6368062019-01-18 20:40:35 +000063 llvm_unreachable("Unknown RefCountBugType");
George Karpenkov2c2d0b62019-01-18 19:24:55 +000064}
65
66RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT)
67 : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount,
68 /*SupressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn),
George Karpenkova9e29562019-01-22 19:51:00 +000069 BT(BT), Checker(Checker) {}
George Karpenkov2c2d0b62019-01-18 19:24:55 +000070
George Karpenkov70c2ee32018-08-17 21:41:07 +000071static bool isNumericLiteralExpression(const Expr *E) {
72 // FIXME: This set of cases was copied from SemaExprObjC.
73 return isa<IntegerLiteral>(E) ||
74 isa<CharacterLiteral>(E) ||
75 isa<FloatingLiteral>(E) ||
76 isa<ObjCBoolLiteralExpr>(E) ||
77 isa<CXXBoolLiteralExpr>(E);
78}
79
George Karpenkovf893ea12018-11-30 02:17:44 +000080/// If type represents a pointer to CXXRecordDecl,
81/// and is not a typedef, return the decl name.
82/// Otherwise, return the serialization of type.
Haojian Wuceff7302018-11-30 09:23:01 +000083static std::string getPrettyTypeName(QualType QT) {
George Karpenkovf893ea12018-11-30 02:17:44 +000084 QualType PT = QT->getPointeeType();
85 if (!PT.isNull() && !QT->getAs<TypedefType>())
86 if (const auto *RD = PT->getAsCXXRecordDecl())
87 return RD->getName();
88 return QT.getAsString();
89}
90
George Karpenkovb3303d72018-11-30 02:17:05 +000091/// Write information about the type state change to {@code os},
92/// return whether the note should be generated.
93static bool shouldGenerateNote(llvm::raw_string_ostream &os,
George Karpenkov2c2d0b62019-01-18 19:24:55 +000094 const RefVal *PrevT,
95 const RefVal &CurrV,
George Karpenkov717c4c02019-01-10 18:15:17 +000096 bool DeallocSent) {
George Karpenkovb3303d72018-11-30 02:17:05 +000097 // Get the previous type state.
98 RefVal PrevV = *PrevT;
99
100 // Specially handle -dealloc.
George Karpenkov717c4c02019-01-10 18:15:17 +0000101 if (DeallocSent) {
George Karpenkovb3303d72018-11-30 02:17:05 +0000102 // Determine if the object's reference count was pushed to zero.
103 assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");
104 // We may not have transitioned to 'release' if we hit an error.
105 // This case is handled elsewhere.
106 if (CurrV.getKind() == RefVal::Released) {
107 assert(CurrV.getCombinedCounts() == 0);
108 os << "Object released by directly sending the '-dealloc' message";
109 return true;
110 }
111 }
112
113 // Determine if the typestate has changed.
114 if (!PrevV.hasSameState(CurrV))
115 switch (CurrV.getKind()) {
116 case RefVal::Owned:
117 case RefVal::NotOwned:
118 if (PrevV.getCount() == CurrV.getCount()) {
119 // Did an autorelease message get sent?
120 if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())
121 return false;
122
123 assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
124 os << "Object autoreleased";
125 return true;
126 }
127
128 if (PrevV.getCount() > CurrV.getCount())
129 os << "Reference count decremented.";
130 else
131 os << "Reference count incremented.";
132
133 if (unsigned Count = CurrV.getCount())
134 os << " The object now has a +" << Count << " retain count.";
135
136 return true;
137
138 case RefVal::Released:
139 if (CurrV.getIvarAccessHistory() ==
140 RefVal::IvarAccessHistory::ReleasedAfterDirectAccess &&
141 CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) {
142 os << "Strong instance variable relinquished. ";
143 }
144 os << "Object released.";
145 return true;
146
147 case RefVal::ReturnedOwned:
148 // Autoreleases can be applied after marking a node ReturnedOwned.
149 if (CurrV.getAutoreleaseCount())
150 return false;
151
152 os << "Object returned to caller as an owning reference (single "
153 "retain count transferred to caller)";
154 return true;
155
156 case RefVal::ReturnedNotOwned:
157 os << "Object returned to caller with a +0 retain count";
158 return true;
159
160 default:
161 return false;
162 }
163 return true;
164}
165
George Karpenkov5be959c2019-01-11 23:35:17 +0000166/// Finds argument index of the out paramter in the call {@code S}
167/// corresponding to the symbol {@code Sym}.
168/// If none found, returns None.
169static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
170 const LocationContext *LCtx,
171 SymbolRef &Sym,
172 Optional<CallEventRef<>> CE) {
173 if (!CE)
174 return None;
175
176 for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++)
177 if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())
178 if (const auto *TR = dyn_cast<TypedValueRegion>(MR))
179 if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymExpr() == Sym)
180 return Idx;
181
182 return None;
183}
184
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000185Optional<std::string> findMetaClassAlloc(const Expr *Callee) {
186 if (const auto *ME = dyn_cast<MemberExpr>(Callee)) {
187 if (ME->getMemberDecl()->getNameAsString() != "alloc")
188 return None;
189 const Expr *This = ME->getBase()->IgnoreParenImpCasts();
190 if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) {
191 const ValueDecl *VD = DRE->getDecl();
192 if (VD->getNameAsString() != "metaClass")
193 return None;
194
195 if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext()))
196 return RD->getNameAsString();
197
198 }
199 }
200 return None;
201}
202
203std::string findAllocatedObjectName(const Stmt *S,
204 QualType QT) {
205 if (const auto *CE = dyn_cast<CallExpr>(S))
206 if (auto Out = findMetaClassAlloc(CE->getCallee()))
207 return *Out;
208 return getPrettyTypeName(QT);
209}
210
George Karpenkovff014862018-12-11 01:13:40 +0000211static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
212 const LocationContext *LCtx,
213 const RefVal &CurrV, SymbolRef &Sym,
214 const Stmt *S,
215 llvm::raw_string_ostream &os) {
George Karpenkov5be959c2019-01-11 23:35:17 +0000216 CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
George Karpenkov62db8862018-11-30 02:18:23 +0000217 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
218 // Get the name of the callee (if it is available)
219 // from the tracked SVal.
220 SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
221 const FunctionDecl *FD = X.getAsFunctionDecl();
222
223 // If failed, try to get it from AST.
224 if (!FD)
225 FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
226
227 if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) {
228 os << "Call to method '" << MD->getQualifiedNameAsString() << '\'';
229 } else if (FD) {
230 os << "Call to function '" << FD->getQualifiedNameAsString() << '\'';
231 } else {
232 os << "function call";
233 }
George Karpenkovff014862018-12-11 01:13:40 +0000234 } else if (isa<CXXNewExpr>(S)) {
235 os << "Operator 'new'";
George Karpenkov62db8862018-11-30 02:18:23 +0000236 } else {
237 assert(isa<ObjCMessageExpr>(S));
George Karpenkov62db8862018-11-30 02:18:23 +0000238 CallEventRef<ObjCMethodCall> Call =
239 Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
240
241 switch (Call->getMessageKind()) {
242 case OCM_Message:
243 os << "Method";
244 break;
245 case OCM_PropertyAccess:
246 os << "Property";
247 break;
248 case OCM_Subscript:
249 os << "Subscript";
250 break;
251 }
252 }
253
George Karpenkov5be959c2019-01-11 23:35:17 +0000254 Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx);
255 auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE);
256
257 // If index is not found, we assume that the symbol was returned.
258 if (!Idx) {
259 os << " returns ";
260 } else {
261 os << " writes ";
262 }
George Karpenkov3c1e0662019-01-10 18:28:10 +0000263
George Karpenkov7e3016d2019-01-10 18:13:46 +0000264 if (CurrV.getObjKind() == ObjKind::CF) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000265 os << "a Core Foundation object of type '"
George Karpenkov4f64b382019-01-10 18:15:57 +0000266 << Sym->getType().getAsString() << "' with a ";
George Karpenkov7e3016d2019-01-10 18:13:46 +0000267 } else if (CurrV.getObjKind() == ObjKind::OS) {
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000268 os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType())
George Karpenkov4f64b382019-01-10 18:15:57 +0000269 << "' with a ";
George Karpenkov7e3016d2019-01-10 18:13:46 +0000270 } else if (CurrV.getObjKind() == ObjKind::Generalized) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000271 os << "an object of type '" << Sym->getType().getAsString()
George Karpenkov4f64b382019-01-10 18:15:57 +0000272 << "' with a ";
George Karpenkov62db8862018-11-30 02:18:23 +0000273 } else {
George Karpenkov7e3016d2019-01-10 18:13:46 +0000274 assert(CurrV.getObjKind() == ObjKind::ObjC);
George Karpenkov62db8862018-11-30 02:18:23 +0000275 QualType T = Sym->getType();
276 if (!isa<ObjCObjectPointerType>(T)) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000277 os << "an Objective-C object with a ";
George Karpenkov62db8862018-11-30 02:18:23 +0000278 } else {
279 const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);
George Karpenkov3c1e0662019-01-10 18:28:10 +0000280 os << "an instance of " << PT->getPointeeType().getAsString()
George Karpenkov62db8862018-11-30 02:18:23 +0000281 << " with a ";
282 }
283 }
284
285 if (CurrV.isOwned()) {
286 os << "+1 retain count";
287 } else {
288 assert(CurrV.isNotOwned());
289 os << "+0 retain count";
290 }
George Karpenkov5be959c2019-01-11 23:35:17 +0000291
292 if (Idx) {
293 os << " into an out parameter '";
294 const ParmVarDecl *PVD = (*CE)->parameters()[*Idx];
295 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
296 /*Qualified=*/false);
297 os << "'";
298
299 QualType RT = (*CE)->getResultType();
300 if (!RT.isNull() && !RT->isVoidType()) {
301 SVal RV = (*CE)->getReturnValue();
302 if (CurrSt->isNull(RV).isConstrainedTrue()) {
303 os << " (assuming the call returns zero)";
304 } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) {
305 os << " (assuming the call returns non-zero)";
306 }
307
308 }
309 }
George Karpenkov62db8862018-11-30 02:18:23 +0000310}
311
312namespace clang {
313namespace ento {
314namespace retaincountchecker {
315
George Karpenkov0bb17c42019-01-10 18:16:25 +0000316class RefCountReportVisitor : public BugReporterVisitor {
George Karpenkov62db8862018-11-30 02:18:23 +0000317protected:
318 SymbolRef Sym;
George Karpenkov62db8862018-11-30 02:18:23 +0000319
320public:
George Karpenkov0bb17c42019-01-10 18:16:25 +0000321 RefCountReportVisitor(SymbolRef sym) : Sym(sym) {}
George Karpenkov62db8862018-11-30 02:18:23 +0000322
323 void Profile(llvm::FoldingSetNodeID &ID) const override {
324 static int x = 0;
325 ID.AddPointer(&x);
326 ID.AddPointer(Sym);
327 }
328
329 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
330 BugReporterContext &BRC,
331 BugReport &BR) override;
332
333 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
334 const ExplodedNode *N,
335 BugReport &BR) override;
336};
337
George Karpenkov0bb17c42019-01-10 18:16:25 +0000338class RefLeakReportVisitor : public RefCountReportVisitor {
George Karpenkov62db8862018-11-30 02:18:23 +0000339public:
George Karpenkov0bb17c42019-01-10 18:16:25 +0000340 RefLeakReportVisitor(SymbolRef sym) : RefCountReportVisitor(sym) {}
George Karpenkov62db8862018-11-30 02:18:23 +0000341
342 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
343 const ExplodedNode *N,
344 BugReport &BR) override;
345};
346
347} // end namespace retaincountchecker
348} // end namespace ento
349} // end namespace clang
350
George Karpenkovff014862018-12-11 01:13:40 +0000351
352/// Find the first node with the parent stack frame.
353static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) {
354 const StackFrameContext *SC = Pred->getStackFrame();
355 if (SC->inTopFrame())
356 return nullptr;
357 const StackFrameContext *PC = SC->getParent()->getStackFrame();
358 if (!PC)
359 return nullptr;
360
361 const ExplodedNode *N = Pred;
362 while (N && N->getStackFrame() != PC) {
363 N = N->getFirstPred();
364 }
365 return N;
366}
367
368
369/// Insert a diagnostic piece at function exit
370/// if a function parameter is annotated as "os_consumed",
371/// but it does not actually consume the reference.
372static std::shared_ptr<PathDiagnosticEventPiece>
373annotateConsumedSummaryMismatch(const ExplodedNode *N,
374 CallExitBegin &CallExitLoc,
375 const SourceManager &SM,
376 CallEventManager &CEMgr) {
377
378 const ExplodedNode *CN = getCalleeNode(N);
379 if (!CN)
380 return nullptr;
381
382 CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState());
383
384 std::string sbuf;
385 llvm::raw_string_ostream os(sbuf);
386 ArrayRef<const ParmVarDecl *> Parameters = Call->parameters();
387 for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {
388 const ParmVarDecl *PVD = Parameters[I];
389
390 if (!PVD->hasAttr<OSConsumedAttr>())
George Karpenkovf5085322018-12-21 02:16:23 +0000391 continue;
George Karpenkovff014862018-12-11 01:13:40 +0000392
393 if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
394 const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR);
395 const RefVal *CountAtExit = getRefBinding(N->getState(), SR);
396
397 if (!CountBeforeCall || !CountAtExit)
398 continue;
399
400 unsigned CountBefore = CountBeforeCall->getCount();
401 unsigned CountAfter = CountAtExit->getCount();
402
403 bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
404 if (!AsExpected) {
405 os << "Parameter '";
406 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
407 /*Qualified=*/false);
George Karpenkov79f03402018-12-21 19:13:28 +0000408 os << "' is marked as consuming, but the function did not consume "
George Karpenkovff014862018-12-11 01:13:40 +0000409 << "the reference\n";
410 }
411 }
412 }
413
414 if (os.str().empty())
415 return nullptr;
416
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000417 PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM);
George Karpenkovff014862018-12-11 01:13:40 +0000418 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
419}
420
George Karpenkovd37ff4e2019-01-29 19:29:59 +0000421/// Annotate the parameter at the analysis entry point.
422static std::shared_ptr<PathDiagnosticEventPiece>
423annotateStartParameter(const ExplodedNode *N, SymbolRef Sym,
424 const SourceManager &SM) {
425 auto PP = N->getLocationAs<BlockEdge>();
426 if (!PP)
427 return nullptr;
428
429 const CFGBlock *Src = PP->getSrc();
430 const RefVal *CurrT = getRefBinding(N->getState(), Sym);
431
432 if (&Src->getParent()->getEntry() != Src || !CurrT ||
433 getRefBinding(N->getFirstPred()->getState(), Sym))
434 return nullptr;
435
436 const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion());
437 const auto *PVD = cast<ParmVarDecl>(VR->getDecl());
438 PathDiagnosticLocation L = PathDiagnosticLocation(PVD, SM);
439
440 std::string s;
441 llvm::raw_string_ostream os(s);
George Karpenkov77eae6d2019-01-30 02:11:04 +0000442 os << "Parameter '" << PVD->getNameAsString() << "' starts at +";
George Karpenkovd37ff4e2019-01-29 19:29:59 +0000443 if (CurrT->getCount() == 1) {
444 os << "1, as it is marked as consuming";
445 } else {
446 assert(CurrT->getCount() == 0);
447 os << "0";
448 }
449 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
450}
451
George Karpenkov70c2ee32018-08-17 21:41:07 +0000452std::shared_ptr<PathDiagnosticPiece>
George Karpenkov0bb17c42019-01-10 18:16:25 +0000453RefCountReportVisitor::VisitNode(const ExplodedNode *N,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000454 BugReporterContext &BRC, BugReport &BR) {
George Karpenkov717c4c02019-01-10 18:15:17 +0000455
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000456 const auto &BT = static_cast<const RefCountBug&>(BR.getBugType());
George Karpenkova9e29562019-01-22 19:51:00 +0000457 const auto *Checker =
458 static_cast<const RetainCountChecker *>(BT.getChecker());
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000459
460 bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned ||
461 BT.getBugType() == RefCountBug::DeallocNotOwned;
462
George Karpenkovff014862018-12-11 01:13:40 +0000463 const SourceManager &SM = BRC.getSourceManager();
464 CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
George Karpenkovf5085322018-12-21 02:16:23 +0000465 if (auto CE = N->getLocationAs<CallExitBegin>())
George Karpenkovff014862018-12-11 01:13:40 +0000466 if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
467 return PD;
George Karpenkovff014862018-12-11 01:13:40 +0000468
George Karpenkovd37ff4e2019-01-29 19:29:59 +0000469 if (auto PD = annotateStartParameter(N, Sym, SM))
470 return PD;
471
George Karpenkov70c2ee32018-08-17 21:41:07 +0000472 // FIXME: We will eventually need to handle non-statement-based events
473 // (__attribute__((cleanup))).
474 if (!N->getLocation().getAs<StmtPoint>())
475 return nullptr;
476
477 // Check if the type state has changed.
George Karpenkov62db8862018-11-30 02:18:23 +0000478 const ExplodedNode *PrevNode = N->getFirstPred();
479 ProgramStateRef PrevSt = PrevNode->getState();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000480 ProgramStateRef CurrSt = N->getState();
481 const LocationContext *LCtx = N->getLocationContext();
482
483 const RefVal* CurrT = getRefBinding(CurrSt, Sym);
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000484 if (!CurrT)
485 return nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000486
487 const RefVal &CurrV = *CurrT;
488 const RefVal *PrevT = getRefBinding(PrevSt, Sym);
489
490 // Create a string buffer to constain all the useful things we want
491 // to tell the user.
492 std::string sbuf;
493 llvm::raw_string_ostream os(sbuf);
494
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000495 if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) {
496 os << "Object is now not exclusively owned";
497 auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM);
498 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
499 }
500
George Karpenkov70c2ee32018-08-17 21:41:07 +0000501 // This is the allocation site since the previous node had no bindings
502 // for this symbol.
503 if (!PrevT) {
504 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
505
506 if (isa<ObjCIvarRefExpr>(S) &&
507 isSynthesizedAccessor(LCtx->getStackFrame())) {
508 S = LCtx->getStackFrame()->getCallSite();
509 }
510
511 if (isa<ObjCArrayLiteral>(S)) {
512 os << "NSArray literal is an object with a +0 retain count";
George Karpenkovb3303d72018-11-30 02:17:05 +0000513 } else if (isa<ObjCDictionaryLiteral>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000514 os << "NSDictionary literal is an object with a +0 retain count";
George Karpenkovb3303d72018-11-30 02:17:05 +0000515 } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000516 if (isNumericLiteralExpression(BL->getSubExpr()))
517 os << "NSNumber literal is an object with a +0 retain count";
518 else {
519 const ObjCInterfaceDecl *BoxClass = nullptr;
520 if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
521 BoxClass = Method->getClassInterface();
522
523 // We should always be able to find the boxing class interface,
524 // but consider this future-proofing.
George Karpenkovb3303d72018-11-30 02:17:05 +0000525 if (BoxClass) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000526 os << *BoxClass << " b";
George Karpenkovb3303d72018-11-30 02:17:05 +0000527 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000528 os << "B";
George Karpenkovb3303d72018-11-30 02:17:05 +0000529 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000530
531 os << "oxed expression produces an object with a +0 retain count";
532 }
George Karpenkovb3303d72018-11-30 02:17:05 +0000533 } else if (isa<ObjCIvarRefExpr>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000534 os << "Object loaded from instance variable";
George Karpenkovb3303d72018-11-30 02:17:05 +0000535 } else {
George Karpenkov62db8862018-11-30 02:18:23 +0000536 generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000537 }
538
George Karpenkovff014862018-12-11 01:13:40 +0000539 PathDiagnosticLocation Pos(S, SM, N->getLocationContext());
George Karpenkov70c2ee32018-08-17 21:41:07 +0000540 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
541 }
542
543 // Gather up the effects that were performed on the object at this
544 // program point
George Karpenkov717c4c02019-01-10 18:15:17 +0000545 bool DeallocSent = false;
546
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000547 const ProgramPointTag *Tag = N->getLocation().getTag();
George Karpenkova9e29562019-01-22 19:51:00 +0000548
549 if (Tag == &Checker->getCastFailTag()) {
550 os << "Assuming dynamic cast returns null due to type mismatch";
551 }
552
553 if (Tag == &Checker->getDeallocSentTag()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000554 // We only have summaries attached to nodes after evaluating CallExpr and
555 // ObjCMessageExprs.
556 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
557
558 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
559 // Iterate through the parameter expressions and see if the symbol
560 // was ever passed as an argument.
561 unsigned i = 0;
562
George Karpenkovb3303d72018-11-30 02:17:05 +0000563 for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000564
565 // Retrieve the value of the argument. Is it the symbol
566 // we are interested in?
567 if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)
568 continue;
569
570 // We have an argument. Get the effect!
George Karpenkov717c4c02019-01-10 18:15:17 +0000571 DeallocSent = true;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000572 }
573 } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
574 if (const Expr *receiver = ME->getInstanceReceiver()) {
575 if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
576 .getAsLocSymbol() == Sym) {
577 // The symbol we are tracking is the receiver.
George Karpenkov717c4c02019-01-10 18:15:17 +0000578 DeallocSent = true;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000579 }
580 }
581 }
582 }
583
George Karpenkov717c4c02019-01-10 18:15:17 +0000584 if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent))
George Karpenkovb3303d72018-11-30 02:17:05 +0000585 return nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000586
587 if (os.str().empty())
588 return nullptr; // We have nothing to say!
589
590 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
591 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
592 N->getLocationContext());
593 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
594
595 // Add the range by scanning the children of the statement for any bindings
596 // to Sym.
597 for (const Stmt *Child : S->children())
598 if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))
599 if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {
600 P->addRange(Exp->getSourceRange());
601 break;
602 }
603
604 return std::move(P);
605}
606
607static Optional<std::string> describeRegion(const MemRegion *MR) {
608 if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
609 return std::string(VR->getDecl()->getName());
610 // Once we support more storage locations for bindings,
611 // this would need to be improved.
612 return None;
613}
614
615namespace {
616// Find the first node in the current function context that referred to the
617// tracked symbol and the memory location that value was stored to. Note, the
618// value is only reported if the allocation occurred in the same function as
619// the leak. The function can also return a location context, which should be
620// treated as interesting.
621struct AllocationInfo {
622 const ExplodedNode* N;
623 const MemRegion *R;
624 const LocationContext *InterestingMethodContext;
625 AllocationInfo(const ExplodedNode *InN,
626 const MemRegion *InR,
627 const LocationContext *InInterestingMethodContext) :
628 N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
629};
630} // end anonymous namespace
631
George Karpenkova1c3bb82018-11-30 02:17:31 +0000632static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
633 const ExplodedNode *N, SymbolRef Sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000634 const ExplodedNode *AllocationNode = N;
635 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
636 const MemRegion *FirstBinding = nullptr;
637 const LocationContext *LeakContext = N->getLocationContext();
638
639 // The location context of the init method called on the leaked object, if
640 // available.
641 const LocationContext *InitMethodContext = nullptr;
642
643 while (N) {
644 ProgramStateRef St = N->getState();
645 const LocationContext *NContext = N->getLocationContext();
646
647 if (!getRefBinding(St, Sym))
648 break;
649
650 StoreManager::FindUniqueBinding FB(Sym);
651 StateMgr.iterBindings(St, FB);
652
653 if (FB) {
654 const MemRegion *R = FB.getRegion();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000655 // Do not show local variables belonging to a function other than
656 // where the error is reported.
George Karpenkov79ed11c2018-12-11 01:13:20 +0000657 if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace()))
658 if (MR->getStackFrame() == LeakContext->getStackFrame())
659 FirstBinding = R;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000660 }
661
662 // AllocationNode is the last node in which the symbol was tracked.
663 AllocationNode = N;
664
665 // AllocationNodeInCurrentContext, is the last node in the current or
666 // parent context in which the symbol was tracked.
667 //
Raphael Isemannb23ccec2018-12-10 12:37:46 +0000668 // Note that the allocation site might be in the parent context. For example,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000669 // the case where an allocation happens in a block that captures a reference
670 // to it and that reference is overwritten/dropped by another call to
671 // the block.
672 if (NContext == LeakContext || NContext->isParentOf(LeakContext))
673 AllocationNodeInCurrentOrParentContext = N;
674
675 // Find the last init that was called on the given symbol and store the
676 // init method's location context.
677 if (!InitMethodContext)
George Karpenkova1c3bb82018-11-30 02:17:31 +0000678 if (auto CEP = N->getLocation().getAs<CallEnter>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000679 const Stmt *CE = CEP->getCallExpr();
George Karpenkova1c3bb82018-11-30 02:17:31 +0000680 if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000681 const Stmt *RecExpr = ME->getInstanceReceiver();
682 if (RecExpr) {
683 SVal RecV = St->getSVal(RecExpr, NContext);
684 if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
685 InitMethodContext = CEP->getCalleeContext();
686 }
687 }
688 }
689
George Karpenkova1c3bb82018-11-30 02:17:31 +0000690 N = N->getFirstPred();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000691 }
692
693 // If we are reporting a leak of the object that was allocated with alloc,
694 // mark its init method as interesting.
695 const LocationContext *InterestingMethodContext = nullptr;
696 if (InitMethodContext) {
697 const ProgramPoint AllocPP = AllocationNode->getLocation();
698 if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
699 if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
700 if (ME->getMethodFamily() == OMF_alloc)
701 InterestingMethodContext = InitMethodContext;
702 }
703
704 // If allocation happened in a function different from the leak node context,
705 // do not report the binding.
706 assert(N && "Could not find allocation node");
George Karpenkova1c3bb82018-11-30 02:17:31 +0000707
708 if (AllocationNodeInCurrentOrParentContext &&
709 AllocationNodeInCurrentOrParentContext->getLocationContext() !=
George Karpenkovd37ff4e2019-01-29 19:29:59 +0000710 LeakContext)
George Karpenkov70c2ee32018-08-17 21:41:07 +0000711 FirstBinding = nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000712
713 return AllocationInfo(AllocationNodeInCurrentOrParentContext,
714 FirstBinding,
715 InterestingMethodContext);
716}
717
718std::shared_ptr<PathDiagnosticPiece>
George Karpenkov0bb17c42019-01-10 18:16:25 +0000719RefCountReportVisitor::getEndPath(BugReporterContext &BRC,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000720 const ExplodedNode *EndN, BugReport &BR) {
721 BR.markInteresting(Sym);
722 return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
723}
724
725std::shared_ptr<PathDiagnosticPiece>
George Karpenkov0bb17c42019-01-10 18:16:25 +0000726RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000727 const ExplodedNode *EndN, BugReport &BR) {
728
729 // Tell the BugReporterContext to report cases when the tracked symbol is
730 // assigned to different variables, etc.
731 BR.markInteresting(Sym);
732
733 // We are reporting a leak. Walk up the graph to get to the first node where
734 // the symbol appeared, and also get the first VarDecl that tracked object
735 // is stored to.
George Karpenkova1c3bb82018-11-30 02:17:31 +0000736 AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000737
738 const MemRegion* FirstBinding = AllocI.R;
739 BR.markInteresting(AllocI.InterestingMethodContext);
740
741 SourceManager& SM = BRC.getSourceManager();
742
743 // Compute an actual location for the leak. Sometimes a leak doesn't
744 // occur at an actual statement (e.g., transition between blocks; end
745 // of function) so we need to walk the graph and compute a real location.
746 const ExplodedNode *LeakN = EndN;
747 PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM);
748
749 std::string sbuf;
750 llvm::raw_string_ostream os(sbuf);
751
752 os << "Object leaked: ";
753
754 Optional<std::string> RegionDescription = describeRegion(FirstBinding);
755 if (RegionDescription) {
756 os << "object allocated and stored into '" << *RegionDescription << '\'';
George Karpenkovb3303d72018-11-30 02:17:05 +0000757 } else {
George Karpenkov4f64b382019-01-10 18:15:57 +0000758 os << "allocated object of type '" << getPrettyTypeName(Sym->getType())
759 << "'";
George Karpenkovb3303d72018-11-30 02:17:05 +0000760 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000761
762 // Get the retain count.
763 const RefVal* RV = getRefBinding(EndN->getState(), Sym);
764 assert(RV);
765
766 if (RV->getKind() == RefVal::ErrorLeakReturned) {
767 // FIXME: Per comments in rdar://6320065, "create" only applies to CF
768 // objects. Only "copy", "alloc", "retain" and "new" transfer ownership
769 // to the caller for NS objects.
770 const Decl *D = &EndN->getCodeDecl();
771
772 os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
773 : " is returned from a function ");
774
George Karpenkova1c3bb82018-11-30 02:17:31 +0000775 if (D->hasAttr<CFReturnsNotRetainedAttr>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000776 os << "that is annotated as CF_RETURNS_NOT_RETAINED";
George Karpenkova1c3bb82018-11-30 02:17:31 +0000777 } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000778 os << "that is annotated as NS_RETURNS_NOT_RETAINED";
George Karpenkovb43772d2018-11-30 02:18:50 +0000779 } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {
780 os << "that is annotated as OS_RETURNS_NOT_RETAINED";
George Karpenkova1c3bb82018-11-30 02:17:31 +0000781 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000782 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
783 if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) {
784 os << "managed by Automatic Reference Counting";
785 } else {
786 os << "whose name ('" << MD->getSelector().getAsString()
787 << "') does not start with "
788 "'copy', 'mutableCopy', 'alloc' or 'new'."
789 " This violates the naming convention rules"
790 " given in the Memory Management Guide for Cocoa";
791 }
George Karpenkov6e9fd132018-08-22 01:17:09 +0000792 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000793 const FunctionDecl *FD = cast<FunctionDecl>(D);
George Karpenkovd37ff4e2019-01-29 19:29:59 +0000794 ObjKind K = RV->getObjKind();
795 if (K == ObjKind::ObjC || K == ObjKind::CF) {
796 os << "whose name ('" << *FD
797 << "') does not contain 'Copy' or 'Create'. This violates the "
798 "naming"
799 " convention rules given in the Memory Management Guide for "
800 "Core"
801 " Foundation";
802 } else if (RV->getObjKind() == ObjKind::OS) {
803 std::string FuncName = FD->getNameAsString();
804 os << "whose name ('" << FuncName
805 << "') starts with '" << StringRef(FuncName).substr(0, 3) << "'";
806 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000807 }
808 }
George Karpenkovf893ea12018-11-30 02:17:44 +0000809 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000810 os << " is not referenced later in this execution path and has a retain "
811 "count of +" << RV->getCount();
George Karpenkovf893ea12018-11-30 02:17:44 +0000812 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000813
814 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
815}
816
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000817RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
George Karpenkov0bb17c42019-01-10 18:16:25 +0000818 ExplodedNode *n, SymbolRef sym,
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000819 bool isLeak)
820 : BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) {
821 if (!isLeak)
George Karpenkov0bb17c42019-01-10 18:16:25 +0000822 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
George Karpenkov62db8862018-11-30 02:18:23 +0000823}
824
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000825RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
George Karpenkov0bb17c42019-01-10 18:16:25 +0000826 ExplodedNode *n, SymbolRef sym,
827 StringRef endText)
George Karpenkov62db8862018-11-30 02:18:23 +0000828 : BugReport(D, D.getDescription(), endText, n) {
829
George Karpenkov0bb17c42019-01-10 18:16:25 +0000830 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
George Karpenkov62db8862018-11-30 02:18:23 +0000831}
832
George Karpenkov0bb17c42019-01-10 18:16:25 +0000833void RefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000834 const SourceManager& SMgr = Ctx.getSourceManager();
835
836 if (!sym->getOriginRegion())
837 return;
838
839 auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion());
840 if (Region) {
841 const Decl *PDecl = Region->getDecl();
842 if (PDecl && isa<ParmVarDecl>(PDecl)) {
George Karpenkov717c4c02019-01-10 18:15:17 +0000843 PathDiagnosticLocation ParamLocation =
844 PathDiagnosticLocation::create(PDecl, SMgr);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000845 Location = ParamLocation;
846 UniqueingLocation = ParamLocation;
847 UniqueingDecl = Ctx.getLocationContext()->getDecl();
848 }
849 }
850}
851
George Karpenkov0bb17c42019-01-10 18:16:25 +0000852void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx,
George Karpenkova1c3bb82018-11-30 02:17:31 +0000853 SymbolRef sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000854 // Most bug reports are cached at the location where they occurred.
855 // With leaks, we want to unique them by the location where they were
856 // allocated, and only report a single path. To do this, we need to find
857 // the allocation site of a piece of tracked memory, which we do via a
858 // call to GetAllocationSite. This will walk the ExplodedGraph backwards.
859 // Note that this is *not* the trimmed graph; we are guaranteed, however,
860 // that all ancestor nodes that represent the allocation site have the
861 // same SourceLocation.
862 const ExplodedNode *AllocNode = nullptr;
863
864 const SourceManager& SMgr = Ctx.getSourceManager();
865
866 AllocationInfo AllocI =
George Karpenkova1c3bb82018-11-30 02:17:31 +0000867 GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000868
869 AllocNode = AllocI.N;
870 AllocBinding = AllocI.R;
871 markInteresting(AllocI.InterestingMethodContext);
872
873 // Get the SourceLocation for the allocation site.
874 // FIXME: This will crash the analyzer if an allocation comes from an
875 // implicit call (ex: a destructor call).
876 // (Currently there are no such allocations in Cocoa, though.)
877 AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
878
879 if (!AllocStmt) {
880 AllocBinding = nullptr;
881 return;
882 }
883
884 PathDiagnosticLocation AllocLocation =
885 PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
886 AllocNode->getLocationContext());
887 Location = AllocLocation;
888
889 // Set uniqieing info, which will be used for unique the bug reports. The
890 // leaks should be uniqued on the allocation site.
891 UniqueingLocation = AllocLocation;
892 UniqueingDecl = AllocNode->getLocationContext()->getDecl();
893}
894
George Karpenkov0bb17c42019-01-10 18:16:25 +0000895void RefLeakReport::createDescription(CheckerContext &Ctx) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000896 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
897 Description.clear();
898 llvm::raw_string_ostream os(Description);
899 os << "Potential leak of an object";
900
901 Optional<std::string> RegionDescription = describeRegion(AllocBinding);
902 if (RegionDescription) {
903 os << " stored into '" << *RegionDescription << '\'';
George Karpenkovf893ea12018-11-30 02:17:44 +0000904 } else {
905
906 // If we can't figure out the name, just supply the type information.
George Karpenkov4f64b382019-01-10 18:15:57 +0000907 os << " of type '" << getPrettyTypeName(Sym->getType()) << "'";
George Karpenkov70c2ee32018-08-17 21:41:07 +0000908 }
909}
910
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000911RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts,
George Karpenkov0bb17c42019-01-10 18:16:25 +0000912 ExplodedNode *n, SymbolRef sym,
913 CheckerContext &Ctx)
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000914 : RefCountReport(D, LOpts, n, sym, /*isLeak=*/true) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000915
916 deriveAllocLocation(Ctx, sym);
917 if (!AllocBinding)
918 deriveParamLocation(Ctx, sym);
919
George Karpenkov936a9c92018-12-07 20:21:37 +0000920 createDescription(Ctx);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000921
George Karpenkov0bb17c42019-01-10 18:16:25 +0000922 addVisitor(llvm::make_unique<RefLeakReportVisitor>(sym));
George Karpenkov70c2ee32018-08-17 21:41:07 +0000923}