blob: b7a866d3ff857a1f265e7b53929a2383232d70e1 [file] [log] [blame]
George Karpenkov70c2ee32018-08-17 21:41:07 +00001// 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
18using namespace clang;
19using namespace ento;
20using namespace retaincountchecker;
21
George Karpenkov2c2d0b62019-01-18 19:24:55 +000022StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) {
23 switch (BT) {
24 case UseAfterRelease:
25 return "Use-after-release";
26 case ReleaseNotOwned:
27 return "Bad release";
28 case DeallocNotOwned:
29 return "-dealloc sent to non-exclusively owned object";
30 case FreeNotOwned:
31 return "freeing non-exclusively owned object";
32 case OverAutorelease:
33 return "Object autoreleased too many times";
34 case ReturnNotOwnedForOwned:
35 return "Method should return an owned object";
36 case LeakWithinFunction:
37 return "Leak";
38 case LeakAtReturn:
39 return "Leak of returned object";
40 }
41}
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 }
63}
64
65RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT)
66 : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount,
67 /*SupressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn),
68 BT(BT) {}
69
George Karpenkov70c2ee32018-08-17 21:41:07 +000070static bool isNumericLiteralExpression(const Expr *E) {
71 // FIXME: This set of cases was copied from SemaExprObjC.
72 return isa<IntegerLiteral>(E) ||
73 isa<CharacterLiteral>(E) ||
74 isa<FloatingLiteral>(E) ||
75 isa<ObjCBoolLiteralExpr>(E) ||
76 isa<CXXBoolLiteralExpr>(E);
77}
78
George Karpenkovf893ea12018-11-30 02:17:44 +000079/// If type represents a pointer to CXXRecordDecl,
80/// and is not a typedef, return the decl name.
81/// Otherwise, return the serialization of type.
Haojian Wuceff7302018-11-30 09:23:01 +000082static std::string getPrettyTypeName(QualType QT) {
George Karpenkovf893ea12018-11-30 02:17:44 +000083 QualType PT = QT->getPointeeType();
84 if (!PT.isNull() && !QT->getAs<TypedefType>())
85 if (const auto *RD = PT->getAsCXXRecordDecl())
86 return RD->getName();
87 return QT.getAsString();
88}
89
George Karpenkovb3303d72018-11-30 02:17:05 +000090/// Write information about the type state change to {@code os},
91/// return whether the note should be generated.
92static bool shouldGenerateNote(llvm::raw_string_ostream &os,
George Karpenkov2c2d0b62019-01-18 19:24:55 +000093 const RefVal *PrevT,
94 const RefVal &CurrV,
George Karpenkov717c4c02019-01-10 18:15:17 +000095 bool DeallocSent) {
George Karpenkovb3303d72018-11-30 02:17:05 +000096 // Get the previous type state.
97 RefVal PrevV = *PrevT;
98
99 // Specially handle -dealloc.
George Karpenkov717c4c02019-01-10 18:15:17 +0000100 if (DeallocSent) {
George Karpenkovb3303d72018-11-30 02:17:05 +0000101 // Determine if the object's reference count was pushed to zero.
102 assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");
103 // We may not have transitioned to 'release' if we hit an error.
104 // This case is handled elsewhere.
105 if (CurrV.getKind() == RefVal::Released) {
106 assert(CurrV.getCombinedCounts() == 0);
107 os << "Object released by directly sending the '-dealloc' message";
108 return true;
109 }
110 }
111
112 // Determine if the typestate has changed.
113 if (!PrevV.hasSameState(CurrV))
114 switch (CurrV.getKind()) {
115 case RefVal::Owned:
116 case RefVal::NotOwned:
117 if (PrevV.getCount() == CurrV.getCount()) {
118 // Did an autorelease message get sent?
119 if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())
120 return false;
121
122 assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
123 os << "Object autoreleased";
124 return true;
125 }
126
127 if (PrevV.getCount() > CurrV.getCount())
128 os << "Reference count decremented.";
129 else
130 os << "Reference count incremented.";
131
132 if (unsigned Count = CurrV.getCount())
133 os << " The object now has a +" << Count << " retain count.";
134
135 return true;
136
137 case RefVal::Released:
138 if (CurrV.getIvarAccessHistory() ==
139 RefVal::IvarAccessHistory::ReleasedAfterDirectAccess &&
140 CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) {
141 os << "Strong instance variable relinquished. ";
142 }
143 os << "Object released.";
144 return true;
145
146 case RefVal::ReturnedOwned:
147 // Autoreleases can be applied after marking a node ReturnedOwned.
148 if (CurrV.getAutoreleaseCount())
149 return false;
150
151 os << "Object returned to caller as an owning reference (single "
152 "retain count transferred to caller)";
153 return true;
154
155 case RefVal::ReturnedNotOwned:
156 os << "Object returned to caller with a +0 retain count";
157 return true;
158
159 default:
160 return false;
161 }
162 return true;
163}
164
George Karpenkov5be959c2019-01-11 23:35:17 +0000165/// Finds argument index of the out paramter in the call {@code S}
166/// corresponding to the symbol {@code Sym}.
167/// If none found, returns None.
168static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
169 const LocationContext *LCtx,
170 SymbolRef &Sym,
171 Optional<CallEventRef<>> CE) {
172 if (!CE)
173 return None;
174
175 for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++)
176 if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())
177 if (const auto *TR = dyn_cast<TypedValueRegion>(MR))
178 if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymExpr() == Sym)
179 return Idx;
180
181 return None;
182}
183
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000184Optional<std::string> findMetaClassAlloc(const Expr *Callee) {
185 if (const auto *ME = dyn_cast<MemberExpr>(Callee)) {
186 if (ME->getMemberDecl()->getNameAsString() != "alloc")
187 return None;
188 const Expr *This = ME->getBase()->IgnoreParenImpCasts();
189 if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) {
190 const ValueDecl *VD = DRE->getDecl();
191 if (VD->getNameAsString() != "metaClass")
192 return None;
193
194 if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext()))
195 return RD->getNameAsString();
196
197 }
198 }
199 return None;
200}
201
202std::string findAllocatedObjectName(const Stmt *S,
203 QualType QT) {
204 if (const auto *CE = dyn_cast<CallExpr>(S))
205 if (auto Out = findMetaClassAlloc(CE->getCallee()))
206 return *Out;
207 return getPrettyTypeName(QT);
208}
209
George Karpenkovff014862018-12-11 01:13:40 +0000210static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
211 const LocationContext *LCtx,
212 const RefVal &CurrV, SymbolRef &Sym,
213 const Stmt *S,
214 llvm::raw_string_ostream &os) {
George Karpenkov5be959c2019-01-11 23:35:17 +0000215 CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
George Karpenkov62db8862018-11-30 02:18:23 +0000216 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
217 // Get the name of the callee (if it is available)
218 // from the tracked SVal.
219 SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
220 const FunctionDecl *FD = X.getAsFunctionDecl();
221
222 // If failed, try to get it from AST.
223 if (!FD)
224 FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
225
226 if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) {
227 os << "Call to method '" << MD->getQualifiedNameAsString() << '\'';
228 } else if (FD) {
229 os << "Call to function '" << FD->getQualifiedNameAsString() << '\'';
230 } else {
231 os << "function call";
232 }
George Karpenkovff014862018-12-11 01:13:40 +0000233 } else if (isa<CXXNewExpr>(S)) {
234 os << "Operator 'new'";
George Karpenkov62db8862018-11-30 02:18:23 +0000235 } else {
236 assert(isa<ObjCMessageExpr>(S));
George Karpenkov62db8862018-11-30 02:18:23 +0000237 CallEventRef<ObjCMethodCall> Call =
238 Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
239
240 switch (Call->getMessageKind()) {
241 case OCM_Message:
242 os << "Method";
243 break;
244 case OCM_PropertyAccess:
245 os << "Property";
246 break;
247 case OCM_Subscript:
248 os << "Subscript";
249 break;
250 }
251 }
252
George Karpenkov5be959c2019-01-11 23:35:17 +0000253 Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx);
254 auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE);
255
256 // If index is not found, we assume that the symbol was returned.
257 if (!Idx) {
258 os << " returns ";
259 } else {
260 os << " writes ";
261 }
George Karpenkov3c1e0662019-01-10 18:28:10 +0000262
George Karpenkov7e3016d2019-01-10 18:13:46 +0000263 if (CurrV.getObjKind() == ObjKind::CF) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000264 os << "a Core Foundation object of type '"
George Karpenkov4f64b382019-01-10 18:15:57 +0000265 << Sym->getType().getAsString() << "' with a ";
George Karpenkov7e3016d2019-01-10 18:13:46 +0000266 } else if (CurrV.getObjKind() == ObjKind::OS) {
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000267 os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType())
George Karpenkov4f64b382019-01-10 18:15:57 +0000268 << "' with a ";
George Karpenkov7e3016d2019-01-10 18:13:46 +0000269 } else if (CurrV.getObjKind() == ObjKind::Generalized) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000270 os << "an object of type '" << Sym->getType().getAsString()
George Karpenkov4f64b382019-01-10 18:15:57 +0000271 << "' with a ";
George Karpenkov62db8862018-11-30 02:18:23 +0000272 } else {
George Karpenkov7e3016d2019-01-10 18:13:46 +0000273 assert(CurrV.getObjKind() == ObjKind::ObjC);
George Karpenkov62db8862018-11-30 02:18:23 +0000274 QualType T = Sym->getType();
275 if (!isa<ObjCObjectPointerType>(T)) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000276 os << "an Objective-C object with a ";
George Karpenkov62db8862018-11-30 02:18:23 +0000277 } else {
278 const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);
George Karpenkov3c1e0662019-01-10 18:28:10 +0000279 os << "an instance of " << PT->getPointeeType().getAsString()
George Karpenkov62db8862018-11-30 02:18:23 +0000280 << " with a ";
281 }
282 }
283
284 if (CurrV.isOwned()) {
285 os << "+1 retain count";
286 } else {
287 assert(CurrV.isNotOwned());
288 os << "+0 retain count";
289 }
George Karpenkov5be959c2019-01-11 23:35:17 +0000290
291 if (Idx) {
292 os << " into an out parameter '";
293 const ParmVarDecl *PVD = (*CE)->parameters()[*Idx];
294 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
295 /*Qualified=*/false);
296 os << "'";
297
298 QualType RT = (*CE)->getResultType();
299 if (!RT.isNull() && !RT->isVoidType()) {
300 SVal RV = (*CE)->getReturnValue();
301 if (CurrSt->isNull(RV).isConstrainedTrue()) {
302 os << " (assuming the call returns zero)";
303 } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) {
304 os << " (assuming the call returns non-zero)";
305 }
306
307 }
308 }
George Karpenkov62db8862018-11-30 02:18:23 +0000309}
310
311namespace clang {
312namespace ento {
313namespace retaincountchecker {
314
George Karpenkov0bb17c42019-01-10 18:16:25 +0000315class RefCountReportVisitor : public BugReporterVisitor {
George Karpenkov62db8862018-11-30 02:18:23 +0000316protected:
317 SymbolRef Sym;
George Karpenkov62db8862018-11-30 02:18:23 +0000318
319public:
George Karpenkov0bb17c42019-01-10 18:16:25 +0000320 RefCountReportVisitor(SymbolRef sym) : Sym(sym) {}
George Karpenkov62db8862018-11-30 02:18:23 +0000321
322 void Profile(llvm::FoldingSetNodeID &ID) const override {
323 static int x = 0;
324 ID.AddPointer(&x);
325 ID.AddPointer(Sym);
326 }
327
328 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
329 BugReporterContext &BRC,
330 BugReport &BR) override;
331
332 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
333 const ExplodedNode *N,
334 BugReport &BR) override;
335};
336
George Karpenkov0bb17c42019-01-10 18:16:25 +0000337class RefLeakReportVisitor : public RefCountReportVisitor {
George Karpenkov62db8862018-11-30 02:18:23 +0000338public:
George Karpenkov0bb17c42019-01-10 18:16:25 +0000339 RefLeakReportVisitor(SymbolRef sym) : RefCountReportVisitor(sym) {}
George Karpenkov62db8862018-11-30 02:18:23 +0000340
341 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
342 const ExplodedNode *N,
343 BugReport &BR) override;
344};
345
346} // end namespace retaincountchecker
347} // end namespace ento
348} // end namespace clang
349
George Karpenkovff014862018-12-11 01:13:40 +0000350
351/// Find the first node with the parent stack frame.
352static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) {
353 const StackFrameContext *SC = Pred->getStackFrame();
354 if (SC->inTopFrame())
355 return nullptr;
356 const StackFrameContext *PC = SC->getParent()->getStackFrame();
357 if (!PC)
358 return nullptr;
359
360 const ExplodedNode *N = Pred;
361 while (N && N->getStackFrame() != PC) {
362 N = N->getFirstPred();
363 }
364 return N;
365}
366
367
368/// Insert a diagnostic piece at function exit
369/// if a function parameter is annotated as "os_consumed",
370/// but it does not actually consume the reference.
371static std::shared_ptr<PathDiagnosticEventPiece>
372annotateConsumedSummaryMismatch(const ExplodedNode *N,
373 CallExitBegin &CallExitLoc,
374 const SourceManager &SM,
375 CallEventManager &CEMgr) {
376
377 const ExplodedNode *CN = getCalleeNode(N);
378 if (!CN)
379 return nullptr;
380
381 CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState());
382
383 std::string sbuf;
384 llvm::raw_string_ostream os(sbuf);
385 ArrayRef<const ParmVarDecl *> Parameters = Call->parameters();
386 for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {
387 const ParmVarDecl *PVD = Parameters[I];
388
389 if (!PVD->hasAttr<OSConsumedAttr>())
George Karpenkovf5085322018-12-21 02:16:23 +0000390 continue;
George Karpenkovff014862018-12-11 01:13:40 +0000391
392 if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
393 const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR);
394 const RefVal *CountAtExit = getRefBinding(N->getState(), SR);
395
396 if (!CountBeforeCall || !CountAtExit)
397 continue;
398
399 unsigned CountBefore = CountBeforeCall->getCount();
400 unsigned CountAfter = CountAtExit->getCount();
401
402 bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
403 if (!AsExpected) {
404 os << "Parameter '";
405 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
406 /*Qualified=*/false);
George Karpenkov79f03402018-12-21 19:13:28 +0000407 os << "' is marked as consuming, but the function did not consume "
George Karpenkovff014862018-12-11 01:13:40 +0000408 << "the reference\n";
409 }
410 }
411 }
412
413 if (os.str().empty())
414 return nullptr;
415
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000416 PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM);
George Karpenkovff014862018-12-11 01:13:40 +0000417 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
418}
419
George Karpenkov70c2ee32018-08-17 21:41:07 +0000420std::shared_ptr<PathDiagnosticPiece>
George Karpenkov0bb17c42019-01-10 18:16:25 +0000421RefCountReportVisitor::VisitNode(const ExplodedNode *N,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000422 BugReporterContext &BRC, BugReport &BR) {
George Karpenkov717c4c02019-01-10 18:15:17 +0000423
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000424 const auto &BT = static_cast<const RefCountBug&>(BR.getBugType());
425
426 bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned ||
427 BT.getBugType() == RefCountBug::DeallocNotOwned;
428
George Karpenkovff014862018-12-11 01:13:40 +0000429 const SourceManager &SM = BRC.getSourceManager();
430 CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
George Karpenkovf5085322018-12-21 02:16:23 +0000431 if (auto CE = N->getLocationAs<CallExitBegin>())
George Karpenkovff014862018-12-11 01:13:40 +0000432 if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
433 return PD;
George Karpenkovff014862018-12-11 01:13:40 +0000434
George Karpenkov70c2ee32018-08-17 21:41:07 +0000435 // FIXME: We will eventually need to handle non-statement-based events
436 // (__attribute__((cleanup))).
437 if (!N->getLocation().getAs<StmtPoint>())
438 return nullptr;
439
440 // Check if the type state has changed.
George Karpenkov62db8862018-11-30 02:18:23 +0000441 const ExplodedNode *PrevNode = N->getFirstPred();
442 ProgramStateRef PrevSt = PrevNode->getState();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000443 ProgramStateRef CurrSt = N->getState();
444 const LocationContext *LCtx = N->getLocationContext();
445
446 const RefVal* CurrT = getRefBinding(CurrSt, Sym);
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000447 if (!CurrT)
448 return nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000449
450 const RefVal &CurrV = *CurrT;
451 const RefVal *PrevT = getRefBinding(PrevSt, Sym);
452
453 // Create a string buffer to constain all the useful things we want
454 // to tell the user.
455 std::string sbuf;
456 llvm::raw_string_ostream os(sbuf);
457
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000458 if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) {
459 os << "Object is now not exclusively owned";
460 auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM);
461 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
462 }
463
George Karpenkov70c2ee32018-08-17 21:41:07 +0000464 // This is the allocation site since the previous node had no bindings
465 // for this symbol.
466 if (!PrevT) {
467 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
468
469 if (isa<ObjCIvarRefExpr>(S) &&
470 isSynthesizedAccessor(LCtx->getStackFrame())) {
471 S = LCtx->getStackFrame()->getCallSite();
472 }
473
474 if (isa<ObjCArrayLiteral>(S)) {
475 os << "NSArray literal is an object with a +0 retain count";
George Karpenkovb3303d72018-11-30 02:17:05 +0000476 } else if (isa<ObjCDictionaryLiteral>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000477 os << "NSDictionary literal is an object with a +0 retain count";
George Karpenkovb3303d72018-11-30 02:17:05 +0000478 } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000479 if (isNumericLiteralExpression(BL->getSubExpr()))
480 os << "NSNumber literal is an object with a +0 retain count";
481 else {
482 const ObjCInterfaceDecl *BoxClass = nullptr;
483 if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
484 BoxClass = Method->getClassInterface();
485
486 // We should always be able to find the boxing class interface,
487 // but consider this future-proofing.
George Karpenkovb3303d72018-11-30 02:17:05 +0000488 if (BoxClass) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000489 os << *BoxClass << " b";
George Karpenkovb3303d72018-11-30 02:17:05 +0000490 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000491 os << "B";
George Karpenkovb3303d72018-11-30 02:17:05 +0000492 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000493
494 os << "oxed expression produces an object with a +0 retain count";
495 }
George Karpenkovb3303d72018-11-30 02:17:05 +0000496 } else if (isa<ObjCIvarRefExpr>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000497 os << "Object loaded from instance variable";
George Karpenkovb3303d72018-11-30 02:17:05 +0000498 } else {
George Karpenkov62db8862018-11-30 02:18:23 +0000499 generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000500 }
501
George Karpenkovff014862018-12-11 01:13:40 +0000502 PathDiagnosticLocation Pos(S, SM, N->getLocationContext());
George Karpenkov70c2ee32018-08-17 21:41:07 +0000503 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
504 }
505
506 // Gather up the effects that were performed on the object at this
507 // program point
George Karpenkov717c4c02019-01-10 18:15:17 +0000508 bool DeallocSent = false;
509
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000510 const ProgramPointTag *Tag = N->getLocation().getTag();
511 if (Tag && Tag->getTagDescription().contains(
512 RetainCountChecker::DeallocTagDescription)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000513 // We only have summaries attached to nodes after evaluating CallExpr and
514 // ObjCMessageExprs.
515 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
516
517 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
518 // Iterate through the parameter expressions and see if the symbol
519 // was ever passed as an argument.
520 unsigned i = 0;
521
George Karpenkovb3303d72018-11-30 02:17:05 +0000522 for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000523
524 // Retrieve the value of the argument. Is it the symbol
525 // we are interested in?
526 if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)
527 continue;
528
529 // We have an argument. Get the effect!
George Karpenkov717c4c02019-01-10 18:15:17 +0000530 DeallocSent = true;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000531 }
532 } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
533 if (const Expr *receiver = ME->getInstanceReceiver()) {
534 if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
535 .getAsLocSymbol() == Sym) {
536 // The symbol we are tracking is the receiver.
George Karpenkov717c4c02019-01-10 18:15:17 +0000537 DeallocSent = true;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000538 }
539 }
540 }
541 }
542
George Karpenkov717c4c02019-01-10 18:15:17 +0000543 if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent))
George Karpenkovb3303d72018-11-30 02:17:05 +0000544 return nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000545
546 if (os.str().empty())
547 return nullptr; // We have nothing to say!
548
549 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
550 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
551 N->getLocationContext());
552 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
553
554 // Add the range by scanning the children of the statement for any bindings
555 // to Sym.
556 for (const Stmt *Child : S->children())
557 if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))
558 if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {
559 P->addRange(Exp->getSourceRange());
560 break;
561 }
562
563 return std::move(P);
564}
565
566static Optional<std::string> describeRegion(const MemRegion *MR) {
567 if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
568 return std::string(VR->getDecl()->getName());
569 // Once we support more storage locations for bindings,
570 // this would need to be improved.
571 return None;
572}
573
574namespace {
575// Find the first node in the current function context that referred to the
576// tracked symbol and the memory location that value was stored to. Note, the
577// value is only reported if the allocation occurred in the same function as
578// the leak. The function can also return a location context, which should be
579// treated as interesting.
580struct AllocationInfo {
581 const ExplodedNode* N;
582 const MemRegion *R;
583 const LocationContext *InterestingMethodContext;
584 AllocationInfo(const ExplodedNode *InN,
585 const MemRegion *InR,
586 const LocationContext *InInterestingMethodContext) :
587 N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
588};
589} // end anonymous namespace
590
George Karpenkova1c3bb82018-11-30 02:17:31 +0000591static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
592 const ExplodedNode *N, SymbolRef Sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000593 const ExplodedNode *AllocationNode = N;
594 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
595 const MemRegion *FirstBinding = nullptr;
596 const LocationContext *LeakContext = N->getLocationContext();
597
598 // The location context of the init method called on the leaked object, if
599 // available.
600 const LocationContext *InitMethodContext = nullptr;
601
602 while (N) {
603 ProgramStateRef St = N->getState();
604 const LocationContext *NContext = N->getLocationContext();
605
606 if (!getRefBinding(St, Sym))
607 break;
608
609 StoreManager::FindUniqueBinding FB(Sym);
610 StateMgr.iterBindings(St, FB);
611
612 if (FB) {
613 const MemRegion *R = FB.getRegion();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000614 // Do not show local variables belonging to a function other than
615 // where the error is reported.
George Karpenkov79ed11c2018-12-11 01:13:20 +0000616 if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace()))
617 if (MR->getStackFrame() == LeakContext->getStackFrame())
618 FirstBinding = R;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000619 }
620
621 // AllocationNode is the last node in which the symbol was tracked.
622 AllocationNode = N;
623
624 // AllocationNodeInCurrentContext, is the last node in the current or
625 // parent context in which the symbol was tracked.
626 //
Raphael Isemannb23ccec2018-12-10 12:37:46 +0000627 // Note that the allocation site might be in the parent context. For example,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000628 // the case where an allocation happens in a block that captures a reference
629 // to it and that reference is overwritten/dropped by another call to
630 // the block.
631 if (NContext == LeakContext || NContext->isParentOf(LeakContext))
632 AllocationNodeInCurrentOrParentContext = N;
633
634 // Find the last init that was called on the given symbol and store the
635 // init method's location context.
636 if (!InitMethodContext)
George Karpenkova1c3bb82018-11-30 02:17:31 +0000637 if (auto CEP = N->getLocation().getAs<CallEnter>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000638 const Stmt *CE = CEP->getCallExpr();
George Karpenkova1c3bb82018-11-30 02:17:31 +0000639 if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000640 const Stmt *RecExpr = ME->getInstanceReceiver();
641 if (RecExpr) {
642 SVal RecV = St->getSVal(RecExpr, NContext);
643 if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
644 InitMethodContext = CEP->getCalleeContext();
645 }
646 }
647 }
648
George Karpenkova1c3bb82018-11-30 02:17:31 +0000649 N = N->getFirstPred();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000650 }
651
652 // If we are reporting a leak of the object that was allocated with alloc,
653 // mark its init method as interesting.
654 const LocationContext *InterestingMethodContext = nullptr;
655 if (InitMethodContext) {
656 const ProgramPoint AllocPP = AllocationNode->getLocation();
657 if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
658 if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
659 if (ME->getMethodFamily() == OMF_alloc)
660 InterestingMethodContext = InitMethodContext;
661 }
662
663 // If allocation happened in a function different from the leak node context,
664 // do not report the binding.
665 assert(N && "Could not find allocation node");
George Karpenkova1c3bb82018-11-30 02:17:31 +0000666
667 if (AllocationNodeInCurrentOrParentContext &&
668 AllocationNodeInCurrentOrParentContext->getLocationContext() !=
669 LeakContext)
George Karpenkov70c2ee32018-08-17 21:41:07 +0000670 FirstBinding = nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000671
672 return AllocationInfo(AllocationNodeInCurrentOrParentContext,
673 FirstBinding,
674 InterestingMethodContext);
675}
676
677std::shared_ptr<PathDiagnosticPiece>
George Karpenkov0bb17c42019-01-10 18:16:25 +0000678RefCountReportVisitor::getEndPath(BugReporterContext &BRC,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000679 const ExplodedNode *EndN, BugReport &BR) {
680 BR.markInteresting(Sym);
681 return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
682}
683
684std::shared_ptr<PathDiagnosticPiece>
George Karpenkov0bb17c42019-01-10 18:16:25 +0000685RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000686 const ExplodedNode *EndN, BugReport &BR) {
687
688 // Tell the BugReporterContext to report cases when the tracked symbol is
689 // assigned to different variables, etc.
690 BR.markInteresting(Sym);
691
692 // We are reporting a leak. Walk up the graph to get to the first node where
693 // the symbol appeared, and also get the first VarDecl that tracked object
694 // is stored to.
George Karpenkova1c3bb82018-11-30 02:17:31 +0000695 AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000696
697 const MemRegion* FirstBinding = AllocI.R;
698 BR.markInteresting(AllocI.InterestingMethodContext);
699
700 SourceManager& SM = BRC.getSourceManager();
701
702 // Compute an actual location for the leak. Sometimes a leak doesn't
703 // occur at an actual statement (e.g., transition between blocks; end
704 // of function) so we need to walk the graph and compute a real location.
705 const ExplodedNode *LeakN = EndN;
706 PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM);
707
708 std::string sbuf;
709 llvm::raw_string_ostream os(sbuf);
710
711 os << "Object leaked: ";
712
713 Optional<std::string> RegionDescription = describeRegion(FirstBinding);
714 if (RegionDescription) {
715 os << "object allocated and stored into '" << *RegionDescription << '\'';
George Karpenkovb3303d72018-11-30 02:17:05 +0000716 } else {
George Karpenkov4f64b382019-01-10 18:15:57 +0000717 os << "allocated object of type '" << getPrettyTypeName(Sym->getType())
718 << "'";
George Karpenkovb3303d72018-11-30 02:17:05 +0000719 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000720
721 // Get the retain count.
722 const RefVal* RV = getRefBinding(EndN->getState(), Sym);
723 assert(RV);
724
725 if (RV->getKind() == RefVal::ErrorLeakReturned) {
726 // FIXME: Per comments in rdar://6320065, "create" only applies to CF
727 // objects. Only "copy", "alloc", "retain" and "new" transfer ownership
728 // to the caller for NS objects.
729 const Decl *D = &EndN->getCodeDecl();
730
731 os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
732 : " is returned from a function ");
733
George Karpenkova1c3bb82018-11-30 02:17:31 +0000734 if (D->hasAttr<CFReturnsNotRetainedAttr>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000735 os << "that is annotated as CF_RETURNS_NOT_RETAINED";
George Karpenkova1c3bb82018-11-30 02:17:31 +0000736 } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000737 os << "that is annotated as NS_RETURNS_NOT_RETAINED";
George Karpenkovb43772d2018-11-30 02:18:50 +0000738 } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {
739 os << "that is annotated as OS_RETURNS_NOT_RETAINED";
George Karpenkova1c3bb82018-11-30 02:17:31 +0000740 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000741 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
742 if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) {
743 os << "managed by Automatic Reference Counting";
744 } else {
745 os << "whose name ('" << MD->getSelector().getAsString()
746 << "') does not start with "
747 "'copy', 'mutableCopy', 'alloc' or 'new'."
748 " This violates the naming convention rules"
749 " given in the Memory Management Guide for Cocoa";
750 }
George Karpenkov6e9fd132018-08-22 01:17:09 +0000751 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000752 const FunctionDecl *FD = cast<FunctionDecl>(D);
753 os << "whose name ('" << *FD
754 << "') does not contain 'Copy' or 'Create'. This violates the naming"
755 " convention rules given in the Memory Management Guide for Core"
756 " Foundation";
757 }
758 }
George Karpenkovf893ea12018-11-30 02:17:44 +0000759 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000760 os << " is not referenced later in this execution path and has a retain "
761 "count of +" << RV->getCount();
George Karpenkovf893ea12018-11-30 02:17:44 +0000762 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000763
764 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
765}
766
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000767RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
George Karpenkov0bb17c42019-01-10 18:16:25 +0000768 ExplodedNode *n, SymbolRef sym,
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000769 bool isLeak)
770 : BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) {
771 if (!isLeak)
George Karpenkov0bb17c42019-01-10 18:16:25 +0000772 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
George Karpenkov62db8862018-11-30 02:18:23 +0000773}
774
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000775RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
George Karpenkov0bb17c42019-01-10 18:16:25 +0000776 ExplodedNode *n, SymbolRef sym,
777 StringRef endText)
George Karpenkov62db8862018-11-30 02:18:23 +0000778 : BugReport(D, D.getDescription(), endText, n) {
779
George Karpenkov0bb17c42019-01-10 18:16:25 +0000780 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
George Karpenkov62db8862018-11-30 02:18:23 +0000781}
782
George Karpenkov0bb17c42019-01-10 18:16:25 +0000783void RefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000784 const SourceManager& SMgr = Ctx.getSourceManager();
785
786 if (!sym->getOriginRegion())
787 return;
788
789 auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion());
790 if (Region) {
791 const Decl *PDecl = Region->getDecl();
792 if (PDecl && isa<ParmVarDecl>(PDecl)) {
George Karpenkov717c4c02019-01-10 18:15:17 +0000793 PathDiagnosticLocation ParamLocation =
794 PathDiagnosticLocation::create(PDecl, SMgr);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000795 Location = ParamLocation;
796 UniqueingLocation = ParamLocation;
797 UniqueingDecl = Ctx.getLocationContext()->getDecl();
798 }
799 }
800}
801
George Karpenkov0bb17c42019-01-10 18:16:25 +0000802void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx,
George Karpenkova1c3bb82018-11-30 02:17:31 +0000803 SymbolRef sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000804 // Most bug reports are cached at the location where they occurred.
805 // With leaks, we want to unique them by the location where they were
806 // allocated, and only report a single path. To do this, we need to find
807 // the allocation site of a piece of tracked memory, which we do via a
808 // call to GetAllocationSite. This will walk the ExplodedGraph backwards.
809 // Note that this is *not* the trimmed graph; we are guaranteed, however,
810 // that all ancestor nodes that represent the allocation site have the
811 // same SourceLocation.
812 const ExplodedNode *AllocNode = nullptr;
813
814 const SourceManager& SMgr = Ctx.getSourceManager();
815
816 AllocationInfo AllocI =
George Karpenkova1c3bb82018-11-30 02:17:31 +0000817 GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000818
819 AllocNode = AllocI.N;
820 AllocBinding = AllocI.R;
821 markInteresting(AllocI.InterestingMethodContext);
822
823 // Get the SourceLocation for the allocation site.
824 // FIXME: This will crash the analyzer if an allocation comes from an
825 // implicit call (ex: a destructor call).
826 // (Currently there are no such allocations in Cocoa, though.)
827 AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
828
829 if (!AllocStmt) {
830 AllocBinding = nullptr;
831 return;
832 }
833
834 PathDiagnosticLocation AllocLocation =
835 PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
836 AllocNode->getLocationContext());
837 Location = AllocLocation;
838
839 // Set uniqieing info, which will be used for unique the bug reports. The
840 // leaks should be uniqued on the allocation site.
841 UniqueingLocation = AllocLocation;
842 UniqueingDecl = AllocNode->getLocationContext()->getDecl();
843}
844
George Karpenkov0bb17c42019-01-10 18:16:25 +0000845void RefLeakReport::createDescription(CheckerContext &Ctx) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000846 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
847 Description.clear();
848 llvm::raw_string_ostream os(Description);
849 os << "Potential leak of an object";
850
851 Optional<std::string> RegionDescription = describeRegion(AllocBinding);
852 if (RegionDescription) {
853 os << " stored into '" << *RegionDescription << '\'';
George Karpenkovf893ea12018-11-30 02:17:44 +0000854 } else {
855
856 // If we can't figure out the name, just supply the type information.
George Karpenkov4f64b382019-01-10 18:15:57 +0000857 os << " of type '" << getPrettyTypeName(Sym->getType()) << "'";
George Karpenkov70c2ee32018-08-17 21:41:07 +0000858 }
859}
860
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000861RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts,
George Karpenkov0bb17c42019-01-10 18:16:25 +0000862 ExplodedNode *n, SymbolRef sym,
863 CheckerContext &Ctx)
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000864 : RefCountReport(D, LOpts, n, sym, /*isLeak=*/true) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000865
866 deriveAllocLocation(Ctx, sym);
867 if (!AllocBinding)
868 deriveParamLocation(Ctx, sym);
869
George Karpenkov936a9c92018-12-07 20:21:37 +0000870 createDescription(Ctx);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000871
George Karpenkov0bb17c42019-01-10 18:16:25 +0000872 addVisitor(llvm::make_unique<RefLeakReportVisitor>(sym));
George Karpenkov70c2ee32018-08-17 21:41:07 +0000873}