1 // RetainCountDiagnostics.cpp - Checks for leaks and other issues -*- C++ -*--//
2 //
3 // 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
6 //
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 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/SmallVector.h"
18
19 using namespace clang;
20 using namespace ento;
21 using namespace retaincountchecker;
22
bugTypeToName(RefCountBug::RefCountBugKind BT)23 StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugKind BT) {
24 switch (BT) {
25 case UseAfterRelease:
26 return "Use-after-release";
27 case ReleaseNotOwned:
28 return "Bad release";
29 case DeallocNotOwned:
30 return "-dealloc sent to non-exclusively owned object";
31 case FreeNotOwned:
32 return "freeing non-exclusively owned object";
33 case OverAutorelease:
34 return "Object autoreleased too many times";
35 case ReturnNotOwnedForOwned:
36 return "Method should return an owned object";
37 case LeakWithinFunction:
38 return "Leak";
39 case LeakAtReturn:
40 return "Leak of returned object";
41 }
42 llvm_unreachable("Unknown RefCountBugKind");
43 }
44
getDescription() const45 StringRef RefCountBug::getDescription() const {
46 switch (BT) {
47 case UseAfterRelease:
48 return "Reference-counted object is used after it is released";
49 case ReleaseNotOwned:
50 return "Incorrect decrement of the reference count of an object that is "
51 "not owned at this point by the caller";
52 case DeallocNotOwned:
53 return "-dealloc sent to object that may be referenced elsewhere";
54 case FreeNotOwned:
55 return "'free' called on an object that may be referenced elsewhere";
56 case OverAutorelease:
57 return "Object autoreleased too many times";
58 case ReturnNotOwnedForOwned:
59 return "Object with a +0 retain count returned to caller where a +1 "
60 "(owning) retain count is expected";
61 case LeakWithinFunction:
62 case LeakAtReturn:
63 return "";
64 }
65 llvm_unreachable("Unknown RefCountBugKind");
66 }
67
RefCountBug(CheckerNameRef Checker,RefCountBugKind BT)68 RefCountBug::RefCountBug(CheckerNameRef Checker, RefCountBugKind BT)
69 : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount,
70 /*SuppressOnSink=*/BT == LeakWithinFunction ||
71 BT == LeakAtReturn),
72 BT(BT) {}
73
isNumericLiteralExpression(const Expr * E)74 static bool isNumericLiteralExpression(const Expr *E) {
75 // FIXME: This set of cases was copied from SemaExprObjC.
76 return isa<IntegerLiteral, CharacterLiteral, FloatingLiteral,
77 ObjCBoolLiteralExpr, CXXBoolLiteralExpr>(E);
78 }
79
80 /// If type represents a pointer to CXXRecordDecl,
81 /// and is not a typedef, return the decl name.
82 /// Otherwise, return the serialization of type.
getPrettyTypeName(QualType QT)83 static std::string getPrettyTypeName(QualType QT) {
84 QualType PT = QT->getPointeeType();
85 if (!PT.isNull() && !QT->getAs<TypedefType>())
86 if (const auto *RD = PT->getAsCXXRecordDecl())
87 return std::string(RD->getName());
88 return QT.getAsString();
89 }
90
91 /// Write information about the type state change to @c os,
92 /// return whether the note should be generated.
shouldGenerateNote(llvm::raw_string_ostream & os,const RefVal * PrevT,const RefVal & CurrV,bool DeallocSent)93 static bool shouldGenerateNote(llvm::raw_string_ostream &os,
94 const RefVal *PrevT,
95 const RefVal &CurrV,
96 bool DeallocSent) {
97 // Get the previous type state.
98 RefVal PrevV = *PrevT;
99
100 // Specially handle -dealloc.
101 if (DeallocSent) {
102 // 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
166 /// Finds argument index of the out paramter in the call @c S
167 /// corresponding to the symbol @c Sym.
168 /// If none found, returns None.
findArgIdxOfSymbol(ProgramStateRef CurrSt,const LocationContext * LCtx,SymbolRef & Sym,Optional<CallEventRef<>> CE)169 static 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()).getAsSymbol() == Sym)
180 return Idx;
181
182 return None;
183 }
184
findMetaClassAlloc(const Expr * Callee)185 static Optional<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
findAllocatedObjectName(const Stmt * S,QualType QT)203 static std::string findAllocatedObjectName(const Stmt *S, 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
generateDiagnosticsForCallLike(ProgramStateRef CurrSt,const LocationContext * LCtx,const RefVal & CurrV,SymbolRef & Sym,const Stmt * S,llvm::raw_string_ostream & os)210 static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
211 const LocationContext *LCtx,
212 const RefVal &CurrV, SymbolRef &Sym,
213 const Stmt *S,
214 llvm::raw_string_ostream &os) {
215 CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
216 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 }
233 } else if (isa<CXXNewExpr>(S)) {
234 os << "Operator 'new'";
235 } else {
236 assert(isa<ObjCMessageExpr>(S));
237 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
253 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 }
262
263 if (CurrV.getObjKind() == ObjKind::CF) {
264 os << "a Core Foundation object of type '" << Sym->getType() << "' with a ";
265 } else if (CurrV.getObjKind() == ObjKind::OS) {
266 os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType())
267 << "' with a ";
268 } else if (CurrV.getObjKind() == ObjKind::Generalized) {
269 os << "an object of type '" << Sym->getType() << "' with a ";
270 } else {
271 assert(CurrV.getObjKind() == ObjKind::ObjC);
272 QualType T = Sym->getType();
273 if (!isa<ObjCObjectPointerType>(T)) {
274 os << "an Objective-C object with a ";
275 } else {
276 const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);
277 os << "an instance of " << PT->getPointeeType() << " with a ";
278 }
279 }
280
281 if (CurrV.isOwned()) {
282 os << "+1 retain count";
283 } else {
284 assert(CurrV.isNotOwned());
285 os << "+0 retain count";
286 }
287
288 if (Idx) {
289 os << " into an out parameter '";
290 const ParmVarDecl *PVD = (*CE)->parameters()[*Idx];
291 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
292 /*Qualified=*/false);
293 os << "'";
294
295 QualType RT = (*CE)->getResultType();
296 if (!RT.isNull() && !RT->isVoidType()) {
297 SVal RV = (*CE)->getReturnValue();
298 if (CurrSt->isNull(RV).isConstrainedTrue()) {
299 os << " (assuming the call returns zero)";
300 } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) {
301 os << " (assuming the call returns non-zero)";
302 }
303
304 }
305 }
306 }
307
308 namespace clang {
309 namespace ento {
310 namespace retaincountchecker {
311
312 class RefCountReportVisitor : public BugReporterVisitor {
313 protected:
314 SymbolRef Sym;
315
316 public:
RefCountReportVisitor(SymbolRef sym)317 RefCountReportVisitor(SymbolRef sym) : Sym(sym) {}
318
Profile(llvm::FoldingSetNodeID & ID) const319 void Profile(llvm::FoldingSetNodeID &ID) const override {
320 static int x = 0;
321 ID.AddPointer(&x);
322 ID.AddPointer(Sym);
323 }
324
325 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
326 BugReporterContext &BRC,
327 PathSensitiveBugReport &BR) override;
328
329 PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
330 const ExplodedNode *N,
331 PathSensitiveBugReport &BR) override;
332 };
333
334 class RefLeakReportVisitor : public RefCountReportVisitor {
335 public:
RefLeakReportVisitor(SymbolRef Sym,const MemRegion * LastBinding)336 RefLeakReportVisitor(SymbolRef Sym, const MemRegion *LastBinding)
337 : RefCountReportVisitor(Sym), LastBinding(LastBinding) {}
338
339 PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
340 const ExplodedNode *N,
341 PathSensitiveBugReport &BR) override;
342
343 private:
344 const MemRegion *LastBinding;
345 };
346
347 } // end namespace retaincountchecker
348 } // end namespace ento
349 } // end namespace clang
350
351
352 /// Find the first node with the parent stack frame.
getCalleeNode(const ExplodedNode * Pred)353 static 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.
372 static std::shared_ptr<PathDiagnosticEventPiece>
annotateConsumedSummaryMismatch(const ExplodedNode * N,CallExitBegin & CallExitLoc,const SourceManager & SM,CallEventManager & CEMgr)373 annotateConsumedSummaryMismatch(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>())
391 continue;
392
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);
408 os << "' is marked as consuming, but the function did not consume "
409 << "the reference\n";
410 }
411 }
412 }
413
414 if (os.str().empty())
415 return nullptr;
416
417 PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM);
418 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
419 }
420
421 /// Annotate the parameter at the analysis entry point.
422 static std::shared_ptr<PathDiagnosticEventPiece>
annotateStartParameter(const ExplodedNode * N,SymbolRef Sym,const SourceManager & SM)423 annotateStartParameter(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);
442 os << "Parameter '" << PVD->getDeclName() << "' starts at +";
443 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
452 PathDiagnosticPieceRef
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)453 RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
454 PathSensitiveBugReport &BR) {
455
456 const auto &BT = static_cast<const RefCountBug&>(BR.getBugType());
457
458 bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned ||
459 BT.getBugType() == RefCountBug::DeallocNotOwned;
460
461 const SourceManager &SM = BRC.getSourceManager();
462 CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
463 if (auto CE = N->getLocationAs<CallExitBegin>())
464 if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
465 return PD;
466
467 if (auto PD = annotateStartParameter(N, Sym, SM))
468 return PD;
469
470 // FIXME: We will eventually need to handle non-statement-based events
471 // (__attribute__((cleanup))).
472 if (!N->getLocation().getAs<StmtPoint>())
473 return nullptr;
474
475 // Check if the type state has changed.
476 const ExplodedNode *PrevNode = N->getFirstPred();
477 ProgramStateRef PrevSt = PrevNode->getState();
478 ProgramStateRef CurrSt = N->getState();
479 const LocationContext *LCtx = N->getLocationContext();
480
481 const RefVal* CurrT = getRefBinding(CurrSt, Sym);
482 if (!CurrT)
483 return nullptr;
484
485 const RefVal &CurrV = *CurrT;
486 const RefVal *PrevT = getRefBinding(PrevSt, Sym);
487
488 // Create a string buffer to constain all the useful things we want
489 // to tell the user.
490 std::string sbuf;
491 llvm::raw_string_ostream os(sbuf);
492
493 if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) {
494 os << "Object is now not exclusively owned";
495 auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM);
496 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
497 }
498
499 // This is the allocation site since the previous node had no bindings
500 // for this symbol.
501 if (!PrevT) {
502 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
503
504 if (isa<ObjCIvarRefExpr>(S) &&
505 isSynthesizedAccessor(LCtx->getStackFrame())) {
506 S = LCtx->getStackFrame()->getCallSite();
507 }
508
509 if (isa<ObjCArrayLiteral>(S)) {
510 os << "NSArray literal is an object with a +0 retain count";
511 } else if (isa<ObjCDictionaryLiteral>(S)) {
512 os << "NSDictionary literal is an object with a +0 retain count";
513 } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
514 if (isNumericLiteralExpression(BL->getSubExpr()))
515 os << "NSNumber literal is an object with a +0 retain count";
516 else {
517 const ObjCInterfaceDecl *BoxClass = nullptr;
518 if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
519 BoxClass = Method->getClassInterface();
520
521 // We should always be able to find the boxing class interface,
522 // but consider this future-proofing.
523 if (BoxClass) {
524 os << *BoxClass << " b";
525 } else {
526 os << "B";
527 }
528
529 os << "oxed expression produces an object with a +0 retain count";
530 }
531 } else if (isa<ObjCIvarRefExpr>(S)) {
532 os << "Object loaded from instance variable";
533 } else {
534 generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os);
535 }
536
537 PathDiagnosticLocation Pos(S, SM, N->getLocationContext());
538 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
539 }
540
541 // Gather up the effects that were performed on the object at this
542 // program point
543 bool DeallocSent = false;
544
545 const ProgramPointTag *Tag = N->getLocation().getTag();
546
547 if (Tag == &RetainCountChecker::getCastFailTag()) {
548 os << "Assuming dynamic cast returns null due to type mismatch";
549 }
550
551 if (Tag == &RetainCountChecker::getDeallocSentTag()) {
552 // We only have summaries attached to nodes after evaluating CallExpr and
553 // ObjCMessageExprs.
554 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
555
556 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
557 // Iterate through the parameter expressions and see if the symbol
558 // was ever passed as an argument.
559 unsigned i = 0;
560
561 for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) {
562
563 // Retrieve the value of the argument. Is it the symbol
564 // we are interested in?
565 if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)
566 continue;
567
568 // We have an argument. Get the effect!
569 DeallocSent = true;
570 }
571 } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
572 if (const Expr *receiver = ME->getInstanceReceiver()) {
573 if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
574 .getAsLocSymbol() == Sym) {
575 // The symbol we are tracking is the receiver.
576 DeallocSent = true;
577 }
578 }
579 }
580 }
581
582 if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent))
583 return nullptr;
584
585 if (os.str().empty())
586 return nullptr; // We have nothing to say!
587
588 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
589 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
590 N->getLocationContext());
591 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
592
593 // Add the range by scanning the children of the statement for any bindings
594 // to Sym.
595 for (const Stmt *Child : S->children())
596 if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))
597 if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {
598 P->addRange(Exp->getSourceRange());
599 break;
600 }
601
602 return std::move(P);
603 }
604
describeRegion(const MemRegion * MR)605 static Optional<std::string> describeRegion(const MemRegion *MR) {
606 if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
607 return std::string(VR->getDecl()->getName());
608 // Once we support more storage locations for bindings,
609 // this would need to be improved.
610 return None;
611 }
612
613 using Bindings = llvm::SmallVector<std::pair<const MemRegion *, SVal>, 4>;
614
615 class VarBindingsCollector : public StoreManager::BindingsHandler {
616 SymbolRef Sym;
617 Bindings &Result;
618
619 public:
VarBindingsCollector(SymbolRef Sym,Bindings & ToFill)620 VarBindingsCollector(SymbolRef Sym, Bindings &ToFill)
621 : Sym(Sym), Result(ToFill) {}
622
HandleBinding(StoreManager & SMgr,Store Store,const MemRegion * R,SVal Val)623 bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *R,
624 SVal Val) override {
625 SymbolRef SymV = Val.getAsLocSymbol();
626 if (!SymV || SymV != Sym)
627 return true;
628
629 if (isa<NonParamVarRegion>(R))
630 Result.emplace_back(R, Val);
631
632 return true;
633 }
634 };
635
getAllVarBindingsForSymbol(ProgramStateManager & Manager,const ExplodedNode * Node,SymbolRef Sym)636 Bindings getAllVarBindingsForSymbol(ProgramStateManager &Manager,
637 const ExplodedNode *Node, SymbolRef Sym) {
638 Bindings Result;
639 VarBindingsCollector Collector{Sym, Result};
640 while (Result.empty() && Node) {
641 Manager.iterBindings(Node->getState(), Collector);
642 Node = Node->getFirstPred();
643 }
644
645 return Result;
646 }
647
648 namespace {
649 // Find the first node in the current function context that referred to the
650 // tracked symbol and the memory location that value was stored to. Note, the
651 // value is only reported if the allocation occurred in the same function as
652 // the leak. The function can also return a location context, which should be
653 // treated as interesting.
654 struct AllocationInfo {
655 const ExplodedNode* N;
656 const MemRegion *R;
657 const LocationContext *InterestingMethodContext;
AllocationInfo__anon7d797ed20111::AllocationInfo658 AllocationInfo(const ExplodedNode *InN,
659 const MemRegion *InR,
660 const LocationContext *InInterestingMethodContext) :
661 N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
662 };
663 } // end anonymous namespace
664
GetAllocationSite(ProgramStateManager & StateMgr,const ExplodedNode * N,SymbolRef Sym)665 static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
666 const ExplodedNode *N, SymbolRef Sym) {
667 const ExplodedNode *AllocationNode = N;
668 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
669 const MemRegion *FirstBinding = nullptr;
670 const LocationContext *LeakContext = N->getLocationContext();
671
672 // The location context of the init method called on the leaked object, if
673 // available.
674 const LocationContext *InitMethodContext = nullptr;
675
676 while (N) {
677 ProgramStateRef St = N->getState();
678 const LocationContext *NContext = N->getLocationContext();
679
680 if (!getRefBinding(St, Sym))
681 break;
682
683 StoreManager::FindUniqueBinding FB(Sym);
684 StateMgr.iterBindings(St, FB);
685
686 if (FB) {
687 const MemRegion *R = FB.getRegion();
688 // Do not show local variables belonging to a function other than
689 // where the error is reported.
690 if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace()))
691 if (MR->getStackFrame() == LeakContext->getStackFrame())
692 FirstBinding = R;
693 }
694
695 // AllocationNode is the last node in which the symbol was tracked.
696 AllocationNode = N;
697
698 // AllocationNodeInCurrentContext, is the last node in the current or
699 // parent context in which the symbol was tracked.
700 //
701 // Note that the allocation site might be in the parent context. For example,
702 // the case where an allocation happens in a block that captures a reference
703 // to it and that reference is overwritten/dropped by another call to
704 // the block.
705 if (NContext == LeakContext || NContext->isParentOf(LeakContext))
706 AllocationNodeInCurrentOrParentContext = N;
707
708 // Find the last init that was called on the given symbol and store the
709 // init method's location context.
710 if (!InitMethodContext)
711 if (auto CEP = N->getLocation().getAs<CallEnter>()) {
712 const Stmt *CE = CEP->getCallExpr();
713 if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
714 const Stmt *RecExpr = ME->getInstanceReceiver();
715 if (RecExpr) {
716 SVal RecV = St->getSVal(RecExpr, NContext);
717 if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
718 InitMethodContext = CEP->getCalleeContext();
719 }
720 }
721 }
722
723 N = N->getFirstPred();
724 }
725
726 // If we are reporting a leak of the object that was allocated with alloc,
727 // mark its init method as interesting.
728 const LocationContext *InterestingMethodContext = nullptr;
729 if (InitMethodContext) {
730 const ProgramPoint AllocPP = AllocationNode->getLocation();
731 if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
732 if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
733 if (ME->getMethodFamily() == OMF_alloc)
734 InterestingMethodContext = InitMethodContext;
735 }
736
737 // If allocation happened in a function different from the leak node context,
738 // do not report the binding.
739 assert(N && "Could not find allocation node");
740
741 if (AllocationNodeInCurrentOrParentContext &&
742 AllocationNodeInCurrentOrParentContext->getLocationContext() !=
743 LeakContext)
744 FirstBinding = nullptr;
745
746 return AllocationInfo(AllocationNodeInCurrentOrParentContext, FirstBinding,
747 InterestingMethodContext);
748 }
749
750 PathDiagnosticPieceRef
getEndPath(BugReporterContext & BRC,const ExplodedNode * EndN,PathSensitiveBugReport & BR)751 RefCountReportVisitor::getEndPath(BugReporterContext &BRC,
752 const ExplodedNode *EndN,
753 PathSensitiveBugReport &BR) {
754 BR.markInteresting(Sym);
755 return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
756 }
757
758 PathDiagnosticPieceRef
getEndPath(BugReporterContext & BRC,const ExplodedNode * EndN,PathSensitiveBugReport & BR)759 RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
760 const ExplodedNode *EndN,
761 PathSensitiveBugReport &BR) {
762
763 // Tell the BugReporterContext to report cases when the tracked symbol is
764 // assigned to different variables, etc.
765 BR.markInteresting(Sym);
766
767 PathDiagnosticLocation L = cast<RefLeakReport>(BR).getEndOfPath();
768
769 std::string sbuf;
770 llvm::raw_string_ostream os(sbuf);
771
772 os << "Object leaked: ";
773
774 Optional<std::string> RegionDescription = describeRegion(LastBinding);
775 if (RegionDescription) {
776 os << "object allocated and stored into '" << *RegionDescription << '\'';
777 } else {
778 os << "allocated object of type '" << getPrettyTypeName(Sym->getType())
779 << "'";
780 }
781
782 // Get the retain count.
783 const RefVal *RV = getRefBinding(EndN->getState(), Sym);
784 assert(RV);
785
786 if (RV->getKind() == RefVal::ErrorLeakReturned) {
787 // FIXME: Per comments in rdar://6320065, "create" only applies to CF
788 // objects. Only "copy", "alloc", "retain" and "new" transfer ownership
789 // to the caller for NS objects.
790 const Decl *D = &EndN->getCodeDecl();
791
792 os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
793 : " is returned from a function ");
794
795 if (D->hasAttr<CFReturnsNotRetainedAttr>()) {
796 os << "that is annotated as CF_RETURNS_NOT_RETAINED";
797 } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) {
798 os << "that is annotated as NS_RETURNS_NOT_RETAINED";
799 } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {
800 os << "that is annotated as OS_RETURNS_NOT_RETAINED";
801 } else {
802 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
803 if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) {
804 os << "managed by Automatic Reference Counting";
805 } else {
806 os << "whose name ('" << MD->getSelector().getAsString()
807 << "') does not start with "
808 "'copy', 'mutableCopy', 'alloc' or 'new'."
809 " This violates the naming convention rules"
810 " given in the Memory Management Guide for Cocoa";
811 }
812 } else {
813 const FunctionDecl *FD = cast<FunctionDecl>(D);
814 ObjKind K = RV->getObjKind();
815 if (K == ObjKind::ObjC || K == ObjKind::CF) {
816 os << "whose name ('" << *FD
817 << "') does not contain 'Copy' or 'Create'. This violates the "
818 "naming"
819 " convention rules given in the Memory Management Guide for "
820 "Core"
821 " Foundation";
822 } else if (RV->getObjKind() == ObjKind::OS) {
823 std::string FuncName = FD->getNameAsString();
824 os << "whose name ('" << FuncName << "') starts with '"
825 << StringRef(FuncName).substr(0, 3) << "'";
826 }
827 }
828 }
829 } else {
830 os << " is not referenced later in this execution path and has a retain "
831 "count of +"
832 << RV->getCount();
833 }
834
835 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
836 }
837
RefCountReport(const RefCountBug & D,const LangOptions & LOpts,ExplodedNode * n,SymbolRef sym,bool isLeak)838 RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
839 ExplodedNode *n, SymbolRef sym, bool isLeak)
840 : PathSensitiveBugReport(D, D.getDescription(), n), Sym(sym),
841 isLeak(isLeak) {
842 if (!isLeak)
843 addVisitor<RefCountReportVisitor>(sym);
844 }
845
RefCountReport(const RefCountBug & D,const LangOptions & LOpts,ExplodedNode * n,SymbolRef sym,StringRef endText)846 RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
847 ExplodedNode *n, SymbolRef sym,
848 StringRef endText)
849 : PathSensitiveBugReport(D, D.getDescription(), endText, n) {
850
851 addVisitor<RefCountReportVisitor>(sym);
852 }
853
deriveParamLocation(CheckerContext & Ctx)854 void RefLeakReport::deriveParamLocation(CheckerContext &Ctx) {
855 const SourceManager &SMgr = Ctx.getSourceManager();
856
857 if (!Sym->getOriginRegion())
858 return;
859
860 auto *Region = dyn_cast<DeclRegion>(Sym->getOriginRegion());
861 if (Region) {
862 const Decl *PDecl = Region->getDecl();
863 if (isa_and_nonnull<ParmVarDecl>(PDecl)) {
864 PathDiagnosticLocation ParamLocation =
865 PathDiagnosticLocation::create(PDecl, SMgr);
866 Location = ParamLocation;
867 UniqueingLocation = ParamLocation;
868 UniqueingDecl = Ctx.getLocationContext()->getDecl();
869 }
870 }
871 }
872
deriveAllocLocation(CheckerContext & Ctx)873 void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx) {
874 // Most bug reports are cached at the location where they occurred.
875 // With leaks, we want to unique them by the location where they were
876 // allocated, and only report a single path. To do this, we need to find
877 // the allocation site of a piece of tracked memory, which we do via a
878 // call to GetAllocationSite. This will walk the ExplodedGraph backwards.
879 // Note that this is *not* the trimmed graph; we are guaranteed, however,
880 // that all ancestor nodes that represent the allocation site have the
881 // same SourceLocation.
882 const ExplodedNode *AllocNode = nullptr;
883
884 const SourceManager &SMgr = Ctx.getSourceManager();
885
886 AllocationInfo AllocI =
887 GetAllocationSite(Ctx.getStateManager(), getErrorNode(), Sym);
888
889 AllocNode = AllocI.N;
890 AllocFirstBinding = AllocI.R;
891 markInteresting(AllocI.InterestingMethodContext);
892
893 // Get the SourceLocation for the allocation site.
894 // FIXME: This will crash the analyzer if an allocation comes from an
895 // implicit call (ex: a destructor call).
896 // (Currently there are no such allocations in Cocoa, though.)
897 AllocStmt = AllocNode->getStmtForDiagnostics();
898
899 if (!AllocStmt) {
900 AllocFirstBinding = nullptr;
901 return;
902 }
903
904 PathDiagnosticLocation AllocLocation = PathDiagnosticLocation::createBegin(
905 AllocStmt, SMgr, AllocNode->getLocationContext());
906 Location = AllocLocation;
907
908 // Set uniqieing info, which will be used for unique the bug reports. The
909 // leaks should be uniqued on the allocation site.
910 UniqueingLocation = AllocLocation;
911 UniqueingDecl = AllocNode->getLocationContext()->getDecl();
912 }
913
createDescription(CheckerContext & Ctx)914 void RefLeakReport::createDescription(CheckerContext &Ctx) {
915 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
916 Description.clear();
917 llvm::raw_string_ostream os(Description);
918 os << "Potential leak of an object";
919
920 Optional<std::string> RegionDescription =
921 describeRegion(AllocBindingToReport);
922 if (RegionDescription) {
923 os << " stored into '" << *RegionDescription << '\'';
924 } else {
925
926 // If we can't figure out the name, just supply the type information.
927 os << " of type '" << getPrettyTypeName(Sym->getType()) << "'";
928 }
929 }
930
findBindingToReport(CheckerContext & Ctx,ExplodedNode * Node)931 void RefLeakReport::findBindingToReport(CheckerContext &Ctx,
932 ExplodedNode *Node) {
933 if (!AllocFirstBinding)
934 // If we don't have any bindings, we won't be able to find any
935 // better binding to report.
936 return;
937
938 // If the original region still contains the leaking symbol...
939 if (Node->getState()->getSVal(AllocFirstBinding).getAsSymbol() == Sym) {
940 // ...it is the best binding to report.
941 AllocBindingToReport = AllocFirstBinding;
942 return;
943 }
944
945 // At this point, we know that the original region doesn't contain the leaking
946 // when the actual leak happens. It means that it can be confusing for the
947 // user to see such description in the message.
948 //
949 // Let's consider the following example:
950 // Object *Original = allocate(...);
951 // Object *New = Original;
952 // Original = allocate(...);
953 // Original->release();
954 //
955 // Complaining about a leaking object "stored into Original" might cause a
956 // rightful confusion because 'Original' is actually released.
957 // We should complain about 'New' instead.
958 Bindings AllVarBindings =
959 getAllVarBindingsForSymbol(Ctx.getStateManager(), Node, Sym);
960
961 // While looking for the last var bindings, we can still find
962 // `AllocFirstBinding` to be one of them. In situations like this,
963 // it would still be the easiest case to explain to our users.
964 if (!AllVarBindings.empty() &&
965 llvm::count_if(AllVarBindings,
966 [this](const std::pair<const MemRegion *, SVal> Binding) {
967 return Binding.first == AllocFirstBinding;
968 }) == 0) {
969 // Let's pick one of them at random (if there is something to pick from).
970 AllocBindingToReport = AllVarBindings[0].first;
971
972 // Because 'AllocBindingToReport' is not the the same as
973 // 'AllocFirstBinding', we need to explain how the leaking object
974 // got from one to another.
975 //
976 // NOTE: We use the actual SVal stored in AllocBindingToReport here because
977 // trackStoredValue compares SVal's and it can get trickier for
978 // something like derived regions if we want to construct SVal from
979 // Sym. Instead, we take the value that is definitely stored in that
980 // region, thus guaranteeing that trackStoredValue will work.
981 bugreporter::trackStoredValue(AllVarBindings[0].second.castAs<KnownSVal>(),
982 AllocBindingToReport, *this);
983 } else {
984 AllocBindingToReport = AllocFirstBinding;
985 }
986 }
987
RefLeakReport(const RefCountBug & D,const LangOptions & LOpts,ExplodedNode * N,SymbolRef Sym,CheckerContext & Ctx)988 RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts,
989 ExplodedNode *N, SymbolRef Sym,
990 CheckerContext &Ctx)
991 : RefCountReport(D, LOpts, N, Sym, /*isLeak=*/true) {
992
993 deriveAllocLocation(Ctx);
994 findBindingToReport(Ctx, N);
995
996 if (!AllocFirstBinding)
997 deriveParamLocation(Ctx);
998
999 createDescription(Ctx);
1000
1001 addVisitor<RefLeakReportVisitor>(Sym, AllocBindingToReport);
1002 }
1003