clang 24.0.0git
RetainCountDiagnostics.cpp
Go to the documentation of this file.
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
15#include "RetainCountChecker.h"
16#include "llvm/ADT/STLExtras.h"
17#include "llvm/ADT/SmallVector.h"
18#include <optional>
19
20using namespace clang;
21using namespace ento;
22using namespace retaincountchecker;
23
24static bool isNumericLiteralExpression(const Expr *E) {
25 // FIXME: This set of cases was copied from SemaExprObjC.
28}
29
30/// If type represents a pointer to CXXRecordDecl,
31/// and is not a typedef, return the decl name.
32/// Otherwise, return the serialization of type.
33static std::string getPrettyTypeName(QualType QT) {
34 QualType PT = QT->getPointeeType();
35 if (!PT.isNull() && !QT->getAs<TypedefType>())
36 if (const auto *RD = PT->getAsCXXRecordDecl())
37 return std::string(RD->getName());
38 return QT.getAsString();
39}
40
41/// Write information about the type state change to @c os,
42/// return whether the note should be generated.
43static bool shouldGenerateNote(llvm::raw_string_ostream &os,
44 const RefVal *PrevT,
45 const RefVal &CurrV,
46 bool DeallocSent) {
47 // Get the previous type state.
48 RefVal PrevV = *PrevT;
49
50 // Specially handle -dealloc.
51 if (DeallocSent) {
52 // Determine if the object's reference count was pushed to zero.
53 assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");
54 // We may not have transitioned to 'release' if we hit an error.
55 // This case is handled elsewhere.
56 if (CurrV.getKind() == RefVal::Released) {
57 assert(CurrV.getCombinedCounts() == 0);
58 os << "Object released by directly sending the '-dealloc' message";
59 return true;
60 }
61 }
62
63 // Determine if the typestate has changed.
64 if (!PrevV.hasSameState(CurrV))
65 switch (CurrV.getKind()) {
66 case RefVal::Owned:
68 if (PrevV.getCount() == CurrV.getCount()) {
69 // Did an autorelease message get sent?
70 if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())
71 return false;
72
73 assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
74 os << "Object autoreleased";
75 return true;
76 }
77
78 if (PrevV.getCount() > CurrV.getCount())
79 os << "Reference count decremented";
80 else
81 os << "Reference count incremented";
82
83 if (unsigned Count = CurrV.getCount())
84 os << "; the object now has a +" << Count << " retain count";
85
86 return true;
87
89 if (CurrV.getIvarAccessHistory() ==
90 RefVal::IvarAccessHistory::ReleasedAfterDirectAccess &&
91 CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) {
92 os << "Strong instance variable relinquished; object released";
93 } else {
94 os << "Object released";
95 }
96 return true;
97
99 // Autoreleases can be applied after marking a node ReturnedOwned.
100 if (CurrV.getAutoreleaseCount())
101 return false;
102
103 os << "Object returned to caller as an owning reference (single "
104 "retain count transferred to caller)";
105 return true;
106
108 os << "Object returned to caller with a +0 retain count";
109 return true;
110
111 default:
112 return false;
113 }
114 return true;
115}
116
117/// Finds argument index of the out paramter in the call @c S
118/// corresponding to the symbol @c Sym.
119/// If none found, returns std::nullopt.
120static std::optional<unsigned>
122 std::optional<CallEventRef<>> CE) {
123 if (!CE)
124 return std::nullopt;
125
126 for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++)
127 if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())
128 if (const auto *TR = dyn_cast<TypedValueRegion>(MR))
129 if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymbol() == Sym)
130 return Idx;
131
132 return std::nullopt;
133}
134
135static std::optional<std::string> findMetaClassAlloc(const Expr *Callee) {
136 if (const auto *ME = dyn_cast<MemberExpr>(Callee)) {
137 if (ME->getMemberDecl()->getNameAsString() != "alloc")
138 return std::nullopt;
139 const Expr *This = ME->getBase()->IgnoreParenImpCasts();
140 if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) {
141 const ValueDecl *VD = DRE->getDecl();
142 if (VD->getNameAsString() != "metaClass")
143 return std::nullopt;
144
145 if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext()))
146 return RD->getNameAsString();
147
148 }
149 }
150 return std::nullopt;
151}
152
153static std::string findAllocatedObjectName(const Stmt *S, QualType QT) {
154 if (const auto *CE = dyn_cast<CallExpr>(S))
155 if (auto Out = findMetaClassAlloc(CE->getCallee()))
156 return *Out;
157 return getPrettyTypeName(QT);
158}
159
161 const StackFrame *SF,
162 const RefVal &CurrV, SymbolRef &Sym,
163 const Stmt *S,
164 llvm::raw_string_ostream &os) {
165 CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
166 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
167 // Get the name of the callee (if it is available)
168 // from the tracked SVal.
169 SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), SF);
170 const FunctionDecl *FD = X.getAsFunctionDecl();
171
172 // If failed, try to get it from AST.
173 if (!FD)
174 FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
175
176 if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) {
177 os << "Call to method '" << MD->getQualifiedNameAsString() << '\'';
178 } else if (FD) {
179 os << "Call to function '" << FD->getQualifiedNameAsString() << '\'';
180 } else {
181 os << "function call";
182 }
183 } else if (isa<CXXNewExpr>(S)) {
184 os << "Operator 'new'";
185 } else {
186 assert(isa<ObjCMessageExpr>(S));
188 cast<ObjCMessageExpr>(S), CurrSt, SF, {nullptr, 0});
189
190 switch (Call->getMessageKind()) {
191 case OCM_Message:
192 os << "Method";
193 break;
195 os << "Property";
196 break;
197 case OCM_Subscript:
198 os << "Subscript";
199 break;
200 }
201 }
202
203 std::optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, SF, {nullptr, 0});
204 auto Idx = findArgIdxOfSymbol(CurrSt, SF, Sym, CE);
205
206 // If index is not found, we assume that the symbol was returned.
207 if (!Idx) {
208 os << " returns ";
209 } else {
210 os << " writes ";
211 }
212
213 if (CurrV.getObjKind() == ObjKind::CF) {
214 os << "a Core Foundation object of type '" << Sym->getType() << "' with a ";
215 } else if (CurrV.getObjKind() == ObjKind::OS) {
216 os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType())
217 << "' with a ";
218 } else if (CurrV.getObjKind() == ObjKind::Generalized) {
219 os << "an object of type '" << Sym->getType() << "' with a ";
220 } else {
221 assert(CurrV.getObjKind() == ObjKind::ObjC);
222 QualType T = Sym->getType();
224 os << "an Objective-C object with a ";
225 } else {
227 os << "an instance of " << PT->getPointeeType() << " with a ";
228 }
229 }
230
231 if (CurrV.isOwned()) {
232 os << "+1 retain count";
233 } else {
234 assert(CurrV.isNotOwned());
235 os << "+0 retain count";
236 }
237
238 if (Idx) {
239 os << " into an out parameter '";
240 const ParmVarDecl *PVD = (*CE)->parameters()[*Idx];
242 /*Qualified=*/false);
243 os << "'";
244
245 QualType RT = (*CE)->getResultType();
246 if (!RT.isNull() && !RT->isVoidType()) {
247 SVal RV = (*CE)->getReturnValue();
248 if (CurrSt->isNull(RV).isConstrainedTrue()) {
249 os << " (assuming the call returns zero)";
250 } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) {
251 os << " (assuming the call returns non-zero)";
252 }
253
254 }
255 }
256}
257
258namespace clang {
259namespace ento {
260namespace retaincountchecker {
261
263protected:
266
267public:
269 : Sym(S), IsReleaseUnowned(IRU) {}
270
271 void Profile(llvm::FoldingSetNodeID &ID) const override {
272 static int x = 0;
273 ID.AddPointer(&x);
274 ID.AddPointer(Sym);
275 }
276
279 PathSensitiveBugReport &BR) override;
280
283 PathSensitiveBugReport &BR) override;
284};
285
287public:
289 : RefCountReportVisitor(Sym, /*IsReleaseUnowned=*/false),
290 LastBinding(LastBinding) {}
291
294 PathSensitiveBugReport &BR) override;
295
296private:
297 const MemRegion *LastBinding;
298};
299
300} // end namespace retaincountchecker
301} // end namespace ento
302} // end namespace clang
303
304
305/// Find the first node with the parent stack frame.
306static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) {
307 const StackFrame *SF = Pred->getStackFrame();
308 if (SF->inTopFrame())
309 return nullptr;
310 const StackFrame *PSF = SF->getParent();
311 if (!PSF)
312 return nullptr;
313
314 const ExplodedNode *N = Pred;
315 while (N && N->getStackFrame() != PSF) {
316 N = N->getFirstPred();
317 }
318 return N;
319}
320
321
322/// Insert a diagnostic piece at function exit
323/// if a function parameter is annotated as "os_consumed",
324/// but it does not actually consume the reference.
325static std::shared_ptr<PathDiagnosticEventPiece>
327 CallExitBegin &CallExitLoc,
328 const SourceManager &SM,
329 CallEventManager &CEMgr) {
330
331 const ExplodedNode *CN = getCalleeNode(N);
332 if (!CN)
333 return nullptr;
334
336
337 std::string sbuf;
338 llvm::raw_string_ostream os(sbuf);
339 ArrayRef<const ParmVarDecl *> Parameters = Call->parameters();
340 for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {
341 const ParmVarDecl *PVD = Parameters[I];
342
343 if (!PVD->hasAttr<OSConsumedAttr>())
344 continue;
345
346 if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
347 const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR);
348 const RefVal *CountAtExit = getRefBinding(N->getState(), SR);
349
350 if (!CountBeforeCall || !CountAtExit)
351 continue;
352
353 unsigned CountBefore = CountBeforeCall->getCount();
354 unsigned CountAfter = CountAtExit->getCount();
355
356 bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
357 if (!AsExpected) {
358 os << "Parameter '";
360 /*Qualified=*/false);
361 os << "' is marked as consuming, but the function did not consume "
362 << "the reference\n";
363 }
364 }
365 }
366
367 if (sbuf.empty())
368 return nullptr;
369
371 return std::make_shared<PathDiagnosticEventPiece>(L, sbuf);
372}
373
374/// Annotate the parameter at the analysis entry point.
375static std::shared_ptr<PathDiagnosticEventPiece>
377 const SourceManager &SM) {
378 auto PP = N->getLocationAs<BlockEdge>();
379 if (!PP)
380 return nullptr;
381
382 const CFGBlock *Src = PP->getSrc();
383 const RefVal *CurrT = getRefBinding(N->getState(), Sym);
384
385 if (&Src->getParent()->getEntry() != Src || !CurrT ||
386 getRefBinding(N->getFirstPred()->getState(), Sym))
387 return nullptr;
388
389 const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion());
390 const auto *PVD = cast<ParmVarDecl>(VR->getDecl());
392
393 std::string s;
394 llvm::raw_string_ostream os(s);
395 os << "Parameter '" << PVD->getDeclName() << "' starts at +";
396 if (CurrT->getCount() == 1) {
397 os << "1, as it is marked as consuming";
398 } else {
399 assert(CurrT->getCount() == 0);
400 os << "0";
401 }
402 return std::make_shared<PathDiagnosticEventPiece>(L, s);
403}
404
408 const SourceManager &SM = BRC.getSourceManager();
410 if (auto CE = N->getLocationAs<CallExitBegin>())
411 if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
412 return PD;
413
414 if (auto PD = annotateStartParameter(N, Sym, SM))
415 return PD;
416
417 // FIXME: We will eventually need to handle non-statement-based events
418 // (__attribute__((cleanup))).
419 if (!N->getLocation().getAs<StmtPoint>())
420 return nullptr;
421
422 // Check if the type state has changed.
423 const ExplodedNode *PrevNode = N->getFirstPred();
424 ProgramStateRef PrevSt = PrevNode->getState();
425 ProgramStateRef CurrSt = N->getState();
426 const StackFrame *SF = N->getStackFrame();
427
428 const RefVal* CurrT = getRefBinding(CurrSt, Sym);
429 if (!CurrT)
430 return nullptr;
431
432 const RefVal &CurrV = *CurrT;
433 const RefVal *PrevT = getRefBinding(PrevSt, Sym);
434
435 // Create a string buffer to constain all the useful things we want
436 // to tell the user.
437 std::string sbuf;
438 llvm::raw_string_ostream os(sbuf);
439
440 if (PrevT && IsReleaseUnowned && CurrV.isNotOwned() && PrevT->isOwned()) {
441 os << "Object is now not exclusively owned";
442 auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM);
443 return std::make_shared<PathDiagnosticEventPiece>(Pos, sbuf);
444 }
445
446 // This is the allocation site since the previous node had no bindings
447 // for this symbol.
448 if (!PrevT) {
449 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
450
452 S = SF->getCallSite();
453 }
454
455 if (isa<ObjCArrayLiteral>(S)) {
456 os << "NSArray literal is an object with a +0 retain count";
457 } else if (isa<ObjCDictionaryLiteral>(S)) {
458 os << "NSDictionary literal is an object with a +0 retain count";
459 } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
460 if (isNumericLiteralExpression(BL->getSubExpr()))
461 os << "NSNumber literal is an object with a +0 retain count";
462 else {
463 const ObjCInterfaceDecl *BoxClass = nullptr;
464 if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
465 BoxClass = Method->getClassInterface();
466
467 // We should always be able to find the boxing class interface,
468 // but consider this future-proofing.
469 if (BoxClass) {
470 os << *BoxClass << " b";
471 } else {
472 os << "B";
473 }
474
475 os << "oxed expression produces an object with a +0 retain count";
476 }
477 } else if (isa<ObjCIvarRefExpr>(S)) {
478 os << "Object loaded from instance variable";
479 } else {
480 generateDiagnosticsForCallLike(CurrSt, SF, CurrV, Sym, S, os);
481 }
482
483 PathDiagnosticLocation Pos(S, SM, N->getStackFrame());
484 return std::make_shared<PathDiagnosticEventPiece>(Pos, sbuf);
485 }
486
487 // Gather up the effects that were performed on the object at this
488 // program point
489 bool DeallocSent = false;
490
491 const ProgramPointTag *Tag = N->getLocation().getTag();
492
494 os << "Assuming dynamic cast returns null due to type mismatch";
495 }
496
498 // We only have summaries attached to nodes after evaluating CallExpr and
499 // ObjCMessageExprs.
500 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
501
502 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
503 // Iterate through the parameter expressions and see if the symbol
504 // was ever passed as an argument.
505 for (auto AI = CE->arg_begin(), AE = CE->arg_end(); AI != AE; ++AI) {
506
507 // Retrieve the value of the argument. Is it the symbol
508 // we are interested in?
509 if (CurrSt->getSValAsScalarOrLoc(*AI, SF).getAsLocSymbol() != Sym)
510 continue;
511
512 // We have an argument. Get the effect!
513 DeallocSent = true;
514 }
515 } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
516 if (const Expr *R = ME->getInstanceReceiver()) {
517 if (CurrSt->getSValAsScalarOrLoc(R, SF).getAsLocSymbol() == Sym) {
518 // The symbol we are tracking is the receiver.
519 DeallocSent = true;
520 }
521 }
522 }
523 }
524
525 if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent))
526 return nullptr;
527
528 if (sbuf.empty())
529 return nullptr; // We have nothing to say!
530
531 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
533 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, sbuf);
534
535 // Add the range by scanning the children of the statement for any bindings
536 // to Sym.
537 for (const Stmt *Child : S->children())
538 if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))
539 if (CurrSt->getSValAsScalarOrLoc(Exp, SF).getAsLocSymbol() == Sym) {
540 P->addRange(Exp->getSourceRange());
541 break;
542 }
543
544 return std::move(P);
545}
546
547static std::optional<std::string> describeRegion(const MemRegion *MR) {
548 if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
549 return std::string(VR->getDecl()->getName());
550 // Once we support more storage locations for bindings,
551 // this would need to be improved.
552 return std::nullopt;
553}
554
556
557namespace {
558class VarBindingsCollector : public StoreManager::BindingsHandler {
559 SymbolRef Sym;
561
562public:
563 VarBindingsCollector(SymbolRef Sym, Bindings &ToFill)
564 : Sym(Sym), Result(ToFill) {}
565
566 bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *R,
567 SVal Val) override {
568 SymbolRef SymV = Val.getAsLocSymbol();
569 if (!SymV || SymV != Sym)
570 return true;
571
573 Result.emplace_back(R, Val);
574
575 return true;
576 }
577};
578} // namespace
579
581 const ExplodedNode *Node,
582 SymbolRef Sym) {
584 VarBindingsCollector Collector{Sym, Result};
585 while (Result.empty() && Node) {
586 Manager.iterBindings(Node->getState(), Collector);
587 Node = Node->getFirstPred();
588 }
589
590 return Result;
591}
592
593namespace {
594// Find the first node in the current function context that referred to the
595// tracked symbol and the memory location that value was stored to. Note, the
596// value is only reported if the allocation occurred in the same function as
597// the leak. The function can also return a stack frame, which should be
598// treated as interesting.
599struct AllocationInfo {
600 const ExplodedNode* N;
601 const MemRegion *R;
602 const StackFrame *InterestingMethodStackFrame;
603 AllocationInfo(const ExplodedNode *InN, const MemRegion *InR,
604 const StackFrame *InterestingMethodStackFrame)
605 : N(InN), R(InR),
606 InterestingMethodStackFrame(InterestingMethodStackFrame) {}
607};
608} // end anonymous namespace
609
610static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
611 const ExplodedNode *N, SymbolRef Sym) {
612 const ExplodedNode *AllocationNode = N;
613 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
614 const MemRegion *FirstBinding = nullptr;
615 const StackFrame *LeakStackFrame = N->getStackFrame();
616
617 // The stack frame of the init method called on the leaked object, if
618 // available.
619 const StackFrame *InitMethodStackFrame = nullptr;
620
621 while (N) {
622 ProgramStateRef St = N->getState();
623 const StackFrame *NStackFrame = N->getStackFrame();
624
625 if (!getRefBinding(St, Sym))
626 break;
627
629 StateMgr.iterBindings(St, FB);
630
631 if (FB) {
632 const MemRegion *R = FB.getRegion();
633 // Do not show local variables belonging to a function other than
634 // where the error is reported.
635 if (const auto *MR = R->getMemorySpaceAs<StackSpaceRegion>(St))
636 if (MR->getStackFrame() == LeakStackFrame)
637 FirstBinding = R;
638 }
639
640 // AllocationNode is the last node in which the symbol was tracked.
641 AllocationNode = N;
642
643 // AllocationNodeInCurrentContext, is the last node in the current or
644 // parent context in which the symbol was tracked.
645 //
646 // Note that the allocation site might be in the parent context. For example,
647 // the case where an allocation happens in a block that captures a reference
648 // to it and that reference is overwritten/dropped by another call to
649 // the block.
650 if (NStackFrame == LeakStackFrame ||
651 NStackFrame->isParentOf(LeakStackFrame))
652 AllocationNodeInCurrentOrParentContext = N;
653
654 // Find the last init that was called on the given symbol and store the
655 // init method's stack frame.
656 if (!InitMethodStackFrame)
657 if (auto CEP = N->getLocation().getAs<CallEnter>()) {
658 const Stmt *CE = CEP->getCallExpr();
659 if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
660 if (const Expr *RecExpr = ME->getInstanceReceiver()) {
661 SVal RecV = St->getSVal(RecExpr, NStackFrame);
662 if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
663 InitMethodStackFrame = CEP->getCalleeStackFrame();
664 }
665 }
666 }
667
668 N = N->getFirstPred();
669 }
670
671 // If we are reporting a leak of the object that was allocated with alloc,
672 // mark its init method as interesting.
673 const StackFrame *InterestingMethodStackFrame = nullptr;
674 if (InitMethodStackFrame) {
675 const ProgramPoint AllocPP = AllocationNode->getLocation();
676 if (std::optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
677 if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
678 if (ME->getMethodFamily() == OMF_alloc)
679 InterestingMethodStackFrame = InitMethodStackFrame;
680 }
681
682 // If allocation happened in a function different from the leak node context,
683 // do not report the binding.
684 assert(N && "Could not find allocation node");
685
686 if (AllocationNodeInCurrentOrParentContext &&
687 AllocationNodeInCurrentOrParentContext->getStackFrame() != LeakStackFrame)
688 FirstBinding = nullptr;
689
690 return AllocationInfo(AllocationNodeInCurrentOrParentContext, FirstBinding,
691 InterestingMethodStackFrame);
692}
693
701
706
707 // Tell the BugReporterContext to report cases when the tracked symbol is
708 // assigned to different variables, etc.
710
711 PathDiagnosticLocation L = cast<RefLeakReport>(BR).getEndOfPath();
712
713 std::string sbuf;
714 llvm::raw_string_ostream os(sbuf);
715
716 os << "Object leaked: ";
717
718 std::optional<std::string> RegionDescription = describeRegion(LastBinding);
719 if (RegionDescription) {
720 os << "object allocated and stored into '" << *RegionDescription << '\'';
721 } else {
722 os << "allocated object of type '" << getPrettyTypeName(Sym->getType())
723 << "'";
724 }
725
726 // Get the retain count.
727 const RefVal *RV = getRefBinding(EndN->getState(), Sym);
728 assert(RV);
729
730 if (RV->getKind() == RefVal::ErrorLeakReturned) {
731 const Decl *D = &EndN->getCodeDecl();
732
733 os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
734 : " is returned from a function ");
735
736 if (D->hasAttr<CFReturnsNotRetainedAttr>()) {
737 os << "that is annotated as CF_RETURNS_NOT_RETAINED";
738 } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) {
739 os << "that is annotated as NS_RETURNS_NOT_RETAINED";
740 } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {
741 os << "that is annotated as OS_RETURNS_NOT_RETAINED";
742 } else {
743 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 " violating the naming convention rules"
751 " given in the Memory Management Guide for Cocoa";
752 }
753 } else {
754 const FunctionDecl *FD = cast<FunctionDecl>(D);
755 ObjKind K = RV->getObjKind();
756 if (K == ObjKind::ObjC || K == ObjKind::CF) {
757 os << "whose name ('" << *FD
758 << "') does not contain 'Copy' or 'Create'; violating the "
759 "naming convention rules given in the Memory Management Guide "
760 "for Core Foundation";
761 } else if (RV->getObjKind() == ObjKind::OS) {
762 std::string FuncName = FD->getNameAsString();
763 os << "whose name ('" << FuncName << "') starts with '"
764 << StringRef(FuncName).substr(0, 3) << "'";
765 }
766 }
767 }
768 } else {
769 os << " is not referenced later in this execution path and has a retain "
770 "count of +"
771 << RV->getCount();
772 }
773
774 return std::make_shared<PathDiagnosticEventPiece>(L, sbuf);
775}
776
778 ExplodedNode *n, SymbolRef sym, bool isLeak,
779 bool IsReleaseUnowned)
780 : PathSensitiveBugReport(D, D.getReportMessage(), n), Sym(sym),
781 isLeak(isLeak) {
782 if (!isLeak)
783 addVisitor<RefCountReportVisitor>(sym, IsReleaseUnowned);
784}
785
787 ExplodedNode *n, SymbolRef sym,
788 StringRef endText)
789 : PathSensitiveBugReport(D, D.getReportMessage(), endText, n) {
790
791 addVisitor<RefCountReportVisitor>(sym, /*IsReleaseUnowned=*/false);
792}
793
794void RefLeakReport::deriveParamLocation(CheckerContext &Ctx) {
795 const SourceManager &SMgr = Ctx.getSourceManager();
796
797 if (!Sym->getOriginRegion())
798 return;
799
800 auto *Region = dyn_cast<DeclRegion>(Sym->getOriginRegion());
801 if (Region) {
802 const Decl *PDecl = Region->getDecl();
803 if (isa_and_nonnull<ParmVarDecl>(PDecl)) {
804 PathDiagnosticLocation ParamLocation =
806 Location = ParamLocation;
807 UniqueingLocation = ParamLocation;
809 }
810 }
811}
812
813void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx) {
814 // Most bug reports are cached at the location where they occurred.
815 // With leaks, we want to unique them by the location where they were
816 // allocated, and only report a single path. To do this, we need to find
817 // the allocation site of a piece of tracked memory, which we do via a
818 // call to GetAllocationSite. This will walk the ExplodedGraph backwards.
819 // Note that this is *not* the trimmed graph; we are guaranteed, however,
820 // that all ancestor nodes that represent the allocation site have the
821 // same SourceLocation.
822 const ExplodedNode *AllocNode = nullptr;
823
824 const SourceManager &SMgr = Ctx.getSourceManager();
825
826 AllocationInfo AllocI =
828
829 AllocNode = AllocI.N;
830 AllocFirstBinding = AllocI.R;
831 markInteresting(AllocI.InterestingMethodStackFrame);
832
833 // Get the SourceLocation for the allocation site.
834 // FIXME: This will crash the analyzer if an allocation comes from an
835 // implicit call (ex: a destructor call).
836 // (Currently there are no such allocations in Cocoa, though.)
837 AllocStmt = AllocNode->getStmtForDiagnostics();
838
839 if (!AllocStmt) {
840 AllocFirstBinding = nullptr;
841 return;
842 }
843
844 PathDiagnosticLocation AllocLocation = PathDiagnosticLocation::createBegin(
845 AllocStmt, SMgr, AllocNode->getStackFrame());
846 Location = AllocLocation;
847
848 // Set uniqieing info, which will be used for unique the bug reports. The
849 // leaks should be uniqued on the allocation site.
850 UniqueingLocation = AllocLocation;
851 UniqueingDecl = AllocNode->getStackFrame()->getDecl();
852}
853
854void RefLeakReport::createDescription(CheckerContext &Ctx) {
855 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
856 Description.clear();
857 llvm::raw_string_ostream os(Description);
858 os << "Potential leak of an object";
859
860 std::optional<std::string> RegionDescription =
861 describeRegion(AllocBindingToReport);
862 if (RegionDescription) {
863 os << " stored into '" << *RegionDescription << '\'';
864 } else {
865
866 // If we can't figure out the name, just supply the type information.
867 os << " of type '" << getPrettyTypeName(Sym->getType()) << "'";
868 }
869}
870
871void RefLeakReport::findBindingToReport(CheckerContext &Ctx,
872 ExplodedNode *Node) {
873 if (!AllocFirstBinding)
874 // If we don't have any bindings, we won't be able to find any
875 // better binding to report.
876 return;
877
878 // If the original region still contains the leaking symbol...
879 if (Node->getState()->getSVal(AllocFirstBinding).getAsSymbol() == Sym) {
880 // ...it is the best binding to report.
881 AllocBindingToReport = AllocFirstBinding;
882 return;
883 }
884
885 // At this point, we know that the original region doesn't contain the leaking
886 // when the actual leak happens. It means that it can be confusing for the
887 // user to see such description in the message.
888 //
889 // Let's consider the following example:
890 // Object *Original = allocate(...);
891 // Object *New = Original;
892 // Original = allocate(...);
893 // Original->release();
894 //
895 // Complaining about a leaking object "stored into Original" might cause a
896 // rightful confusion because 'Original' is actually released.
897 // We should complain about 'New' instead.
898 Bindings AllVarBindings =
900
901 // While looking for the last var bindings, we can still find
902 // `AllocFirstBinding` to be one of them. In situations like this,
903 // it would still be the easiest case to explain to our users.
904 if (!AllVarBindings.empty() &&
905 !llvm::is_contained(llvm::make_first_range(AllVarBindings),
906 AllocFirstBinding)) {
907 // Let's pick one of them at random (if there is something to pick from).
908 AllocBindingToReport = AllVarBindings[0].first;
909
910 // Because 'AllocBindingToReport' is not the same as
911 // 'AllocFirstBinding', we need to explain how the leaking object
912 // got from one to another.
913 //
914 // NOTE: We use the actual SVal stored in AllocBindingToReport here because
915 // trackStoredValue compares SVal's and it can get trickier for
916 // something like derived regions if we want to construct SVal from
917 // Sym. Instead, we take the value that is definitely stored in that
918 // region, thus guaranteeing that trackStoredValue will work.
919 bugreporter::trackStoredValue(AllVarBindings[0].second,
920 AllocBindingToReport, *this);
921 } else {
922 AllocBindingToReport = AllocFirstBinding;
923 }
924}
925
928 CheckerContext &Ctx)
929 : RefCountReport(D, LOpts, N, Sym, /*isLeak=*/true) {
930
931 deriveAllocLocation(Ctx);
932 findBindingToReport(Ctx, N);
933
934 if (!AllocFirstBinding)
935 deriveParamLocation(Ctx);
936
937 createDescription(Ctx);
938
939 addVisitor<RefLeakReportVisitor>(Sym, AllocBindingToReport);
940}
Result
Implement __builtin_bit_cast and related operations.
#define X(type, name)
Definition Value.h:97
static std::shared_ptr< PathDiagnosticEventPiece > annotateStartParameter(const ExplodedNode *N, SymbolRef Sym, const SourceManager &SM)
Annotate the parameter at the analysis entry point.
static std::string getPrettyTypeName(QualType QT)
If type represents a pointer to CXXRecordDecl, and is not a typedef, return the decl name.
static bool shouldGenerateNote(llvm::raw_string_ostream &os, const RefVal *PrevT, const RefVal &CurrV, bool DeallocSent)
Write information about the type state change to os, return whether the note should be generated.
static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, const StackFrame *SF, const RefVal &CurrV, SymbolRef &Sym, const Stmt *S, llvm::raw_string_ostream &os)
static std::optional< unsigned > findArgIdxOfSymbol(ProgramStateRef CurrSt, const StackFrame *SF, SymbolRef &Sym, std::optional< CallEventRef<> > CE)
Finds argument index of the out paramter in the call S corresponding to the symbol Sym.
static bool isNumericLiteralExpression(const Expr *E)
static std::shared_ptr< PathDiagnosticEventPiece > annotateConsumedSummaryMismatch(const ExplodedNode *N, CallExitBegin &CallExitLoc, const SourceManager &SM, CallEventManager &CEMgr)
Insert a diagnostic piece at function exit if a function parameter is annotated as "os_consumed",...
llvm::SmallVector< std::pair< const MemRegion *, SVal >, 4 > Bindings
static std::string findAllocatedObjectName(const Stmt *S, QualType QT)
static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, const ExplodedNode *N, SymbolRef Sym)
static std::optional< std::string > describeRegion(const MemRegion *MR)
static std::optional< std::string > findMetaClassAlloc(const Expr *Callee)
static const ExplodedNode * getCalleeNode(const ExplodedNode *Pred)
Find the first node with the parent stack frame.
static Bindings getAllVarBindingsForSymbol(ProgramStateManager &Manager, const ExplodedNode *Node, SymbolRef Sym)
a trap message and trap category.
const LangOptions & getLangOpts() const
const clang::PrintingPolicy & getPrintingPolicy() const
Definition ASTContext.h:905
Represents a single basic block in a source-level CFG.
Definition CFG.h:651
CFG * getParent() const
Definition CFG.h:1179
CFGBlock & getEntry()
Definition CFG.h:1408
A boolean literal, per ([C++ lex.bool] Boolean literals).
Definition ExprCXX.h:728
Represents a point when we begin processing an inlined call.
Represents a point when we start the call exit sequence (for inlined call).
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition Expr.h:3012
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:550
DeclContext * getDeclContext()
Definition DeclBase.h:457
bool hasAttr() const
Definition DeclBase.h:586
This represents one expression.
Definition Expr.h:114
Represents a function declaration or definition.
Definition Decl.h:2071
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
std::string getQualifiedNameAsString() const
Definition Decl.cpp:1683
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
Definition Decl.h:319
virtual void getNameForDiagnostic(raw_ostream &OS, const PrintingPolicy &Policy, bool Qualified) const
Appends a human-readable name for this declaration into the given stream.
Definition Decl.cpp:1849
ObjCBoolLiteralExpr - Objective-C Boolean Literal.
Definition ExprObjC.h:118
ObjCBoxedExpr - used for generalized expression boxing.
Definition ExprObjC.h:158
Represents an ObjC class declaration.
Definition DeclObjC.h:1160
An expression that sends a message to the given Objective-C object or class.
Definition ExprObjC.h:972
ObjCMethodDecl - Represents an instance or class method declaration.
Definition DeclObjC.h:140
Represents a pointer to an Objective C object.
Definition TypeBase.h:8219
QualType getPointeeType() const
Gets the type pointed to by this ObjC pointer.
Definition TypeBase.h:8231
Represents a parameter to a function.
Definition Decl.h:1832
ProgramPoints can be "tagged" as representing points specific to a given analysis entity.
const ProgramPointTag * getTag() const
T castAs() const
Convert to the specified ProgramPoint type, asserting that this ProgramPoint is of the desired type.
std::optional< T > getAs() const
Convert to the specified ProgramPoint type, returning std::nullopt if this ProgramPoint is not of the...
A (possibly-)qualified type.
Definition TypeBase.h:938
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition TypeBase.h:1005
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
Definition TypeBase.h:1348
This class handles loading and caching of source files into memory.
It represents a stack frame of the call stack.
bool isParentOf(const StackFrame *SF) const
const Expr * getCallSite() const
const Decl * getDecl() const
const StackFrame * getParent() const
It might return null.
Stmt - This represents one statement.
Definition Stmt.h:85
child_range children()
Definition Stmt.cpp:304
bool isVoidType() const
Definition TypeBase.h:9200
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition Type.h:26
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:885
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9431
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:714
ASTContext & getASTContext() const
ProgramStateManager & getStateManager() const
const SourceManager & getSourceManager() const
static PathDiagnosticPieceRef getDefaultEndPath(const BugReporterContext &BRC, const ExplodedNode *N, const PathSensitiveBugReport &BR)
Generates the default final diagnostic piece.
Manages the lifetime of CallEvent objects.
Definition CallEvent.h:1361
CallEventRef getCaller(const StackFrame *CalleeSF, ProgramStateRef State)
Gets an outside caller given a callee context.
CallEventRef< ObjCMethodCall > getObjCMethodCall(const ObjCMessageExpr *E, ProgramStateRef State, const StackFrame *SF, CFGBlock::ConstCFGElementRef ElemRef)
Definition CallEvent.h:1431
CallEventRef getCall(const Stmt *S, ProgramStateRef State, const StackFrame *SF, CFGBlock::ConstCFGElementRef ElemRef)
Gets a call event for a function call, Objective-C method call, a 'new', or a 'delete' call.
const SourceManager & getSourceManager()
ProgramStateManager & getStateManager()
const StackFrame * getStackFrame() const
const ProgramStateRef & getState() const
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
std::optional< T > getLocationAs() const &
ExplodedNode * getFirstPred()
const Decl & getCodeDecl() const
const StackFrame * getStackFrame() const
MemRegion - The root abstract class for all memory regions.
Definition MemRegion.h:97
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind=bugreporter::TrackingKind::Thorough)
Marks a symbol as interesting.
PathSensitiveBugReport(const BugType &bt, const llvm::Twine &desc, const ExplodedNode *errorNode)
PathDiagnosticLocation UniqueingLocation
Reports with different uniqueing locations are considered to be different for the purposes of dedupli...
const ExplodedNode * getErrorNode() const
void addVisitor(std::unique_ptr< BugReporterVisitor > visitor)
Add custom or predefined bug report visitors to this report.
CallEventManager & getCallEventManager()
void iterBindings(ProgramStateRef state, StoreManager::BindingsHandler &F)
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition SVals.h:57
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition SVals.cpp:103
SymbolRef getAsLocSymbol(bool IncludeBaseRegions=false) const
If this SVal is a location and wraps a symbol, return that SymbolRef.
Definition SVals.cpp:67
virtual const MemRegion * getOriginRegion() const
Find the region from which this symbol originates.
Definition SymExpr.h:124
virtual QualType getType() const =0
void Profile(llvm::FoldingSetNodeID &ID) const override
PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &BR) override
Return a diagnostic piece which should be associated with the given node.
PathDiagnosticPieceRef getEndPath(const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &BR) override
Provide custom definition for the final diagnostic piece on the path - the piece, which is displayed ...
RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, bool isLeak=false, bool IsReleaseUnowned=false)
RefLeakReportVisitor(SymbolRef Sym, const MemRegion *LastBinding)
PathDiagnosticPieceRef getEndPath(const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &BR) override
Provide custom definition for the final diagnostic piece on the path - the piece, which is displayed ...
RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, CheckerContext &Ctx)
IvarAccessHistory getIvarAccessHistory() const
Returns what the analyzer knows about direct accesses to a particular instance variable.
static const SimpleProgramPointTag & getCastFailTag()
static const SimpleProgramPointTag & getDeallocSentTag()
void trackStoredValue(SVal V, const MemRegion *R, PathSensitiveBugReport &Report, TrackingOptions Opts={}, const StackFrame *Origin=nullptr)
Track how the value got stored into the given region and where it came from.
bool isSynthesizedAccessor(const StackFrame *SF)
Returns true if this stack frame is for an Objective-C method that is a property getter or setter who...
const RefVal * getRefBinding(ProgramStateRef State, SymbolRef Sym)
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
Definition SymExpr.h:133
ObjKind
Determines the object kind of a tracked object.
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
@ CF
Indicates that the tracked object is a CF object.
@ ObjC
Indicates that the tracked object is an Objective-C object.
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
const void * Store
Store - This opaque type encapsulates an immutable mapping from locations to values.
Definition StoreRef.h:27
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
const FunctionProtoType * T
U cast(CodeGen::Address addr)
Definition Address.h:327
#define false
Definition stdbool.h:26
#define true
Definition stdbool.h:25