clang 23.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. ";
93 }
94 os << "Object released.";
95 return true;
96
98 // Autoreleases can be applied after marking a node ReturnedOwned.
99 if (CurrV.getAutoreleaseCount())
100 return false;
101
102 os << "Object returned to caller as an owning reference (single "
103 "retain count transferred to caller)";
104 return true;
105
107 os << "Object returned to caller with a +0 retain count";
108 return true;
109
110 default:
111 return false;
112 }
113 return true;
114}
115
116/// Finds argument index of the out paramter in the call @c S
117/// corresponding to the symbol @c Sym.
118/// If none found, returns std::nullopt.
119static std::optional<unsigned>
121 std::optional<CallEventRef<>> CE) {
122 if (!CE)
123 return std::nullopt;
124
125 for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++)
126 if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())
127 if (const auto *TR = dyn_cast<TypedValueRegion>(MR))
128 if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymbol() == Sym)
129 return Idx;
130
131 return std::nullopt;
132}
133
134static std::optional<std::string> findMetaClassAlloc(const Expr *Callee) {
135 if (const auto *ME = dyn_cast<MemberExpr>(Callee)) {
136 if (ME->getMemberDecl()->getNameAsString() != "alloc")
137 return std::nullopt;
138 const Expr *This = ME->getBase()->IgnoreParenImpCasts();
139 if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) {
140 const ValueDecl *VD = DRE->getDecl();
141 if (VD->getNameAsString() != "metaClass")
142 return std::nullopt;
143
144 if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext()))
145 return RD->getNameAsString();
146
147 }
148 }
149 return std::nullopt;
150}
151
152static std::string findAllocatedObjectName(const Stmt *S, QualType QT) {
153 if (const auto *CE = dyn_cast<CallExpr>(S))
154 if (auto Out = findMetaClassAlloc(CE->getCallee()))
155 return *Out;
156 return getPrettyTypeName(QT);
157}
158
160 const StackFrame *SF,
161 const RefVal &CurrV, SymbolRef &Sym,
162 const Stmt *S,
163 llvm::raw_string_ostream &os) {
164 CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
165 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
166 // Get the name of the callee (if it is available)
167 // from the tracked SVal.
168 SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), SF);
169 const FunctionDecl *FD = X.getAsFunctionDecl();
170
171 // If failed, try to get it from AST.
172 if (!FD)
173 FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
174
175 if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) {
176 os << "Call to method '" << MD->getQualifiedNameAsString() << '\'';
177 } else if (FD) {
178 os << "Call to function '" << FD->getQualifiedNameAsString() << '\'';
179 } else {
180 os << "function call";
181 }
182 } else if (isa<CXXNewExpr>(S)) {
183 os << "Operator 'new'";
184 } else {
185 assert(isa<ObjCMessageExpr>(S));
187 cast<ObjCMessageExpr>(S), CurrSt, SF, {nullptr, 0});
188
189 switch (Call->getMessageKind()) {
190 case OCM_Message:
191 os << "Method";
192 break;
194 os << "Property";
195 break;
196 case OCM_Subscript:
197 os << "Subscript";
198 break;
199 }
200 }
201
202 std::optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, SF, {nullptr, 0});
203 auto Idx = findArgIdxOfSymbol(CurrSt, SF, Sym, CE);
204
205 // If index is not found, we assume that the symbol was returned.
206 if (!Idx) {
207 os << " returns ";
208 } else {
209 os << " writes ";
210 }
211
212 if (CurrV.getObjKind() == ObjKind::CF) {
213 os << "a Core Foundation object of type '" << Sym->getType() << "' with a ";
214 } else if (CurrV.getObjKind() == ObjKind::OS) {
215 os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType())
216 << "' with a ";
217 } else if (CurrV.getObjKind() == ObjKind::Generalized) {
218 os << "an object of type '" << Sym->getType() << "' with a ";
219 } else {
220 assert(CurrV.getObjKind() == ObjKind::ObjC);
221 QualType T = Sym->getType();
223 os << "an Objective-C object with a ";
224 } else {
226 os << "an instance of " << PT->getPointeeType() << " with a ";
227 }
228 }
229
230 if (CurrV.isOwned()) {
231 os << "+1 retain count";
232 } else {
233 assert(CurrV.isNotOwned());
234 os << "+0 retain count";
235 }
236
237 if (Idx) {
238 os << " into an out parameter '";
239 const ParmVarDecl *PVD = (*CE)->parameters()[*Idx];
241 /*Qualified=*/false);
242 os << "'";
243
244 QualType RT = (*CE)->getResultType();
245 if (!RT.isNull() && !RT->isVoidType()) {
246 SVal RV = (*CE)->getReturnValue();
247 if (CurrSt->isNull(RV).isConstrainedTrue()) {
248 os << " (assuming the call returns zero)";
249 } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) {
250 os << " (assuming the call returns non-zero)";
251 }
252
253 }
254 }
255}
256
257namespace clang {
258namespace ento {
259namespace retaincountchecker {
260
262protected:
265
266public:
268 : Sym(S), IsReleaseUnowned(IRU) {}
269
270 void Profile(llvm::FoldingSetNodeID &ID) const override {
271 static int x = 0;
272 ID.AddPointer(&x);
273 ID.AddPointer(Sym);
274 }
275
278 PathSensitiveBugReport &BR) override;
279
281 const ExplodedNode *N,
282 PathSensitiveBugReport &BR) override;
283};
284
286public:
288 : RefCountReportVisitor(Sym, /*IsReleaseUnowned=*/false),
289 LastBinding(LastBinding) {}
290
292 const ExplodedNode *N,
293 PathSensitiveBugReport &BR) override;
294
295private:
296 const MemRegion *LastBinding;
297};
298
299} // end namespace retaincountchecker
300} // end namespace ento
301} // end namespace clang
302
303
304/// Find the first node with the parent stack frame.
305static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) {
306 const StackFrame *SF = Pred->getStackFrame();
307 if (SF->inTopFrame())
308 return nullptr;
309 const StackFrame *PSF = SF->getParent();
310 if (!PSF)
311 return nullptr;
312
313 const ExplodedNode *N = Pred;
314 while (N && N->getStackFrame() != PSF) {
315 N = N->getFirstPred();
316 }
317 return N;
318}
319
320
321/// Insert a diagnostic piece at function exit
322/// if a function parameter is annotated as "os_consumed",
323/// but it does not actually consume the reference.
324static std::shared_ptr<PathDiagnosticEventPiece>
326 CallExitBegin &CallExitLoc,
327 const SourceManager &SM,
328 CallEventManager &CEMgr) {
329
330 const ExplodedNode *CN = getCalleeNode(N);
331 if (!CN)
332 return nullptr;
333
335
336 std::string sbuf;
337 llvm::raw_string_ostream os(sbuf);
338 ArrayRef<const ParmVarDecl *> Parameters = Call->parameters();
339 for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {
340 const ParmVarDecl *PVD = Parameters[I];
341
342 if (!PVD->hasAttr<OSConsumedAttr>())
343 continue;
344
345 if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
346 const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR);
347 const RefVal *CountAtExit = getRefBinding(N->getState(), SR);
348
349 if (!CountBeforeCall || !CountAtExit)
350 continue;
351
352 unsigned CountBefore = CountBeforeCall->getCount();
353 unsigned CountAfter = CountAtExit->getCount();
354
355 bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
356 if (!AsExpected) {
357 os << "Parameter '";
359 /*Qualified=*/false);
360 os << "' is marked as consuming, but the function did not consume "
361 << "the reference\n";
362 }
363 }
364 }
365
366 if (sbuf.empty())
367 return nullptr;
368
370 return std::make_shared<PathDiagnosticEventPiece>(L, sbuf);
371}
372
373/// Annotate the parameter at the analysis entry point.
374static std::shared_ptr<PathDiagnosticEventPiece>
376 const SourceManager &SM) {
377 auto PP = N->getLocationAs<BlockEdge>();
378 if (!PP)
379 return nullptr;
380
381 const CFGBlock *Src = PP->getSrc();
382 const RefVal *CurrT = getRefBinding(N->getState(), Sym);
383
384 if (&Src->getParent()->getEntry() != Src || !CurrT ||
385 getRefBinding(N->getFirstPred()->getState(), Sym))
386 return nullptr;
387
388 const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion());
389 const auto *PVD = cast<ParmVarDecl>(VR->getDecl());
391
392 std::string s;
393 llvm::raw_string_ostream os(s);
394 os << "Parameter '" << PVD->getDeclName() << "' starts at +";
395 if (CurrT->getCount() == 1) {
396 os << "1, as it is marked as consuming";
397 } else {
398 assert(CurrT->getCount() == 0);
399 os << "0";
400 }
401 return std::make_shared<PathDiagnosticEventPiece>(L, s);
402}
403
407 const SourceManager &SM = BRC.getSourceManager();
409 if (auto CE = N->getLocationAs<CallExitBegin>())
410 if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
411 return PD;
412
413 if (auto PD = annotateStartParameter(N, Sym, SM))
414 return PD;
415
416 // FIXME: We will eventually need to handle non-statement-based events
417 // (__attribute__((cleanup))).
418 if (!N->getLocation().getAs<StmtPoint>())
419 return nullptr;
420
421 // Check if the type state has changed.
422 const ExplodedNode *PrevNode = N->getFirstPred();
423 ProgramStateRef PrevSt = PrevNode->getState();
424 ProgramStateRef CurrSt = N->getState();
425 const StackFrame *SF = N->getStackFrame();
426
427 const RefVal* CurrT = getRefBinding(CurrSt, Sym);
428 if (!CurrT)
429 return nullptr;
430
431 const RefVal &CurrV = *CurrT;
432 const RefVal *PrevT = getRefBinding(PrevSt, Sym);
433
434 // Create a string buffer to constain all the useful things we want
435 // to tell the user.
436 std::string sbuf;
437 llvm::raw_string_ostream os(sbuf);
438
439 if (PrevT && IsReleaseUnowned && CurrV.isNotOwned() && PrevT->isOwned()) {
440 os << "Object is now not exclusively owned";
442 return std::make_shared<PathDiagnosticEventPiece>(Pos, sbuf);
443 }
444
445 // This is the allocation site since the previous node had no bindings
446 // for this symbol.
447 if (!PrevT) {
448 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
449
451 S = SF->getCallSite();
452 }
453
454 if (isa<ObjCArrayLiteral>(S)) {
455 os << "NSArray literal is an object with a +0 retain count";
456 } else if (isa<ObjCDictionaryLiteral>(S)) {
457 os << "NSDictionary literal is an object with a +0 retain count";
458 } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
459 if (isNumericLiteralExpression(BL->getSubExpr()))
460 os << "NSNumber literal is an object with a +0 retain count";
461 else {
462 const ObjCInterfaceDecl *BoxClass = nullptr;
463 if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
464 BoxClass = Method->getClassInterface();
465
466 // We should always be able to find the boxing class interface,
467 // but consider this future-proofing.
468 if (BoxClass) {
469 os << *BoxClass << " b";
470 } else {
471 os << "B";
472 }
473
474 os << "oxed expression produces an object with a +0 retain count";
475 }
476 } else if (isa<ObjCIvarRefExpr>(S)) {
477 os << "Object loaded from instance variable";
478 } else {
479 generateDiagnosticsForCallLike(CurrSt, SF, CurrV, Sym, S, os);
480 }
481
483 return std::make_shared<PathDiagnosticEventPiece>(Pos, sbuf);
484 }
485
486 // Gather up the effects that were performed on the object at this
487 // program point
488 bool DeallocSent = false;
489
490 const ProgramPointTag *Tag = N->getLocation().getTag();
491
493 os << "Assuming dynamic cast returns null due to type mismatch";
494 }
495
497 // We only have summaries attached to nodes after evaluating CallExpr and
498 // ObjCMessageExprs.
499 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
500
501 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
502 // Iterate through the parameter expressions and see if the symbol
503 // was ever passed as an argument.
504 unsigned i = 0;
505
506 for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) {
507
508 // Retrieve the value of the argument. Is it the symbol
509 // we are interested in?
510 if (CurrSt->getSValAsScalarOrLoc(*AI, SF).getAsLocSymbol() != Sym)
511 continue;
512
513 // We have an argument. Get the effect!
514 DeallocSent = true;
515 }
516 } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
517 if (const Expr *R = ME->getInstanceReceiver()) {
518 if (CurrSt->getSValAsScalarOrLoc(R, SF).getAsLocSymbol() == Sym) {
519 // The symbol we are tracking is the receiver.
520 DeallocSent = true;
521 }
522 }
523 }
524 }
525
526 if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent))
527 return nullptr;
528
529 if (sbuf.empty())
530 return nullptr; // We have nothing to say!
531
532 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
534 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, sbuf);
535
536 // Add the range by scanning the children of the statement for any bindings
537 // to Sym.
538 for (const Stmt *Child : S->children())
539 if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))
540 if (CurrSt->getSValAsScalarOrLoc(Exp, SF).getAsLocSymbol() == Sym) {
541 P->addRange(Exp->getSourceRange());
542 break;
543 }
544
545 return std::move(P);
546}
547
548static std::optional<std::string> describeRegion(const MemRegion *MR) {
549 if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
550 return std::string(VR->getDecl()->getName());
551 // Once we support more storage locations for bindings,
552 // this would need to be improved.
553 return std::nullopt;
554}
555
557
558namespace {
559class VarBindingsCollector : public StoreManager::BindingsHandler {
560 SymbolRef Sym;
562
563public:
564 VarBindingsCollector(SymbolRef Sym, Bindings &ToFill)
565 : Sym(Sym), Result(ToFill) {}
566
567 bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *R,
568 SVal Val) override {
569 SymbolRef SymV = Val.getAsLocSymbol();
570 if (!SymV || SymV != Sym)
571 return true;
572
574 Result.emplace_back(R, Val);
575
576 return true;
577 }
578};
579} // namespace
580
582 const ExplodedNode *Node,
583 SymbolRef Sym) {
585 VarBindingsCollector Collector{Sym, Result};
586 while (Result.empty() && Node) {
587 Manager.iterBindings(Node->getState(), Collector);
588 Node = Node->getFirstPred();
589 }
590
591 return Result;
592}
593
594namespace {
595// Find the first node in the current function context that referred to the
596// tracked symbol and the memory location that value was stored to. Note, the
597// value is only reported if the allocation occurred in the same function as
598// the leak. The function can also return a stack frame, which should be
599// treated as interesting.
600struct AllocationInfo {
601 const ExplodedNode* N;
602 const MemRegion *R;
603 const StackFrame *InterestingMethodStackFrame;
604 AllocationInfo(const ExplodedNode *InN, const MemRegion *InR,
605 const StackFrame *InterestingMethodStackFrame)
606 : N(InN), R(InR),
607 InterestingMethodStackFrame(InterestingMethodStackFrame) {}
608};
609} // end anonymous namespace
610
611static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
612 const ExplodedNode *N, SymbolRef Sym) {
613 const ExplodedNode *AllocationNode = N;
614 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
615 const MemRegion *FirstBinding = nullptr;
616 const StackFrame *LeakStackFrame = N->getStackFrame();
617
618 // The stack frame of the init method called on the leaked object, if
619 // available.
620 const StackFrame *InitMethodStackFrame = nullptr;
621
622 while (N) {
623 ProgramStateRef St = N->getState();
624 const StackFrame *NStackFrame = N->getStackFrame();
625
626 if (!getRefBinding(St, Sym))
627 break;
628
630 StateMgr.iterBindings(St, FB);
631
632 if (FB) {
633 const MemRegion *R = FB.getRegion();
634 // Do not show local variables belonging to a function other than
635 // where the error is reported.
636 if (const auto *MR = R->getMemorySpaceAs<StackSpaceRegion>(St))
637 if (MR->getStackFrame() == LeakStackFrame)
638 FirstBinding = R;
639 }
640
641 // AllocationNode is the last node in which the symbol was tracked.
642 AllocationNode = N;
643
644 // AllocationNodeInCurrentContext, is the last node in the current or
645 // parent context in which the symbol was tracked.
646 //
647 // Note that the allocation site might be in the parent context. For example,
648 // the case where an allocation happens in a block that captures a reference
649 // to it and that reference is overwritten/dropped by another call to
650 // the block.
651 if (NStackFrame == LeakStackFrame ||
652 NStackFrame->isParentOf(LeakStackFrame))
653 AllocationNodeInCurrentOrParentContext = N;
654
655 // Find the last init that was called on the given symbol and store the
656 // init method's stack frame.
657 if (!InitMethodStackFrame)
658 if (auto CEP = N->getLocation().getAs<CallEnter>()) {
659 const Stmt *CE = CEP->getCallExpr();
660 if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
661 if (const Expr *RecExpr = ME->getInstanceReceiver()) {
662 SVal RecV = St->getSVal(RecExpr, NStackFrame);
663 if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
664 InitMethodStackFrame = CEP->getCalleeStackFrame();
665 }
666 }
667 }
668
669 N = N->getFirstPred();
670 }
671
672 // If we are reporting a leak of the object that was allocated with alloc,
673 // mark its init method as interesting.
674 const StackFrame *InterestingMethodStackFrame = nullptr;
675 if (InitMethodStackFrame) {
676 const ProgramPoint AllocPP = AllocationNode->getLocation();
677 if (std::optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
678 if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
679 if (ME->getMethodFamily() == OMF_alloc)
680 InterestingMethodStackFrame = InitMethodStackFrame;
681 }
682
683 // If allocation happened in a function different from the leak node context,
684 // do not report the binding.
685 assert(N && "Could not find allocation node");
686
687 if (AllocationNodeInCurrentOrParentContext &&
688 AllocationNodeInCurrentOrParentContext->getStackFrame() != LeakStackFrame)
689 FirstBinding = nullptr;
690
691 return AllocationInfo(AllocationNodeInCurrentOrParentContext, FirstBinding,
692 InterestingMethodStackFrame);
693}
694
702
705 const ExplodedNode *EndN,
707
708 // Tell the BugReporterContext to report cases when the tracked symbol is
709 // assigned to different variables, etc.
711
712 PathDiagnosticLocation L = cast<RefLeakReport>(BR).getEndOfPath();
713
714 std::string sbuf;
715 llvm::raw_string_ostream os(sbuf);
716
717 os << "Object leaked: ";
718
719 std::optional<std::string> RegionDescription = describeRegion(LastBinding);
720 if (RegionDescription) {
721 os << "object allocated and stored into '" << *RegionDescription << '\'';
722 } else {
723 os << "allocated object of type '" << getPrettyTypeName(Sym->getType())
724 << "'";
725 }
726
727 // Get the retain count.
728 const RefVal *RV = getRefBinding(EndN->getState(), Sym);
729 assert(RV);
730
731 if (RV->getKind() == RefVal::ErrorLeakReturned) {
732 const Decl *D = &EndN->getCodeDecl();
733
734 os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
735 : " is returned from a function ");
736
737 if (D->hasAttr<CFReturnsNotRetainedAttr>()) {
738 os << "that is annotated as CF_RETURNS_NOT_RETAINED";
739 } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) {
740 os << "that is annotated as NS_RETURNS_NOT_RETAINED";
741 } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {
742 os << "that is annotated as OS_RETURNS_NOT_RETAINED";
743 } else {
744 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
745 if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) {
746 os << "managed by Automatic Reference Counting";
747 } else {
748 os << "whose name ('" << MD->getSelector().getAsString()
749 << "') does not start with "
750 "'copy', 'mutableCopy', 'alloc' or 'new'."
751 " This violates the naming convention rules"
752 " given in the Memory Management Guide for Cocoa";
753 }
754 } else {
755 const FunctionDecl *FD = cast<FunctionDecl>(D);
756 ObjKind K = RV->getObjKind();
757 if (K == ObjKind::ObjC || K == ObjKind::CF) {
758 os << "whose name ('" << *FD
759 << "') does not contain 'Copy' or 'Create'. This violates the "
760 "naming convention rules given in the Memory Management Guide "
761 "for Core Foundation";
762 } else if (RV->getObjKind() == ObjKind::OS) {
763 std::string FuncName = FD->getNameAsString();
764 os << "whose name ('" << FuncName << "') starts with '"
765 << StringRef(FuncName).substr(0, 3) << "'";
766 }
767 }
768 }
769 } else {
770 os << " is not referenced later in this execution path and has a retain "
771 "count of +"
772 << RV->getCount();
773 }
774
775 return std::make_shared<PathDiagnosticEventPiece>(L, sbuf);
776}
777
779 ExplodedNode *n, SymbolRef sym, bool isLeak,
780 bool IsReleaseUnowned)
781 : PathSensitiveBugReport(D, D.getReportMessage(), n), Sym(sym),
782 isLeak(isLeak) {
783 if (!isLeak)
784 addVisitor<RefCountReportVisitor>(sym, IsReleaseUnowned);
785}
786
788 ExplodedNode *n, SymbolRef sym,
789 StringRef endText)
790 : PathSensitiveBugReport(D, D.getReportMessage(), endText, n) {
791
792 addVisitor<RefCountReportVisitor>(sym, /*IsReleaseUnowned=*/false);
793}
794
795void RefLeakReport::deriveParamLocation(CheckerContext &Ctx) {
796 const SourceManager &SMgr = Ctx.getSourceManager();
797
798 if (!Sym->getOriginRegion())
799 return;
800
801 auto *Region = dyn_cast<DeclRegion>(Sym->getOriginRegion());
802 if (Region) {
803 const Decl *PDecl = Region->getDecl();
804 if (isa_and_nonnull<ParmVarDecl>(PDecl)) {
805 PathDiagnosticLocation ParamLocation =
807 Location = ParamLocation;
808 UniqueingLocation = ParamLocation;
810 }
811 }
812}
813
814void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx) {
815 // Most bug reports are cached at the location where they occurred.
816 // With leaks, we want to unique them by the location where they were
817 // allocated, and only report a single path. To do this, we need to find
818 // the allocation site of a piece of tracked memory, which we do via a
819 // call to GetAllocationSite. This will walk the ExplodedGraph backwards.
820 // Note that this is *not* the trimmed graph; we are guaranteed, however,
821 // that all ancestor nodes that represent the allocation site have the
822 // same SourceLocation.
823 const ExplodedNode *AllocNode = nullptr;
824
825 const SourceManager &SMgr = Ctx.getSourceManager();
826
827 AllocationInfo AllocI =
829
830 AllocNode = AllocI.N;
831 AllocFirstBinding = AllocI.R;
832 markInteresting(AllocI.InterestingMethodStackFrame);
833
834 // Get the SourceLocation for the allocation site.
835 // FIXME: This will crash the analyzer if an allocation comes from an
836 // implicit call (ex: a destructor call).
837 // (Currently there are no such allocations in Cocoa, though.)
838 AllocStmt = AllocNode->getStmtForDiagnostics();
839
840 if (!AllocStmt) {
841 AllocFirstBinding = nullptr;
842 return;
843 }
844
845 PathDiagnosticLocation AllocLocation = PathDiagnosticLocation::createBegin(
846 AllocStmt, SMgr, AllocNode->getStackFrame());
847 Location = AllocLocation;
848
849 // Set uniqieing info, which will be used for unique the bug reports. The
850 // leaks should be uniqued on the allocation site.
851 UniqueingLocation = AllocLocation;
852 UniqueingDecl = AllocNode->getStackFrame()->getDecl();
853}
854
855void RefLeakReport::createDescription(CheckerContext &Ctx) {
856 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
857 Description.clear();
858 llvm::raw_string_ostream os(Description);
859 os << "Potential leak of an object";
860
861 std::optional<std::string> RegionDescription =
862 describeRegion(AllocBindingToReport);
863 if (RegionDescription) {
864 os << " stored into '" << *RegionDescription << '\'';
865 } else {
866
867 // If we can't figure out the name, just supply the type information.
868 os << " of type '" << getPrettyTypeName(Sym->getType()) << "'";
869 }
870}
871
872void RefLeakReport::findBindingToReport(CheckerContext &Ctx,
873 ExplodedNode *Node) {
874 if (!AllocFirstBinding)
875 // If we don't have any bindings, we won't be able to find any
876 // better binding to report.
877 return;
878
879 // If the original region still contains the leaking symbol...
880 if (Node->getState()->getSVal(AllocFirstBinding).getAsSymbol() == Sym) {
881 // ...it is the best binding to report.
882 AllocBindingToReport = AllocFirstBinding;
883 return;
884 }
885
886 // At this point, we know that the original region doesn't contain the leaking
887 // when the actual leak happens. It means that it can be confusing for the
888 // user to see such description in the message.
889 //
890 // Let's consider the following example:
891 // Object *Original = allocate(...);
892 // Object *New = Original;
893 // Original = allocate(...);
894 // Original->release();
895 //
896 // Complaining about a leaking object "stored into Original" might cause a
897 // rightful confusion because 'Original' is actually released.
898 // We should complain about 'New' instead.
899 Bindings AllVarBindings =
901
902 // While looking for the last var bindings, we can still find
903 // `AllocFirstBinding` to be one of them. In situations like this,
904 // it would still be the easiest case to explain to our users.
905 if (!AllVarBindings.empty() &&
906 !llvm::is_contained(llvm::make_first_range(AllVarBindings),
907 AllocFirstBinding)) {
908 // Let's pick one of them at random (if there is something to pick from).
909 AllocBindingToReport = AllVarBindings[0].first;
910
911 // Because 'AllocBindingToReport' is not the same as
912 // 'AllocFirstBinding', we need to explain how the leaking object
913 // got from one to another.
914 //
915 // NOTE: We use the actual SVal stored in AllocBindingToReport here because
916 // trackStoredValue compares SVal's and it can get trickier for
917 // something like derived regions if we want to construct SVal from
918 // Sym. Instead, we take the value that is definitely stored in that
919 // region, thus guaranteeing that trackStoredValue will work.
920 bugreporter::trackStoredValue(AllVarBindings[0].second,
921 AllocBindingToReport, *this);
922 } else {
923 AllocBindingToReport = AllocFirstBinding;
924 }
925}
926
929 CheckerContext &Ctx)
930 : RefCountReport(D, LOpts, N, Sym, /*isLeak=*/true) {
931
932 deriveAllocLocation(Ctx);
933 findBindingToReport(Ctx, N);
934
935 if (!AllocFirstBinding)
936 deriveParamLocation(Ctx);
937
938 createDescription(Ctx);
939
940 addVisitor<RefLeakReportVisitor>(Sym, AllocBindingToReport);
941}
static const MemRegion * getRegion(const CallEvent &Call, const MutexDescriptor &Descriptor, bool IsLock)
Result
Implement __builtin_bit_cast and related operations.
#define X(type, name)
Definition Value.h:97
#define SM(sm)
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)
__device__ __2f16 float __ockl_bool s
a trap message and trap category.
const LangOptions & getLangOpts() const
Definition ASTContext.h:961
const clang::PrintingPolicy & getPrintingPolicy() const
Definition ASTContext.h:860
Represents a single basic block in a source-level CFG.
Definition CFG.h:632
CFG * getParent() const
Definition CFG.h:1136
CFGBlock & getEntry()
Definition CFG.h:1364
A boolean literal, per ([C++ lex.bool] Boolean literals).
Definition ExprCXX.h:727
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:2946
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:547
DeclContext * getDeclContext()
Definition DeclBase.h:456
bool hasAttr() const
Definition DeclBase.h:585
This represents one expression.
Definition Expr.h:112
Represents a function declaration or definition.
Definition Decl.h:2018
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:1681
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:317
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:1847
ObjCBoolLiteralExpr - Objective-C Boolean Literal.
Definition ExprObjC.h:119
ObjCBoxedExpr - used for generalized expression boxing.
Definition ExprObjC.h:159
Represents an ObjC class declaration.
Definition DeclObjC.h:1154
An expression that sends a message to the given Objective-C object or class.
Definition ExprObjC.h:971
ObjCMethodDecl - Represents an instance or class method declaration.
Definition DeclObjC.h:140
Represents a pointer to an Objective C object.
Definition TypeBase.h:8063
QualType getPointeeType() const
Gets the type pointed to by this ObjC pointer.
Definition TypeBase.h:8075
Represents a parameter to a function.
Definition Decl.h:1808
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:937
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition TypeBase.h:1004
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
Definition TypeBase.h:1347
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:86
child_range children()
Definition Stmt.cpp:304
bool isVoidType() const
Definition TypeBase.h:9048
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:789
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9275
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:712
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:1363
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:1433
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.
PathDiagnosticLocation UniqueingLocation
Reports with different uniqueing locations are considered to be different for the purposes of dedupli...
const ExplodedNode * getErrorNode() const
PathSensitiveBugReport(const BugType &bt, StringRef desc, const ExplodedNode *errorNode)
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:56
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(BugReporterContext &BRC, const ExplodedNode *N, 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(BugReporterContext &BRC, const ExplodedNode *N, 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
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
U cast(CodeGen::Address addr)
Definition Address.h:327
#define false
Definition stdbool.h:26
#define true
Definition stdbool.h:25