blob: f546112f96096ea4173f534112e7c4339f062697 [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 }
Simon Pilgrimc6368062019-01-18 20:40:35 +000041 llvm_unreachable("Unknown RefCountBugType");
George Karpenkov2c2d0b62019-01-18 19:24:55 +000042}
43
44StringRef RefCountBug::getDescription() const {
45 switch (BT) {
46 case UseAfterRelease:
47 return "Reference-counted object is used after it is released";
48 case ReleaseNotOwned:
49 return "Incorrect decrement of the reference count of an object that is "
50 "not owned at this point by the caller";
51 case DeallocNotOwned:
52 return "-dealloc sent to object that may be referenced elsewhere";
53 case FreeNotOwned:
54 return "'free' called on an object that may be referenced elsewhere";
55 case OverAutorelease:
56 return "Object autoreleased too many times";
57 case ReturnNotOwnedForOwned:
58 return "Object with a +0 retain count returned to caller where a +1 "
59 "(owning) retain count is expected";
60 case LeakWithinFunction:
61 case LeakAtReturn:
62 return "";
63 }
Simon Pilgrimc6368062019-01-18 20:40:35 +000064 llvm_unreachable("Unknown RefCountBugType");
George Karpenkov2c2d0b62019-01-18 19:24:55 +000065}
66
67RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT)
68 : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount,
69 /*SupressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn),
70 BT(BT) {}
71
George Karpenkov70c2ee32018-08-17 21:41:07 +000072static bool isNumericLiteralExpression(const Expr *E) {
73 // FIXME: This set of cases was copied from SemaExprObjC.
74 return isa<IntegerLiteral>(E) ||
75 isa<CharacterLiteral>(E) ||
76 isa<FloatingLiteral>(E) ||
77 isa<ObjCBoolLiteralExpr>(E) ||
78 isa<CXXBoolLiteralExpr>(E);
79}
80
George Karpenkovf893ea12018-11-30 02:17:44 +000081/// If type represents a pointer to CXXRecordDecl,
82/// and is not a typedef, return the decl name.
83/// Otherwise, return the serialization of type.
Haojian Wuceff7302018-11-30 09:23:01 +000084static std::string getPrettyTypeName(QualType QT) {
George Karpenkovf893ea12018-11-30 02:17:44 +000085 QualType PT = QT->getPointeeType();
86 if (!PT.isNull() && !QT->getAs<TypedefType>())
87 if (const auto *RD = PT->getAsCXXRecordDecl())
88 return RD->getName();
89 return QT.getAsString();
90}
91
George Karpenkovb3303d72018-11-30 02:17:05 +000092/// Write information about the type state change to {@code os},
93/// return whether the note should be generated.
94static bool shouldGenerateNote(llvm::raw_string_ostream &os,
George Karpenkov2c2d0b62019-01-18 19:24:55 +000095 const RefVal *PrevT,
96 const RefVal &CurrV,
George Karpenkov717c4c02019-01-10 18:15:17 +000097 bool DeallocSent) {
George Karpenkovb3303d72018-11-30 02:17:05 +000098 // Get the previous type state.
99 RefVal PrevV = *PrevT;
100
101 // Specially handle -dealloc.
George Karpenkov717c4c02019-01-10 18:15:17 +0000102 if (DeallocSent) {
George Karpenkovb3303d72018-11-30 02:17:05 +0000103 // Determine if the object's reference count was pushed to zero.
104 assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");
105 // We may not have transitioned to 'release' if we hit an error.
106 // This case is handled elsewhere.
107 if (CurrV.getKind() == RefVal::Released) {
108 assert(CurrV.getCombinedCounts() == 0);
109 os << "Object released by directly sending the '-dealloc' message";
110 return true;
111 }
112 }
113
114 // Determine if the typestate has changed.
115 if (!PrevV.hasSameState(CurrV))
116 switch (CurrV.getKind()) {
117 case RefVal::Owned:
118 case RefVal::NotOwned:
119 if (PrevV.getCount() == CurrV.getCount()) {
120 // Did an autorelease message get sent?
121 if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())
122 return false;
123
124 assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
125 os << "Object autoreleased";
126 return true;
127 }
128
129 if (PrevV.getCount() > CurrV.getCount())
130 os << "Reference count decremented.";
131 else
132 os << "Reference count incremented.";
133
134 if (unsigned Count = CurrV.getCount())
135 os << " The object now has a +" << Count << " retain count.";
136
137 return true;
138
139 case RefVal::Released:
140 if (CurrV.getIvarAccessHistory() ==
141 RefVal::IvarAccessHistory::ReleasedAfterDirectAccess &&
142 CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) {
143 os << "Strong instance variable relinquished. ";
144 }
145 os << "Object released.";
146 return true;
147
148 case RefVal::ReturnedOwned:
149 // Autoreleases can be applied after marking a node ReturnedOwned.
150 if (CurrV.getAutoreleaseCount())
151 return false;
152
153 os << "Object returned to caller as an owning reference (single "
154 "retain count transferred to caller)";
155 return true;
156
157 case RefVal::ReturnedNotOwned:
158 os << "Object returned to caller with a +0 retain count";
159 return true;
160
161 default:
162 return false;
163 }
164 return true;
165}
166
George Karpenkov5be959c2019-01-11 23:35:17 +0000167/// Finds argument index of the out paramter in the call {@code S}
168/// corresponding to the symbol {@code Sym}.
169/// If none found, returns None.
170static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
171 const LocationContext *LCtx,
172 SymbolRef &Sym,
173 Optional<CallEventRef<>> CE) {
174 if (!CE)
175 return None;
176
177 for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++)
178 if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())
179 if (const auto *TR = dyn_cast<TypedValueRegion>(MR))
180 if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymExpr() == Sym)
181 return Idx;
182
183 return None;
184}
185
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000186Optional<std::string> findMetaClassAlloc(const Expr *Callee) {
187 if (const auto *ME = dyn_cast<MemberExpr>(Callee)) {
188 if (ME->getMemberDecl()->getNameAsString() != "alloc")
189 return None;
190 const Expr *This = ME->getBase()->IgnoreParenImpCasts();
191 if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) {
192 const ValueDecl *VD = DRE->getDecl();
193 if (VD->getNameAsString() != "metaClass")
194 return None;
195
196 if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext()))
197 return RD->getNameAsString();
198
199 }
200 }
201 return None;
202}
203
204std::string findAllocatedObjectName(const Stmt *S,
205 QualType QT) {
206 if (const auto *CE = dyn_cast<CallExpr>(S))
207 if (auto Out = findMetaClassAlloc(CE->getCallee()))
208 return *Out;
209 return getPrettyTypeName(QT);
210}
211
George Karpenkovff014862018-12-11 01:13:40 +0000212static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
213 const LocationContext *LCtx,
214 const RefVal &CurrV, SymbolRef &Sym,
215 const Stmt *S,
216 llvm::raw_string_ostream &os) {
George Karpenkov5be959c2019-01-11 23:35:17 +0000217 CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
George Karpenkov62db8862018-11-30 02:18:23 +0000218 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
219 // Get the name of the callee (if it is available)
220 // from the tracked SVal.
221 SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
222 const FunctionDecl *FD = X.getAsFunctionDecl();
223
224 // If failed, try to get it from AST.
225 if (!FD)
226 FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
227
228 if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) {
229 os << "Call to method '" << MD->getQualifiedNameAsString() << '\'';
230 } else if (FD) {
231 os << "Call to function '" << FD->getQualifiedNameAsString() << '\'';
232 } else {
233 os << "function call";
234 }
George Karpenkovff014862018-12-11 01:13:40 +0000235 } else if (isa<CXXNewExpr>(S)) {
236 os << "Operator 'new'";
George Karpenkov62db8862018-11-30 02:18:23 +0000237 } else {
238 assert(isa<ObjCMessageExpr>(S));
George Karpenkov62db8862018-11-30 02:18:23 +0000239 CallEventRef<ObjCMethodCall> Call =
240 Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
241
242 switch (Call->getMessageKind()) {
243 case OCM_Message:
244 os << "Method";
245 break;
246 case OCM_PropertyAccess:
247 os << "Property";
248 break;
249 case OCM_Subscript:
250 os << "Subscript";
251 break;
252 }
253 }
254
George Karpenkov5be959c2019-01-11 23:35:17 +0000255 Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx);
256 auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE);
257
258 // If index is not found, we assume that the symbol was returned.
259 if (!Idx) {
260 os << " returns ";
261 } else {
262 os << " writes ";
263 }
George Karpenkov3c1e0662019-01-10 18:28:10 +0000264
George Karpenkov7e3016d2019-01-10 18:13:46 +0000265 if (CurrV.getObjKind() == ObjKind::CF) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000266 os << "a Core Foundation object of type '"
George Karpenkov4f64b382019-01-10 18:15:57 +0000267 << Sym->getType().getAsString() << "' with a ";
George Karpenkov7e3016d2019-01-10 18:13:46 +0000268 } else if (CurrV.getObjKind() == ObjKind::OS) {
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000269 os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType())
George Karpenkov4f64b382019-01-10 18:15:57 +0000270 << "' with a ";
George Karpenkov7e3016d2019-01-10 18:13:46 +0000271 } else if (CurrV.getObjKind() == ObjKind::Generalized) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000272 os << "an object of type '" << Sym->getType().getAsString()
George Karpenkov4f64b382019-01-10 18:15:57 +0000273 << "' with a ";
George Karpenkov62db8862018-11-30 02:18:23 +0000274 } else {
George Karpenkov7e3016d2019-01-10 18:13:46 +0000275 assert(CurrV.getObjKind() == ObjKind::ObjC);
George Karpenkov62db8862018-11-30 02:18:23 +0000276 QualType T = Sym->getType();
277 if (!isa<ObjCObjectPointerType>(T)) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000278 os << "an Objective-C object with a ";
George Karpenkov62db8862018-11-30 02:18:23 +0000279 } else {
280 const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);
George Karpenkov3c1e0662019-01-10 18:28:10 +0000281 os << "an instance of " << PT->getPointeeType().getAsString()
George Karpenkov62db8862018-11-30 02:18:23 +0000282 << " with a ";
283 }
284 }
285
286 if (CurrV.isOwned()) {
287 os << "+1 retain count";
288 } else {
289 assert(CurrV.isNotOwned());
290 os << "+0 retain count";
291 }
George Karpenkov5be959c2019-01-11 23:35:17 +0000292
293 if (Idx) {
294 os << " into an out parameter '";
295 const ParmVarDecl *PVD = (*CE)->parameters()[*Idx];
296 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
297 /*Qualified=*/false);
298 os << "'";
299
300 QualType RT = (*CE)->getResultType();
301 if (!RT.isNull() && !RT->isVoidType()) {
302 SVal RV = (*CE)->getReturnValue();
303 if (CurrSt->isNull(RV).isConstrainedTrue()) {
304 os << " (assuming the call returns zero)";
305 } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) {
306 os << " (assuming the call returns non-zero)";
307 }
308
309 }
310 }
George Karpenkov62db8862018-11-30 02:18:23 +0000311}
312
313namespace clang {
314namespace ento {
315namespace retaincountchecker {
316
George Karpenkov0bb17c42019-01-10 18:16:25 +0000317class RefCountReportVisitor : public BugReporterVisitor {
George Karpenkov62db8862018-11-30 02:18:23 +0000318protected:
319 SymbolRef Sym;
George Karpenkov62db8862018-11-30 02:18:23 +0000320
321public:
George Karpenkov0bb17c42019-01-10 18:16:25 +0000322 RefCountReportVisitor(SymbolRef sym) : Sym(sym) {}
George Karpenkov62db8862018-11-30 02:18:23 +0000323
324 void Profile(llvm::FoldingSetNodeID &ID) const override {
325 static int x = 0;
326 ID.AddPointer(&x);
327 ID.AddPointer(Sym);
328 }
329
330 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
331 BugReporterContext &BRC,
332 BugReport &BR) override;
333
334 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
335 const ExplodedNode *N,
336 BugReport &BR) override;
337};
338
George Karpenkov0bb17c42019-01-10 18:16:25 +0000339class RefLeakReportVisitor : public RefCountReportVisitor {
George Karpenkov62db8862018-11-30 02:18:23 +0000340public:
George Karpenkov0bb17c42019-01-10 18:16:25 +0000341 RefLeakReportVisitor(SymbolRef sym) : RefCountReportVisitor(sym) {}
George Karpenkov62db8862018-11-30 02:18:23 +0000342
343 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
344 const ExplodedNode *N,
345 BugReport &BR) override;
346};
347
348} // end namespace retaincountchecker
349} // end namespace ento
350} // end namespace clang
351
George Karpenkovff014862018-12-11 01:13:40 +0000352
353/// Find the first node with the parent stack frame.
354static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) {
355 const StackFrameContext *SC = Pred->getStackFrame();
356 if (SC->inTopFrame())
357 return nullptr;
358 const StackFrameContext *PC = SC->getParent()->getStackFrame();
359 if (!PC)
360 return nullptr;
361
362 const ExplodedNode *N = Pred;
363 while (N && N->getStackFrame() != PC) {
364 N = N->getFirstPred();
365 }
366 return N;
367}
368
369
370/// Insert a diagnostic piece at function exit
371/// if a function parameter is annotated as "os_consumed",
372/// but it does not actually consume the reference.
373static std::shared_ptr<PathDiagnosticEventPiece>
374annotateConsumedSummaryMismatch(const ExplodedNode *N,
375 CallExitBegin &CallExitLoc,
376 const SourceManager &SM,
377 CallEventManager &CEMgr) {
378
379 const ExplodedNode *CN = getCalleeNode(N);
380 if (!CN)
381 return nullptr;
382
383 CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState());
384
385 std::string sbuf;
386 llvm::raw_string_ostream os(sbuf);
387 ArrayRef<const ParmVarDecl *> Parameters = Call->parameters();
388 for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {
389 const ParmVarDecl *PVD = Parameters[I];
390
391 if (!PVD->hasAttr<OSConsumedAttr>())
George Karpenkovf5085322018-12-21 02:16:23 +0000392 continue;
George Karpenkovff014862018-12-11 01:13:40 +0000393
394 if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
395 const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR);
396 const RefVal *CountAtExit = getRefBinding(N->getState(), SR);
397
398 if (!CountBeforeCall || !CountAtExit)
399 continue;
400
401 unsigned CountBefore = CountBeforeCall->getCount();
402 unsigned CountAfter = CountAtExit->getCount();
403
404 bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
405 if (!AsExpected) {
406 os << "Parameter '";
407 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
408 /*Qualified=*/false);
George Karpenkov79f03402018-12-21 19:13:28 +0000409 os << "' is marked as consuming, but the function did not consume "
George Karpenkovff014862018-12-11 01:13:40 +0000410 << "the reference\n";
411 }
412 }
413 }
414
415 if (os.str().empty())
416 return nullptr;
417
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000418 PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM);
George Karpenkovff014862018-12-11 01:13:40 +0000419 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
420}
421
George Karpenkov70c2ee32018-08-17 21:41:07 +0000422std::shared_ptr<PathDiagnosticPiece>
George Karpenkov0bb17c42019-01-10 18:16:25 +0000423RefCountReportVisitor::VisitNode(const ExplodedNode *N,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000424 BugReporterContext &BRC, BugReport &BR) {
George Karpenkov717c4c02019-01-10 18:15:17 +0000425
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000426 const auto &BT = static_cast<const RefCountBug&>(BR.getBugType());
427
428 bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned ||
429 BT.getBugType() == RefCountBug::DeallocNotOwned;
430
George Karpenkovff014862018-12-11 01:13:40 +0000431 const SourceManager &SM = BRC.getSourceManager();
432 CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
George Karpenkovf5085322018-12-21 02:16:23 +0000433 if (auto CE = N->getLocationAs<CallExitBegin>())
George Karpenkovff014862018-12-11 01:13:40 +0000434 if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
435 return PD;
George Karpenkovff014862018-12-11 01:13:40 +0000436
George Karpenkov70c2ee32018-08-17 21:41:07 +0000437 // FIXME: We will eventually need to handle non-statement-based events
438 // (__attribute__((cleanup))).
439 if (!N->getLocation().getAs<StmtPoint>())
440 return nullptr;
441
442 // Check if the type state has changed.
George Karpenkov62db8862018-11-30 02:18:23 +0000443 const ExplodedNode *PrevNode = N->getFirstPred();
444 ProgramStateRef PrevSt = PrevNode->getState();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000445 ProgramStateRef CurrSt = N->getState();
446 const LocationContext *LCtx = N->getLocationContext();
447
448 const RefVal* CurrT = getRefBinding(CurrSt, Sym);
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000449 if (!CurrT)
450 return nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000451
452 const RefVal &CurrV = *CurrT;
453 const RefVal *PrevT = getRefBinding(PrevSt, Sym);
454
455 // Create a string buffer to constain all the useful things we want
456 // to tell the user.
457 std::string sbuf;
458 llvm::raw_string_ostream os(sbuf);
459
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000460 if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) {
461 os << "Object is now not exclusively owned";
462 auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM);
463 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
464 }
465
George Karpenkov70c2ee32018-08-17 21:41:07 +0000466 // This is the allocation site since the previous node had no bindings
467 // for this symbol.
468 if (!PrevT) {
469 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
470
471 if (isa<ObjCIvarRefExpr>(S) &&
472 isSynthesizedAccessor(LCtx->getStackFrame())) {
473 S = LCtx->getStackFrame()->getCallSite();
474 }
475
476 if (isa<ObjCArrayLiteral>(S)) {
477 os << "NSArray literal is an object with a +0 retain count";
George Karpenkovb3303d72018-11-30 02:17:05 +0000478 } else if (isa<ObjCDictionaryLiteral>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000479 os << "NSDictionary literal is an object with a +0 retain count";
George Karpenkovb3303d72018-11-30 02:17:05 +0000480 } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000481 if (isNumericLiteralExpression(BL->getSubExpr()))
482 os << "NSNumber literal is an object with a +0 retain count";
483 else {
484 const ObjCInterfaceDecl *BoxClass = nullptr;
485 if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
486 BoxClass = Method->getClassInterface();
487
488 // We should always be able to find the boxing class interface,
489 // but consider this future-proofing.
George Karpenkovb3303d72018-11-30 02:17:05 +0000490 if (BoxClass) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000491 os << *BoxClass << " b";
George Karpenkovb3303d72018-11-30 02:17:05 +0000492 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000493 os << "B";
George Karpenkovb3303d72018-11-30 02:17:05 +0000494 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000495
496 os << "oxed expression produces an object with a +0 retain count";
497 }
George Karpenkovb3303d72018-11-30 02:17:05 +0000498 } else if (isa<ObjCIvarRefExpr>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000499 os << "Object loaded from instance variable";
George Karpenkovb3303d72018-11-30 02:17:05 +0000500 } else {
George Karpenkov62db8862018-11-30 02:18:23 +0000501 generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000502 }
503
George Karpenkovff014862018-12-11 01:13:40 +0000504 PathDiagnosticLocation Pos(S, SM, N->getLocationContext());
George Karpenkov70c2ee32018-08-17 21:41:07 +0000505 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
506 }
507
508 // Gather up the effects that were performed on the object at this
509 // program point
George Karpenkov717c4c02019-01-10 18:15:17 +0000510 bool DeallocSent = false;
511
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000512 const ProgramPointTag *Tag = N->getLocation().getTag();
513 if (Tag && Tag->getTagDescription().contains(
514 RetainCountChecker::DeallocTagDescription)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000515 // We only have summaries attached to nodes after evaluating CallExpr and
516 // ObjCMessageExprs.
517 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
518
519 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
520 // Iterate through the parameter expressions and see if the symbol
521 // was ever passed as an argument.
522 unsigned i = 0;
523
George Karpenkovb3303d72018-11-30 02:17:05 +0000524 for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000525
526 // Retrieve the value of the argument. Is it the symbol
527 // we are interested in?
528 if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)
529 continue;
530
531 // We have an argument. Get the effect!
George Karpenkov717c4c02019-01-10 18:15:17 +0000532 DeallocSent = true;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000533 }
534 } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
535 if (const Expr *receiver = ME->getInstanceReceiver()) {
536 if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
537 .getAsLocSymbol() == Sym) {
538 // The symbol we are tracking is the receiver.
George Karpenkov717c4c02019-01-10 18:15:17 +0000539 DeallocSent = true;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000540 }
541 }
542 }
543 }
544
George Karpenkov717c4c02019-01-10 18:15:17 +0000545 if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent))
George Karpenkovb3303d72018-11-30 02:17:05 +0000546 return nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000547
548 if (os.str().empty())
549 return nullptr; // We have nothing to say!
550
551 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
552 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
553 N->getLocationContext());
554 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
555
556 // Add the range by scanning the children of the statement for any bindings
557 // to Sym.
558 for (const Stmt *Child : S->children())
559 if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))
560 if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {
561 P->addRange(Exp->getSourceRange());
562 break;
563 }
564
565 return std::move(P);
566}
567
568static Optional<std::string> describeRegion(const MemRegion *MR) {
569 if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
570 return std::string(VR->getDecl()->getName());
571 // Once we support more storage locations for bindings,
572 // this would need to be improved.
573 return None;
574}
575
576namespace {
577// Find the first node in the current function context that referred to the
578// tracked symbol and the memory location that value was stored to. Note, the
579// value is only reported if the allocation occurred in the same function as
580// the leak. The function can also return a location context, which should be
581// treated as interesting.
582struct AllocationInfo {
583 const ExplodedNode* N;
584 const MemRegion *R;
585 const LocationContext *InterestingMethodContext;
586 AllocationInfo(const ExplodedNode *InN,
587 const MemRegion *InR,
588 const LocationContext *InInterestingMethodContext) :
589 N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
590};
591} // end anonymous namespace
592
George Karpenkova1c3bb82018-11-30 02:17:31 +0000593static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
594 const ExplodedNode *N, SymbolRef Sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000595 const ExplodedNode *AllocationNode = N;
596 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
597 const MemRegion *FirstBinding = nullptr;
598 const LocationContext *LeakContext = N->getLocationContext();
599
600 // The location context of the init method called on the leaked object, if
601 // available.
602 const LocationContext *InitMethodContext = nullptr;
603
604 while (N) {
605 ProgramStateRef St = N->getState();
606 const LocationContext *NContext = N->getLocationContext();
607
608 if (!getRefBinding(St, Sym))
609 break;
610
611 StoreManager::FindUniqueBinding FB(Sym);
612 StateMgr.iterBindings(St, FB);
613
614 if (FB) {
615 const MemRegion *R = FB.getRegion();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000616 // Do not show local variables belonging to a function other than
617 // where the error is reported.
George Karpenkov79ed11c2018-12-11 01:13:20 +0000618 if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace()))
619 if (MR->getStackFrame() == LeakContext->getStackFrame())
620 FirstBinding = R;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000621 }
622
623 // AllocationNode is the last node in which the symbol was tracked.
624 AllocationNode = N;
625
626 // AllocationNodeInCurrentContext, is the last node in the current or
627 // parent context in which the symbol was tracked.
628 //
Raphael Isemannb23ccec2018-12-10 12:37:46 +0000629 // Note that the allocation site might be in the parent context. For example,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000630 // the case where an allocation happens in a block that captures a reference
631 // to it and that reference is overwritten/dropped by another call to
632 // the block.
633 if (NContext == LeakContext || NContext->isParentOf(LeakContext))
634 AllocationNodeInCurrentOrParentContext = N;
635
636 // Find the last init that was called on the given symbol and store the
637 // init method's location context.
638 if (!InitMethodContext)
George Karpenkova1c3bb82018-11-30 02:17:31 +0000639 if (auto CEP = N->getLocation().getAs<CallEnter>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000640 const Stmt *CE = CEP->getCallExpr();
George Karpenkova1c3bb82018-11-30 02:17:31 +0000641 if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000642 const Stmt *RecExpr = ME->getInstanceReceiver();
643 if (RecExpr) {
644 SVal RecV = St->getSVal(RecExpr, NContext);
645 if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
646 InitMethodContext = CEP->getCalleeContext();
647 }
648 }
649 }
650
George Karpenkova1c3bb82018-11-30 02:17:31 +0000651 N = N->getFirstPred();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000652 }
653
654 // If we are reporting a leak of the object that was allocated with alloc,
655 // mark its init method as interesting.
656 const LocationContext *InterestingMethodContext = nullptr;
657 if (InitMethodContext) {
658 const ProgramPoint AllocPP = AllocationNode->getLocation();
659 if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
660 if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
661 if (ME->getMethodFamily() == OMF_alloc)
662 InterestingMethodContext = InitMethodContext;
663 }
664
665 // If allocation happened in a function different from the leak node context,
666 // do not report the binding.
667 assert(N && "Could not find allocation node");
George Karpenkova1c3bb82018-11-30 02:17:31 +0000668
669 if (AllocationNodeInCurrentOrParentContext &&
670 AllocationNodeInCurrentOrParentContext->getLocationContext() !=
671 LeakContext)
George Karpenkov70c2ee32018-08-17 21:41:07 +0000672 FirstBinding = nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000673
674 return AllocationInfo(AllocationNodeInCurrentOrParentContext,
675 FirstBinding,
676 InterestingMethodContext);
677}
678
679std::shared_ptr<PathDiagnosticPiece>
George Karpenkov0bb17c42019-01-10 18:16:25 +0000680RefCountReportVisitor::getEndPath(BugReporterContext &BRC,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000681 const ExplodedNode *EndN, BugReport &BR) {
682 BR.markInteresting(Sym);
683 return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
684}
685
686std::shared_ptr<PathDiagnosticPiece>
George Karpenkov0bb17c42019-01-10 18:16:25 +0000687RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000688 const ExplodedNode *EndN, BugReport &BR) {
689
690 // Tell the BugReporterContext to report cases when the tracked symbol is
691 // assigned to different variables, etc.
692 BR.markInteresting(Sym);
693
694 // We are reporting a leak. Walk up the graph to get to the first node where
695 // the symbol appeared, and also get the first VarDecl that tracked object
696 // is stored to.
George Karpenkova1c3bb82018-11-30 02:17:31 +0000697 AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000698
699 const MemRegion* FirstBinding = AllocI.R;
700 BR.markInteresting(AllocI.InterestingMethodContext);
701
702 SourceManager& SM = BRC.getSourceManager();
703
704 // Compute an actual location for the leak. Sometimes a leak doesn't
705 // occur at an actual statement (e.g., transition between blocks; end
706 // of function) so we need to walk the graph and compute a real location.
707 const ExplodedNode *LeakN = EndN;
708 PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM);
709
710 std::string sbuf;
711 llvm::raw_string_ostream os(sbuf);
712
713 os << "Object leaked: ";
714
715 Optional<std::string> RegionDescription = describeRegion(FirstBinding);
716 if (RegionDescription) {
717 os << "object allocated and stored into '" << *RegionDescription << '\'';
George Karpenkovb3303d72018-11-30 02:17:05 +0000718 } else {
George Karpenkov4f64b382019-01-10 18:15:57 +0000719 os << "allocated object of type '" << getPrettyTypeName(Sym->getType())
720 << "'";
George Karpenkovb3303d72018-11-30 02:17:05 +0000721 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000722
723 // Get the retain count.
724 const RefVal* RV = getRefBinding(EndN->getState(), Sym);
725 assert(RV);
726
727 if (RV->getKind() == RefVal::ErrorLeakReturned) {
728 // FIXME: Per comments in rdar://6320065, "create" only applies to CF
729 // objects. Only "copy", "alloc", "retain" and "new" transfer ownership
730 // to the caller for NS objects.
731 const Decl *D = &EndN->getCodeDecl();
732
733 os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
734 : " is returned from a function ");
735
George Karpenkova1c3bb82018-11-30 02:17:31 +0000736 if (D->hasAttr<CFReturnsNotRetainedAttr>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000737 os << "that is annotated as CF_RETURNS_NOT_RETAINED";
George Karpenkova1c3bb82018-11-30 02:17:31 +0000738 } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000739 os << "that is annotated as NS_RETURNS_NOT_RETAINED";
George Karpenkovb43772d2018-11-30 02:18:50 +0000740 } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {
741 os << "that is annotated as OS_RETURNS_NOT_RETAINED";
George Karpenkova1c3bb82018-11-30 02:17:31 +0000742 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000743 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
744 if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) {
745 os << "managed by Automatic Reference Counting";
746 } else {
747 os << "whose name ('" << MD->getSelector().getAsString()
748 << "') does not start with "
749 "'copy', 'mutableCopy', 'alloc' or 'new'."
750 " This violates the naming convention rules"
751 " given in the Memory Management Guide for Cocoa";
752 }
George Karpenkov6e9fd132018-08-22 01:17:09 +0000753 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000754 const FunctionDecl *FD = cast<FunctionDecl>(D);
755 os << "whose name ('" << *FD
756 << "') does not contain 'Copy' or 'Create'. This violates the naming"
757 " convention rules given in the Memory Management Guide for Core"
758 " Foundation";
759 }
760 }
George Karpenkovf893ea12018-11-30 02:17:44 +0000761 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000762 os << " is not referenced later in this execution path and has a retain "
763 "count of +" << RV->getCount();
George Karpenkovf893ea12018-11-30 02:17:44 +0000764 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000765
766 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
767}
768
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000769RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
George Karpenkov0bb17c42019-01-10 18:16:25 +0000770 ExplodedNode *n, SymbolRef sym,
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000771 bool isLeak)
772 : BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) {
773 if (!isLeak)
George Karpenkov0bb17c42019-01-10 18:16:25 +0000774 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
George Karpenkov62db8862018-11-30 02:18:23 +0000775}
776
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000777RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
George Karpenkov0bb17c42019-01-10 18:16:25 +0000778 ExplodedNode *n, SymbolRef sym,
779 StringRef endText)
George Karpenkov62db8862018-11-30 02:18:23 +0000780 : BugReport(D, D.getDescription(), endText, n) {
781
George Karpenkov0bb17c42019-01-10 18:16:25 +0000782 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
George Karpenkov62db8862018-11-30 02:18:23 +0000783}
784
George Karpenkov0bb17c42019-01-10 18:16:25 +0000785void RefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000786 const SourceManager& SMgr = Ctx.getSourceManager();
787
788 if (!sym->getOriginRegion())
789 return;
790
791 auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion());
792 if (Region) {
793 const Decl *PDecl = Region->getDecl();
794 if (PDecl && isa<ParmVarDecl>(PDecl)) {
George Karpenkov717c4c02019-01-10 18:15:17 +0000795 PathDiagnosticLocation ParamLocation =
796 PathDiagnosticLocation::create(PDecl, SMgr);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000797 Location = ParamLocation;
798 UniqueingLocation = ParamLocation;
799 UniqueingDecl = Ctx.getLocationContext()->getDecl();
800 }
801 }
802}
803
George Karpenkov0bb17c42019-01-10 18:16:25 +0000804void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx,
George Karpenkova1c3bb82018-11-30 02:17:31 +0000805 SymbolRef sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000806 // Most bug reports are cached at the location where they occurred.
807 // With leaks, we want to unique them by the location where they were
808 // allocated, and only report a single path. To do this, we need to find
809 // the allocation site of a piece of tracked memory, which we do via a
810 // call to GetAllocationSite. This will walk the ExplodedGraph backwards.
811 // Note that this is *not* the trimmed graph; we are guaranteed, however,
812 // that all ancestor nodes that represent the allocation site have the
813 // same SourceLocation.
814 const ExplodedNode *AllocNode = nullptr;
815
816 const SourceManager& SMgr = Ctx.getSourceManager();
817
818 AllocationInfo AllocI =
George Karpenkova1c3bb82018-11-30 02:17:31 +0000819 GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000820
821 AllocNode = AllocI.N;
822 AllocBinding = AllocI.R;
823 markInteresting(AllocI.InterestingMethodContext);
824
825 // Get the SourceLocation for the allocation site.
826 // FIXME: This will crash the analyzer if an allocation comes from an
827 // implicit call (ex: a destructor call).
828 // (Currently there are no such allocations in Cocoa, though.)
829 AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
830
831 if (!AllocStmt) {
832 AllocBinding = nullptr;
833 return;
834 }
835
836 PathDiagnosticLocation AllocLocation =
837 PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
838 AllocNode->getLocationContext());
839 Location = AllocLocation;
840
841 // Set uniqieing info, which will be used for unique the bug reports. The
842 // leaks should be uniqued on the allocation site.
843 UniqueingLocation = AllocLocation;
844 UniqueingDecl = AllocNode->getLocationContext()->getDecl();
845}
846
George Karpenkov0bb17c42019-01-10 18:16:25 +0000847void RefLeakReport::createDescription(CheckerContext &Ctx) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000848 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
849 Description.clear();
850 llvm::raw_string_ostream os(Description);
851 os << "Potential leak of an object";
852
853 Optional<std::string> RegionDescription = describeRegion(AllocBinding);
854 if (RegionDescription) {
855 os << " stored into '" << *RegionDescription << '\'';
George Karpenkovf893ea12018-11-30 02:17:44 +0000856 } else {
857
858 // If we can't figure out the name, just supply the type information.
George Karpenkov4f64b382019-01-10 18:15:57 +0000859 os << " of type '" << getPrettyTypeName(Sym->getType()) << "'";
George Karpenkov70c2ee32018-08-17 21:41:07 +0000860 }
861}
862
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000863RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts,
George Karpenkov0bb17c42019-01-10 18:16:25 +0000864 ExplodedNode *n, SymbolRef sym,
865 CheckerContext &Ctx)
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000866 : RefCountReport(D, LOpts, n, sym, /*isLeak=*/true) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000867
868 deriveAllocLocation(Ctx, sym);
869 if (!AllocBinding)
870 deriveParamLocation(Ctx, sym);
871
George Karpenkov936a9c92018-12-07 20:21:37 +0000872 createDescription(Ctx);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000873
George Karpenkov0bb17c42019-01-10 18:16:25 +0000874 addVisitor(llvm::make_unique<RefLeakReportVisitor>(sym));
George Karpenkov70c2ee32018-08-17 21:41:07 +0000875}