10b57cec5SDimitry Andric // RetainCountDiagnostics.cpp - Checks for leaks and other issues -*- C++ -*--// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file defines diagnostics for RetainCountChecker, which implements 100b57cec5SDimitry Andric // a reference count checker for Core Foundation and Cocoa on (Mac OS X). 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include "RetainCountDiagnostics.h" 150b57cec5SDimitry Andric #include "RetainCountChecker.h" 16fe6060f1SDimitry Andric #include "llvm/ADT/STLExtras.h" 17fe6060f1SDimitry Andric #include "llvm/ADT/SmallVector.h" 18bdd1243dSDimitry Andric #include <optional> 190b57cec5SDimitry Andric 200b57cec5SDimitry Andric using namespace clang; 210b57cec5SDimitry Andric using namespace ento; 220b57cec5SDimitry Andric using namespace retaincountchecker; 230b57cec5SDimitry Andric 245ffd83dbSDimitry Andric StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugKind BT) { 250b57cec5SDimitry Andric switch (BT) { 260b57cec5SDimitry Andric case UseAfterRelease: 270b57cec5SDimitry Andric return "Use-after-release"; 280b57cec5SDimitry Andric case ReleaseNotOwned: 290b57cec5SDimitry Andric return "Bad release"; 300b57cec5SDimitry Andric case DeallocNotOwned: 310b57cec5SDimitry Andric return "-dealloc sent to non-exclusively owned object"; 320b57cec5SDimitry Andric case FreeNotOwned: 330b57cec5SDimitry Andric return "freeing non-exclusively owned object"; 340b57cec5SDimitry Andric case OverAutorelease: 350b57cec5SDimitry Andric return "Object autoreleased too many times"; 360b57cec5SDimitry Andric case ReturnNotOwnedForOwned: 370b57cec5SDimitry Andric return "Method should return an owned object"; 380b57cec5SDimitry Andric case LeakWithinFunction: 390b57cec5SDimitry Andric return "Leak"; 400b57cec5SDimitry Andric case LeakAtReturn: 410b57cec5SDimitry Andric return "Leak of returned object"; 420b57cec5SDimitry Andric } 435ffd83dbSDimitry Andric llvm_unreachable("Unknown RefCountBugKind"); 440b57cec5SDimitry Andric } 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric StringRef RefCountBug::getDescription() const { 470b57cec5SDimitry Andric switch (BT) { 480b57cec5SDimitry Andric case UseAfterRelease: 490b57cec5SDimitry Andric return "Reference-counted object is used after it is released"; 500b57cec5SDimitry Andric case ReleaseNotOwned: 510b57cec5SDimitry Andric return "Incorrect decrement of the reference count of an object that is " 520b57cec5SDimitry Andric "not owned at this point by the caller"; 530b57cec5SDimitry Andric case DeallocNotOwned: 540b57cec5SDimitry Andric return "-dealloc sent to object that may be referenced elsewhere"; 550b57cec5SDimitry Andric case FreeNotOwned: 560b57cec5SDimitry Andric return "'free' called on an object that may be referenced elsewhere"; 570b57cec5SDimitry Andric case OverAutorelease: 580b57cec5SDimitry Andric return "Object autoreleased too many times"; 590b57cec5SDimitry Andric case ReturnNotOwnedForOwned: 600b57cec5SDimitry Andric return "Object with a +0 retain count returned to caller where a +1 " 610b57cec5SDimitry Andric "(owning) retain count is expected"; 620b57cec5SDimitry Andric case LeakWithinFunction: 630b57cec5SDimitry Andric case LeakAtReturn: 640b57cec5SDimitry Andric return ""; 650b57cec5SDimitry Andric } 665ffd83dbSDimitry Andric llvm_unreachable("Unknown RefCountBugKind"); 670b57cec5SDimitry Andric } 680b57cec5SDimitry Andric 695ffd83dbSDimitry Andric RefCountBug::RefCountBug(CheckerNameRef Checker, RefCountBugKind BT) 700b57cec5SDimitry Andric : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount, 715ffd83dbSDimitry Andric /*SuppressOnSink=*/BT == LeakWithinFunction || 725ffd83dbSDimitry Andric BT == LeakAtReturn), 735ffd83dbSDimitry Andric BT(BT) {} 740b57cec5SDimitry Andric 750b57cec5SDimitry Andric static bool isNumericLiteralExpression(const Expr *E) { 760b57cec5SDimitry Andric // FIXME: This set of cases was copied from SemaExprObjC. 77349cc55cSDimitry Andric return isa<IntegerLiteral, CharacterLiteral, FloatingLiteral, 78349cc55cSDimitry Andric ObjCBoolLiteralExpr, CXXBoolLiteralExpr>(E); 790b57cec5SDimitry Andric } 800b57cec5SDimitry Andric 810b57cec5SDimitry Andric /// If type represents a pointer to CXXRecordDecl, 820b57cec5SDimitry Andric /// and is not a typedef, return the decl name. 830b57cec5SDimitry Andric /// Otherwise, return the serialization of type. 840b57cec5SDimitry Andric static std::string getPrettyTypeName(QualType QT) { 850b57cec5SDimitry Andric QualType PT = QT->getPointeeType(); 860b57cec5SDimitry Andric if (!PT.isNull() && !QT->getAs<TypedefType>()) 870b57cec5SDimitry Andric if (const auto *RD = PT->getAsCXXRecordDecl()) 885ffd83dbSDimitry Andric return std::string(RD->getName()); 890b57cec5SDimitry Andric return QT.getAsString(); 900b57cec5SDimitry Andric } 910b57cec5SDimitry Andric 92fe6060f1SDimitry Andric /// Write information about the type state change to @c os, 930b57cec5SDimitry Andric /// return whether the note should be generated. 940b57cec5SDimitry Andric static bool shouldGenerateNote(llvm::raw_string_ostream &os, 950b57cec5SDimitry Andric const RefVal *PrevT, 960b57cec5SDimitry Andric const RefVal &CurrV, 970b57cec5SDimitry Andric bool DeallocSent) { 980b57cec5SDimitry Andric // Get the previous type state. 990b57cec5SDimitry Andric RefVal PrevV = *PrevT; 1000b57cec5SDimitry Andric 1010b57cec5SDimitry Andric // Specially handle -dealloc. 1020b57cec5SDimitry Andric if (DeallocSent) { 1030b57cec5SDimitry Andric // Determine if the object's reference count was pushed to zero. 1040b57cec5SDimitry Andric assert(!PrevV.hasSameState(CurrV) && "The state should have changed."); 1050b57cec5SDimitry Andric // We may not have transitioned to 'release' if we hit an error. 1060b57cec5SDimitry Andric // This case is handled elsewhere. 1070b57cec5SDimitry Andric if (CurrV.getKind() == RefVal::Released) { 1080b57cec5SDimitry Andric assert(CurrV.getCombinedCounts() == 0); 1090b57cec5SDimitry Andric os << "Object released by directly sending the '-dealloc' message"; 1100b57cec5SDimitry Andric return true; 1110b57cec5SDimitry Andric } 1120b57cec5SDimitry Andric } 1130b57cec5SDimitry Andric 1140b57cec5SDimitry Andric // Determine if the typestate has changed. 1150b57cec5SDimitry Andric if (!PrevV.hasSameState(CurrV)) 1160b57cec5SDimitry Andric switch (CurrV.getKind()) { 1170b57cec5SDimitry Andric case RefVal::Owned: 1180b57cec5SDimitry Andric case RefVal::NotOwned: 1190b57cec5SDimitry Andric if (PrevV.getCount() == CurrV.getCount()) { 1200b57cec5SDimitry Andric // Did an autorelease message get sent? 1210b57cec5SDimitry Andric if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount()) 1220b57cec5SDimitry Andric return false; 1230b57cec5SDimitry Andric 1240b57cec5SDimitry Andric assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount()); 1250b57cec5SDimitry Andric os << "Object autoreleased"; 1260b57cec5SDimitry Andric return true; 1270b57cec5SDimitry Andric } 1280b57cec5SDimitry Andric 1290b57cec5SDimitry Andric if (PrevV.getCount() > CurrV.getCount()) 1300b57cec5SDimitry Andric os << "Reference count decremented."; 1310b57cec5SDimitry Andric else 1320b57cec5SDimitry Andric os << "Reference count incremented."; 1330b57cec5SDimitry Andric 1340b57cec5SDimitry Andric if (unsigned Count = CurrV.getCount()) 1350b57cec5SDimitry Andric os << " The object now has a +" << Count << " retain count."; 1360b57cec5SDimitry Andric 1370b57cec5SDimitry Andric return true; 1380b57cec5SDimitry Andric 1390b57cec5SDimitry Andric case RefVal::Released: 1400b57cec5SDimitry Andric if (CurrV.getIvarAccessHistory() == 1410b57cec5SDimitry Andric RefVal::IvarAccessHistory::ReleasedAfterDirectAccess && 1420b57cec5SDimitry Andric CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) { 1430b57cec5SDimitry Andric os << "Strong instance variable relinquished. "; 1440b57cec5SDimitry Andric } 1450b57cec5SDimitry Andric os << "Object released."; 1460b57cec5SDimitry Andric return true; 1470b57cec5SDimitry Andric 1480b57cec5SDimitry Andric case RefVal::ReturnedOwned: 1490b57cec5SDimitry Andric // Autoreleases can be applied after marking a node ReturnedOwned. 1500b57cec5SDimitry Andric if (CurrV.getAutoreleaseCount()) 1510b57cec5SDimitry Andric return false; 1520b57cec5SDimitry Andric 1530b57cec5SDimitry Andric os << "Object returned to caller as an owning reference (single " 1540b57cec5SDimitry Andric "retain count transferred to caller)"; 1550b57cec5SDimitry Andric return true; 1560b57cec5SDimitry Andric 1570b57cec5SDimitry Andric case RefVal::ReturnedNotOwned: 1580b57cec5SDimitry Andric os << "Object returned to caller with a +0 retain count"; 1590b57cec5SDimitry Andric return true; 1600b57cec5SDimitry Andric 1610b57cec5SDimitry Andric default: 1620b57cec5SDimitry Andric return false; 1630b57cec5SDimitry Andric } 1640b57cec5SDimitry Andric return true; 1650b57cec5SDimitry Andric } 1660b57cec5SDimitry Andric 167fe6060f1SDimitry Andric /// Finds argument index of the out paramter in the call @c S 168fe6060f1SDimitry Andric /// corresponding to the symbol @c Sym. 169bdd1243dSDimitry Andric /// If none found, returns std::nullopt. 170bdd1243dSDimitry Andric static std::optional<unsigned> 171bdd1243dSDimitry Andric findArgIdxOfSymbol(ProgramStateRef CurrSt, const LocationContext *LCtx, 172bdd1243dSDimitry Andric SymbolRef &Sym, std::optional<CallEventRef<>> CE) { 1730b57cec5SDimitry Andric if (!CE) 174bdd1243dSDimitry Andric return std::nullopt; 1750b57cec5SDimitry Andric 1760b57cec5SDimitry Andric for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++) 1770b57cec5SDimitry Andric if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion()) 1780b57cec5SDimitry Andric if (const auto *TR = dyn_cast<TypedValueRegion>(MR)) 179e8d8bef9SDimitry Andric if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymbol() == Sym) 1800b57cec5SDimitry Andric return Idx; 1810b57cec5SDimitry Andric 182bdd1243dSDimitry Andric return std::nullopt; 1830b57cec5SDimitry Andric } 1840b57cec5SDimitry Andric 185bdd1243dSDimitry Andric static std::optional<std::string> findMetaClassAlloc(const Expr *Callee) { 1860b57cec5SDimitry Andric if (const auto *ME = dyn_cast<MemberExpr>(Callee)) { 1870b57cec5SDimitry Andric if (ME->getMemberDecl()->getNameAsString() != "alloc") 188bdd1243dSDimitry Andric return std::nullopt; 1890b57cec5SDimitry Andric const Expr *This = ME->getBase()->IgnoreParenImpCasts(); 1900b57cec5SDimitry Andric if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) { 1910b57cec5SDimitry Andric const ValueDecl *VD = DRE->getDecl(); 1920b57cec5SDimitry Andric if (VD->getNameAsString() != "metaClass") 193bdd1243dSDimitry Andric return std::nullopt; 1940b57cec5SDimitry Andric 1950b57cec5SDimitry Andric if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext())) 1960b57cec5SDimitry Andric return RD->getNameAsString(); 1970b57cec5SDimitry Andric 1980b57cec5SDimitry Andric } 1990b57cec5SDimitry Andric } 200bdd1243dSDimitry Andric return std::nullopt; 2010b57cec5SDimitry Andric } 2020b57cec5SDimitry Andric 2030b57cec5SDimitry Andric static std::string findAllocatedObjectName(const Stmt *S, QualType QT) { 2040b57cec5SDimitry Andric if (const auto *CE = dyn_cast<CallExpr>(S)) 2050b57cec5SDimitry Andric if (auto Out = findMetaClassAlloc(CE->getCallee())) 2060b57cec5SDimitry Andric return *Out; 2070b57cec5SDimitry Andric return getPrettyTypeName(QT); 2080b57cec5SDimitry Andric } 2090b57cec5SDimitry Andric 2100b57cec5SDimitry Andric static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, 2110b57cec5SDimitry Andric const LocationContext *LCtx, 2120b57cec5SDimitry Andric const RefVal &CurrV, SymbolRef &Sym, 2130b57cec5SDimitry Andric const Stmt *S, 2140b57cec5SDimitry Andric llvm::raw_string_ostream &os) { 2150b57cec5SDimitry Andric CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager(); 2160b57cec5SDimitry Andric if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { 2170b57cec5SDimitry Andric // Get the name of the callee (if it is available) 2180b57cec5SDimitry Andric // from the tracked SVal. 2190b57cec5SDimitry Andric SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); 2200b57cec5SDimitry Andric const FunctionDecl *FD = X.getAsFunctionDecl(); 2210b57cec5SDimitry Andric 2220b57cec5SDimitry Andric // If failed, try to get it from AST. 2230b57cec5SDimitry Andric if (!FD) 2240b57cec5SDimitry Andric FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl()); 2250b57cec5SDimitry Andric 2260b57cec5SDimitry Andric if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) { 2270b57cec5SDimitry Andric os << "Call to method '" << MD->getQualifiedNameAsString() << '\''; 2280b57cec5SDimitry Andric } else if (FD) { 2290b57cec5SDimitry Andric os << "Call to function '" << FD->getQualifiedNameAsString() << '\''; 2300b57cec5SDimitry Andric } else { 2310b57cec5SDimitry Andric os << "function call"; 2320b57cec5SDimitry Andric } 2330b57cec5SDimitry Andric } else if (isa<CXXNewExpr>(S)) { 2340b57cec5SDimitry Andric os << "Operator 'new'"; 2350b57cec5SDimitry Andric } else { 2360b57cec5SDimitry Andric assert(isa<ObjCMessageExpr>(S)); 23706c3fb27SDimitry Andric CallEventRef<ObjCMethodCall> Call = Mgr.getObjCMethodCall( 23806c3fb27SDimitry Andric cast<ObjCMessageExpr>(S), CurrSt, LCtx, {nullptr, 0}); 2390b57cec5SDimitry Andric 2400b57cec5SDimitry Andric switch (Call->getMessageKind()) { 2410b57cec5SDimitry Andric case OCM_Message: 2420b57cec5SDimitry Andric os << "Method"; 2430b57cec5SDimitry Andric break; 2440b57cec5SDimitry Andric case OCM_PropertyAccess: 2450b57cec5SDimitry Andric os << "Property"; 2460b57cec5SDimitry Andric break; 2470b57cec5SDimitry Andric case OCM_Subscript: 2480b57cec5SDimitry Andric os << "Subscript"; 2490b57cec5SDimitry Andric break; 2500b57cec5SDimitry Andric } 2510b57cec5SDimitry Andric } 2520b57cec5SDimitry Andric 25306c3fb27SDimitry Andric std::optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx, {nullptr, 0}); 2540b57cec5SDimitry Andric auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE); 2550b57cec5SDimitry Andric 2560b57cec5SDimitry Andric // If index is not found, we assume that the symbol was returned. 2570b57cec5SDimitry Andric if (!Idx) { 2580b57cec5SDimitry Andric os << " returns "; 2590b57cec5SDimitry Andric } else { 2600b57cec5SDimitry Andric os << " writes "; 2610b57cec5SDimitry Andric } 2620b57cec5SDimitry Andric 2630b57cec5SDimitry Andric if (CurrV.getObjKind() == ObjKind::CF) { 26481ad6265SDimitry Andric os << "a Core Foundation object of type '" << Sym->getType() << "' with a "; 2650b57cec5SDimitry Andric } else if (CurrV.getObjKind() == ObjKind::OS) { 2660b57cec5SDimitry Andric os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType()) 2670b57cec5SDimitry Andric << "' with a "; 2680b57cec5SDimitry Andric } else if (CurrV.getObjKind() == ObjKind::Generalized) { 26981ad6265SDimitry Andric os << "an object of type '" << Sym->getType() << "' with a "; 2700b57cec5SDimitry Andric } else { 2710b57cec5SDimitry Andric assert(CurrV.getObjKind() == ObjKind::ObjC); 2720b57cec5SDimitry Andric QualType T = Sym->getType(); 2730b57cec5SDimitry Andric if (!isa<ObjCObjectPointerType>(T)) { 2740b57cec5SDimitry Andric os << "an Objective-C object with a "; 2750b57cec5SDimitry Andric } else { 2760b57cec5SDimitry Andric const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T); 27781ad6265SDimitry Andric os << "an instance of " << PT->getPointeeType() << " with a "; 2780b57cec5SDimitry Andric } 2790b57cec5SDimitry Andric } 2800b57cec5SDimitry Andric 2810b57cec5SDimitry Andric if (CurrV.isOwned()) { 2820b57cec5SDimitry Andric os << "+1 retain count"; 2830b57cec5SDimitry Andric } else { 2840b57cec5SDimitry Andric assert(CurrV.isNotOwned()); 2850b57cec5SDimitry Andric os << "+0 retain count"; 2860b57cec5SDimitry Andric } 2870b57cec5SDimitry Andric 2880b57cec5SDimitry Andric if (Idx) { 2890b57cec5SDimitry Andric os << " into an out parameter '"; 2900b57cec5SDimitry Andric const ParmVarDecl *PVD = (*CE)->parameters()[*Idx]; 2910b57cec5SDimitry Andric PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(), 2920b57cec5SDimitry Andric /*Qualified=*/false); 2930b57cec5SDimitry Andric os << "'"; 2940b57cec5SDimitry Andric 2950b57cec5SDimitry Andric QualType RT = (*CE)->getResultType(); 2960b57cec5SDimitry Andric if (!RT.isNull() && !RT->isVoidType()) { 2970b57cec5SDimitry Andric SVal RV = (*CE)->getReturnValue(); 2980b57cec5SDimitry Andric if (CurrSt->isNull(RV).isConstrainedTrue()) { 2990b57cec5SDimitry Andric os << " (assuming the call returns zero)"; 3000b57cec5SDimitry Andric } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) { 3010b57cec5SDimitry Andric os << " (assuming the call returns non-zero)"; 3020b57cec5SDimitry Andric } 3030b57cec5SDimitry Andric 3040b57cec5SDimitry Andric } 3050b57cec5SDimitry Andric } 3060b57cec5SDimitry Andric } 3070b57cec5SDimitry Andric 3080b57cec5SDimitry Andric namespace clang { 3090b57cec5SDimitry Andric namespace ento { 3100b57cec5SDimitry Andric namespace retaincountchecker { 3110b57cec5SDimitry Andric 3120b57cec5SDimitry Andric class RefCountReportVisitor : public BugReporterVisitor { 3130b57cec5SDimitry Andric protected: 3140b57cec5SDimitry Andric SymbolRef Sym; 3150b57cec5SDimitry Andric 3160b57cec5SDimitry Andric public: 3170b57cec5SDimitry Andric RefCountReportVisitor(SymbolRef sym) : Sym(sym) {} 3180b57cec5SDimitry Andric 3190b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const override { 3200b57cec5SDimitry Andric static int x = 0; 3210b57cec5SDimitry Andric ID.AddPointer(&x); 3220b57cec5SDimitry Andric ID.AddPointer(Sym); 3230b57cec5SDimitry Andric } 3240b57cec5SDimitry Andric 325a7dea167SDimitry Andric PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 3260b57cec5SDimitry Andric BugReporterContext &BRC, 327a7dea167SDimitry Andric PathSensitiveBugReport &BR) override; 3280b57cec5SDimitry Andric 329a7dea167SDimitry Andric PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, 3300b57cec5SDimitry Andric const ExplodedNode *N, 331a7dea167SDimitry Andric PathSensitiveBugReport &BR) override; 3320b57cec5SDimitry Andric }; 3330b57cec5SDimitry Andric 3340b57cec5SDimitry Andric class RefLeakReportVisitor : public RefCountReportVisitor { 3350b57cec5SDimitry Andric public: 336fe6060f1SDimitry Andric RefLeakReportVisitor(SymbolRef Sym, const MemRegion *LastBinding) 337fe6060f1SDimitry Andric : RefCountReportVisitor(Sym), LastBinding(LastBinding) {} 3380b57cec5SDimitry Andric 339a7dea167SDimitry Andric PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, 3400b57cec5SDimitry Andric const ExplodedNode *N, 341a7dea167SDimitry Andric PathSensitiveBugReport &BR) override; 342fe6060f1SDimitry Andric 343fe6060f1SDimitry Andric private: 344fe6060f1SDimitry Andric const MemRegion *LastBinding; 3450b57cec5SDimitry Andric }; 3460b57cec5SDimitry Andric 3470b57cec5SDimitry Andric } // end namespace retaincountchecker 3480b57cec5SDimitry Andric } // end namespace ento 3490b57cec5SDimitry Andric } // end namespace clang 3500b57cec5SDimitry Andric 3510b57cec5SDimitry Andric 3520b57cec5SDimitry Andric /// Find the first node with the parent stack frame. 3530b57cec5SDimitry Andric static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) { 3540b57cec5SDimitry Andric const StackFrameContext *SC = Pred->getStackFrame(); 3550b57cec5SDimitry Andric if (SC->inTopFrame()) 3560b57cec5SDimitry Andric return nullptr; 3570b57cec5SDimitry Andric const StackFrameContext *PC = SC->getParent()->getStackFrame(); 3580b57cec5SDimitry Andric if (!PC) 3590b57cec5SDimitry Andric return nullptr; 3600b57cec5SDimitry Andric 3610b57cec5SDimitry Andric const ExplodedNode *N = Pred; 3620b57cec5SDimitry Andric while (N && N->getStackFrame() != PC) { 3630b57cec5SDimitry Andric N = N->getFirstPred(); 3640b57cec5SDimitry Andric } 3650b57cec5SDimitry Andric return N; 3660b57cec5SDimitry Andric } 3670b57cec5SDimitry Andric 3680b57cec5SDimitry Andric 3690b57cec5SDimitry Andric /// Insert a diagnostic piece at function exit 3700b57cec5SDimitry Andric /// if a function parameter is annotated as "os_consumed", 3710b57cec5SDimitry Andric /// but it does not actually consume the reference. 3720b57cec5SDimitry Andric static std::shared_ptr<PathDiagnosticEventPiece> 3730b57cec5SDimitry Andric annotateConsumedSummaryMismatch(const ExplodedNode *N, 3740b57cec5SDimitry Andric CallExitBegin &CallExitLoc, 3750b57cec5SDimitry Andric const SourceManager &SM, 3760b57cec5SDimitry Andric CallEventManager &CEMgr) { 3770b57cec5SDimitry Andric 3780b57cec5SDimitry Andric const ExplodedNode *CN = getCalleeNode(N); 3790b57cec5SDimitry Andric if (!CN) 3800b57cec5SDimitry Andric return nullptr; 3810b57cec5SDimitry Andric 3820b57cec5SDimitry Andric CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState()); 3830b57cec5SDimitry Andric 3840b57cec5SDimitry Andric std::string sbuf; 3850b57cec5SDimitry Andric llvm::raw_string_ostream os(sbuf); 3860b57cec5SDimitry Andric ArrayRef<const ParmVarDecl *> Parameters = Call->parameters(); 3870b57cec5SDimitry Andric for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) { 3880b57cec5SDimitry Andric const ParmVarDecl *PVD = Parameters[I]; 3890b57cec5SDimitry Andric 3900b57cec5SDimitry Andric if (!PVD->hasAttr<OSConsumedAttr>()) 3910b57cec5SDimitry Andric continue; 3920b57cec5SDimitry Andric 3930b57cec5SDimitry Andric if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) { 3940b57cec5SDimitry Andric const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR); 3950b57cec5SDimitry Andric const RefVal *CountAtExit = getRefBinding(N->getState(), SR); 3960b57cec5SDimitry Andric 3970b57cec5SDimitry Andric if (!CountBeforeCall || !CountAtExit) 3980b57cec5SDimitry Andric continue; 3990b57cec5SDimitry Andric 4000b57cec5SDimitry Andric unsigned CountBefore = CountBeforeCall->getCount(); 4010b57cec5SDimitry Andric unsigned CountAfter = CountAtExit->getCount(); 4020b57cec5SDimitry Andric 4030b57cec5SDimitry Andric bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1; 4040b57cec5SDimitry Andric if (!AsExpected) { 4050b57cec5SDimitry Andric os << "Parameter '"; 4060b57cec5SDimitry Andric PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(), 4070b57cec5SDimitry Andric /*Qualified=*/false); 4080b57cec5SDimitry Andric os << "' is marked as consuming, but the function did not consume " 4090b57cec5SDimitry Andric << "the reference\n"; 4100b57cec5SDimitry Andric } 4110b57cec5SDimitry Andric } 4120b57cec5SDimitry Andric } 4130b57cec5SDimitry Andric 414*0fca6ea1SDimitry Andric if (sbuf.empty()) 4150b57cec5SDimitry Andric return nullptr; 4160b57cec5SDimitry Andric 4170b57cec5SDimitry Andric PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM); 418*0fca6ea1SDimitry Andric return std::make_shared<PathDiagnosticEventPiece>(L, sbuf); 4190b57cec5SDimitry Andric } 4200b57cec5SDimitry Andric 4210b57cec5SDimitry Andric /// Annotate the parameter at the analysis entry point. 4220b57cec5SDimitry Andric static std::shared_ptr<PathDiagnosticEventPiece> 4230b57cec5SDimitry Andric annotateStartParameter(const ExplodedNode *N, SymbolRef Sym, 4240b57cec5SDimitry Andric const SourceManager &SM) { 4250b57cec5SDimitry Andric auto PP = N->getLocationAs<BlockEdge>(); 4260b57cec5SDimitry Andric if (!PP) 4270b57cec5SDimitry Andric return nullptr; 4280b57cec5SDimitry Andric 4290b57cec5SDimitry Andric const CFGBlock *Src = PP->getSrc(); 4300b57cec5SDimitry Andric const RefVal *CurrT = getRefBinding(N->getState(), Sym); 4310b57cec5SDimitry Andric 4320b57cec5SDimitry Andric if (&Src->getParent()->getEntry() != Src || !CurrT || 4330b57cec5SDimitry Andric getRefBinding(N->getFirstPred()->getState(), Sym)) 4340b57cec5SDimitry Andric return nullptr; 4350b57cec5SDimitry Andric 4360b57cec5SDimitry Andric const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion()); 4370b57cec5SDimitry Andric const auto *PVD = cast<ParmVarDecl>(VR->getDecl()); 4380b57cec5SDimitry Andric PathDiagnosticLocation L = PathDiagnosticLocation(PVD, SM); 4390b57cec5SDimitry Andric 4400b57cec5SDimitry Andric std::string s; 4410b57cec5SDimitry Andric llvm::raw_string_ostream os(s); 442e8d8bef9SDimitry Andric os << "Parameter '" << PVD->getDeclName() << "' starts at +"; 4430b57cec5SDimitry Andric if (CurrT->getCount() == 1) { 4440b57cec5SDimitry Andric os << "1, as it is marked as consuming"; 4450b57cec5SDimitry Andric } else { 4460b57cec5SDimitry Andric assert(CurrT->getCount() == 0); 4470b57cec5SDimitry Andric os << "0"; 4480b57cec5SDimitry Andric } 449*0fca6ea1SDimitry Andric return std::make_shared<PathDiagnosticEventPiece>(L, s); 4500b57cec5SDimitry Andric } 4510b57cec5SDimitry Andric 452a7dea167SDimitry Andric PathDiagnosticPieceRef 453a7dea167SDimitry Andric RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, 454a7dea167SDimitry Andric PathSensitiveBugReport &BR) { 4550b57cec5SDimitry Andric 4560b57cec5SDimitry Andric const auto &BT = static_cast<const RefCountBug&>(BR.getBugType()); 4570b57cec5SDimitry Andric 4580b57cec5SDimitry Andric bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned || 4590b57cec5SDimitry Andric BT.getBugType() == RefCountBug::DeallocNotOwned; 4600b57cec5SDimitry Andric 4610b57cec5SDimitry Andric const SourceManager &SM = BRC.getSourceManager(); 4620b57cec5SDimitry Andric CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager(); 4630b57cec5SDimitry Andric if (auto CE = N->getLocationAs<CallExitBegin>()) 4640b57cec5SDimitry Andric if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr)) 4650b57cec5SDimitry Andric return PD; 4660b57cec5SDimitry Andric 4670b57cec5SDimitry Andric if (auto PD = annotateStartParameter(N, Sym, SM)) 4680b57cec5SDimitry Andric return PD; 4690b57cec5SDimitry Andric 4700b57cec5SDimitry Andric // FIXME: We will eventually need to handle non-statement-based events 4710b57cec5SDimitry Andric // (__attribute__((cleanup))). 4720b57cec5SDimitry Andric if (!N->getLocation().getAs<StmtPoint>()) 4730b57cec5SDimitry Andric return nullptr; 4740b57cec5SDimitry Andric 4750b57cec5SDimitry Andric // Check if the type state has changed. 4760b57cec5SDimitry Andric const ExplodedNode *PrevNode = N->getFirstPred(); 4770b57cec5SDimitry Andric ProgramStateRef PrevSt = PrevNode->getState(); 4780b57cec5SDimitry Andric ProgramStateRef CurrSt = N->getState(); 4790b57cec5SDimitry Andric const LocationContext *LCtx = N->getLocationContext(); 4800b57cec5SDimitry Andric 4810b57cec5SDimitry Andric const RefVal* CurrT = getRefBinding(CurrSt, Sym); 4820b57cec5SDimitry Andric if (!CurrT) 4830b57cec5SDimitry Andric return nullptr; 4840b57cec5SDimitry Andric 4850b57cec5SDimitry Andric const RefVal &CurrV = *CurrT; 4860b57cec5SDimitry Andric const RefVal *PrevT = getRefBinding(PrevSt, Sym); 4870b57cec5SDimitry Andric 4880b57cec5SDimitry Andric // Create a string buffer to constain all the useful things we want 4890b57cec5SDimitry Andric // to tell the user. 4900b57cec5SDimitry Andric std::string sbuf; 4910b57cec5SDimitry Andric llvm::raw_string_ostream os(sbuf); 4920b57cec5SDimitry Andric 4930b57cec5SDimitry Andric if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) { 4940b57cec5SDimitry Andric os << "Object is now not exclusively owned"; 4950b57cec5SDimitry Andric auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM); 496*0fca6ea1SDimitry Andric return std::make_shared<PathDiagnosticEventPiece>(Pos, sbuf); 4970b57cec5SDimitry Andric } 4980b57cec5SDimitry Andric 4990b57cec5SDimitry Andric // This is the allocation site since the previous node had no bindings 5000b57cec5SDimitry Andric // for this symbol. 5010b57cec5SDimitry Andric if (!PrevT) { 5020b57cec5SDimitry Andric const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); 5030b57cec5SDimitry Andric 5040b57cec5SDimitry Andric if (isa<ObjCIvarRefExpr>(S) && 5050b57cec5SDimitry Andric isSynthesizedAccessor(LCtx->getStackFrame())) { 5060b57cec5SDimitry Andric S = LCtx->getStackFrame()->getCallSite(); 5070b57cec5SDimitry Andric } 5080b57cec5SDimitry Andric 5090b57cec5SDimitry Andric if (isa<ObjCArrayLiteral>(S)) { 5100b57cec5SDimitry Andric os << "NSArray literal is an object with a +0 retain count"; 5110b57cec5SDimitry Andric } else if (isa<ObjCDictionaryLiteral>(S)) { 5120b57cec5SDimitry Andric os << "NSDictionary literal is an object with a +0 retain count"; 5130b57cec5SDimitry Andric } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) { 5140b57cec5SDimitry Andric if (isNumericLiteralExpression(BL->getSubExpr())) 5150b57cec5SDimitry Andric os << "NSNumber literal is an object with a +0 retain count"; 5160b57cec5SDimitry Andric else { 5170b57cec5SDimitry Andric const ObjCInterfaceDecl *BoxClass = nullptr; 5180b57cec5SDimitry Andric if (const ObjCMethodDecl *Method = BL->getBoxingMethod()) 5190b57cec5SDimitry Andric BoxClass = Method->getClassInterface(); 5200b57cec5SDimitry Andric 5210b57cec5SDimitry Andric // We should always be able to find the boxing class interface, 5220b57cec5SDimitry Andric // but consider this future-proofing. 5230b57cec5SDimitry Andric if (BoxClass) { 5240b57cec5SDimitry Andric os << *BoxClass << " b"; 5250b57cec5SDimitry Andric } else { 5260b57cec5SDimitry Andric os << "B"; 5270b57cec5SDimitry Andric } 5280b57cec5SDimitry Andric 5290b57cec5SDimitry Andric os << "oxed expression produces an object with a +0 retain count"; 5300b57cec5SDimitry Andric } 5310b57cec5SDimitry Andric } else if (isa<ObjCIvarRefExpr>(S)) { 5320b57cec5SDimitry Andric os << "Object loaded from instance variable"; 5330b57cec5SDimitry Andric } else { 5340b57cec5SDimitry Andric generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os); 5350b57cec5SDimitry Andric } 5360b57cec5SDimitry Andric 5370b57cec5SDimitry Andric PathDiagnosticLocation Pos(S, SM, N->getLocationContext()); 538*0fca6ea1SDimitry Andric return std::make_shared<PathDiagnosticEventPiece>(Pos, sbuf); 5390b57cec5SDimitry Andric } 5400b57cec5SDimitry Andric 5410b57cec5SDimitry Andric // Gather up the effects that were performed on the object at this 5420b57cec5SDimitry Andric // program point 5430b57cec5SDimitry Andric bool DeallocSent = false; 5440b57cec5SDimitry Andric 5450b57cec5SDimitry Andric const ProgramPointTag *Tag = N->getLocation().getTag(); 5460b57cec5SDimitry Andric 5475ffd83dbSDimitry Andric if (Tag == &RetainCountChecker::getCastFailTag()) { 5480b57cec5SDimitry Andric os << "Assuming dynamic cast returns null due to type mismatch"; 5490b57cec5SDimitry Andric } 5500b57cec5SDimitry Andric 5515ffd83dbSDimitry Andric if (Tag == &RetainCountChecker::getDeallocSentTag()) { 5520b57cec5SDimitry Andric // We only have summaries attached to nodes after evaluating CallExpr and 5530b57cec5SDimitry Andric // ObjCMessageExprs. 5540b57cec5SDimitry Andric const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); 5550b57cec5SDimitry Andric 5560b57cec5SDimitry Andric if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { 5570b57cec5SDimitry Andric // Iterate through the parameter expressions and see if the symbol 5580b57cec5SDimitry Andric // was ever passed as an argument. 5590b57cec5SDimitry Andric unsigned i = 0; 5600b57cec5SDimitry Andric 5610b57cec5SDimitry Andric for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) { 5620b57cec5SDimitry Andric 5630b57cec5SDimitry Andric // Retrieve the value of the argument. Is it the symbol 5640b57cec5SDimitry Andric // we are interested in? 5650b57cec5SDimitry Andric if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym) 5660b57cec5SDimitry Andric continue; 5670b57cec5SDimitry Andric 5680b57cec5SDimitry Andric // We have an argument. Get the effect! 5690b57cec5SDimitry Andric DeallocSent = true; 5700b57cec5SDimitry Andric } 5710b57cec5SDimitry Andric } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { 5720b57cec5SDimitry Andric if (const Expr *receiver = ME->getInstanceReceiver()) { 5730b57cec5SDimitry Andric if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx) 5740b57cec5SDimitry Andric .getAsLocSymbol() == Sym) { 5750b57cec5SDimitry Andric // The symbol we are tracking is the receiver. 5760b57cec5SDimitry Andric DeallocSent = true; 5770b57cec5SDimitry Andric } 5780b57cec5SDimitry Andric } 5790b57cec5SDimitry Andric } 5800b57cec5SDimitry Andric } 5810b57cec5SDimitry Andric 5820b57cec5SDimitry Andric if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent)) 5830b57cec5SDimitry Andric return nullptr; 5840b57cec5SDimitry Andric 585*0fca6ea1SDimitry Andric if (sbuf.empty()) 5860b57cec5SDimitry Andric return nullptr; // We have nothing to say! 5870b57cec5SDimitry Andric 5880b57cec5SDimitry Andric const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); 5890b57cec5SDimitry Andric PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 5900b57cec5SDimitry Andric N->getLocationContext()); 591*0fca6ea1SDimitry Andric auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, sbuf); 5920b57cec5SDimitry Andric 5930b57cec5SDimitry Andric // Add the range by scanning the children of the statement for any bindings 5940b57cec5SDimitry Andric // to Sym. 5950b57cec5SDimitry Andric for (const Stmt *Child : S->children()) 5960b57cec5SDimitry Andric if (const Expr *Exp = dyn_cast_or_null<Expr>(Child)) 5970b57cec5SDimitry Andric if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) { 5980b57cec5SDimitry Andric P->addRange(Exp->getSourceRange()); 5990b57cec5SDimitry Andric break; 6000b57cec5SDimitry Andric } 6010b57cec5SDimitry Andric 6020b57cec5SDimitry Andric return std::move(P); 6030b57cec5SDimitry Andric } 6040b57cec5SDimitry Andric 605bdd1243dSDimitry Andric static std::optional<std::string> describeRegion(const MemRegion *MR) { 6060b57cec5SDimitry Andric if (const auto *VR = dyn_cast_or_null<VarRegion>(MR)) 6070b57cec5SDimitry Andric return std::string(VR->getDecl()->getName()); 6080b57cec5SDimitry Andric // Once we support more storage locations for bindings, 6090b57cec5SDimitry Andric // this would need to be improved. 610bdd1243dSDimitry Andric return std::nullopt; 6110b57cec5SDimitry Andric } 6120b57cec5SDimitry Andric 613fe6060f1SDimitry Andric using Bindings = llvm::SmallVector<std::pair<const MemRegion *, SVal>, 4>; 614fe6060f1SDimitry Andric 615bdd1243dSDimitry Andric namespace { 616fe6060f1SDimitry Andric class VarBindingsCollector : public StoreManager::BindingsHandler { 617fe6060f1SDimitry Andric SymbolRef Sym; 618fe6060f1SDimitry Andric Bindings &Result; 619fe6060f1SDimitry Andric 620fe6060f1SDimitry Andric public: 621fe6060f1SDimitry Andric VarBindingsCollector(SymbolRef Sym, Bindings &ToFill) 622fe6060f1SDimitry Andric : Sym(Sym), Result(ToFill) {} 623fe6060f1SDimitry Andric 624fe6060f1SDimitry Andric bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *R, 625fe6060f1SDimitry Andric SVal Val) override { 626fe6060f1SDimitry Andric SymbolRef SymV = Val.getAsLocSymbol(); 627fe6060f1SDimitry Andric if (!SymV || SymV != Sym) 628fe6060f1SDimitry Andric return true; 629fe6060f1SDimitry Andric 630fe6060f1SDimitry Andric if (isa<NonParamVarRegion>(R)) 631fe6060f1SDimitry Andric Result.emplace_back(R, Val); 632fe6060f1SDimitry Andric 633fe6060f1SDimitry Andric return true; 634fe6060f1SDimitry Andric } 635fe6060f1SDimitry Andric }; 636bdd1243dSDimitry Andric } // namespace 637fe6060f1SDimitry Andric 638fe6060f1SDimitry Andric Bindings getAllVarBindingsForSymbol(ProgramStateManager &Manager, 639fe6060f1SDimitry Andric const ExplodedNode *Node, SymbolRef Sym) { 640fe6060f1SDimitry Andric Bindings Result; 641fe6060f1SDimitry Andric VarBindingsCollector Collector{Sym, Result}; 642fe6060f1SDimitry Andric while (Result.empty() && Node) { 643fe6060f1SDimitry Andric Manager.iterBindings(Node->getState(), Collector); 644fe6060f1SDimitry Andric Node = Node->getFirstPred(); 645fe6060f1SDimitry Andric } 646fe6060f1SDimitry Andric 647fe6060f1SDimitry Andric return Result; 648fe6060f1SDimitry Andric } 649fe6060f1SDimitry Andric 6500b57cec5SDimitry Andric namespace { 6510b57cec5SDimitry Andric // Find the first node in the current function context that referred to the 6520b57cec5SDimitry Andric // tracked symbol and the memory location that value was stored to. Note, the 6530b57cec5SDimitry Andric // value is only reported if the allocation occurred in the same function as 6540b57cec5SDimitry Andric // the leak. The function can also return a location context, which should be 6550b57cec5SDimitry Andric // treated as interesting. 6560b57cec5SDimitry Andric struct AllocationInfo { 6570b57cec5SDimitry Andric const ExplodedNode* N; 6580b57cec5SDimitry Andric const MemRegion *R; 6590b57cec5SDimitry Andric const LocationContext *InterestingMethodContext; 6600b57cec5SDimitry Andric AllocationInfo(const ExplodedNode *InN, 6610b57cec5SDimitry Andric const MemRegion *InR, 6620b57cec5SDimitry Andric const LocationContext *InInterestingMethodContext) : 6630b57cec5SDimitry Andric N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {} 6640b57cec5SDimitry Andric }; 6650b57cec5SDimitry Andric } // end anonymous namespace 6660b57cec5SDimitry Andric 6670b57cec5SDimitry Andric static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, 6680b57cec5SDimitry Andric const ExplodedNode *N, SymbolRef Sym) { 6690b57cec5SDimitry Andric const ExplodedNode *AllocationNode = N; 6700b57cec5SDimitry Andric const ExplodedNode *AllocationNodeInCurrentOrParentContext = N; 6710b57cec5SDimitry Andric const MemRegion *FirstBinding = nullptr; 6720b57cec5SDimitry Andric const LocationContext *LeakContext = N->getLocationContext(); 6730b57cec5SDimitry Andric 6740b57cec5SDimitry Andric // The location context of the init method called on the leaked object, if 6750b57cec5SDimitry Andric // available. 6760b57cec5SDimitry Andric const LocationContext *InitMethodContext = nullptr; 6770b57cec5SDimitry Andric 6780b57cec5SDimitry Andric while (N) { 6790b57cec5SDimitry Andric ProgramStateRef St = N->getState(); 6800b57cec5SDimitry Andric const LocationContext *NContext = N->getLocationContext(); 6810b57cec5SDimitry Andric 6820b57cec5SDimitry Andric if (!getRefBinding(St, Sym)) 6830b57cec5SDimitry Andric break; 6840b57cec5SDimitry Andric 6850b57cec5SDimitry Andric StoreManager::FindUniqueBinding FB(Sym); 6860b57cec5SDimitry Andric StateMgr.iterBindings(St, FB); 6870b57cec5SDimitry Andric 6880b57cec5SDimitry Andric if (FB) { 6890b57cec5SDimitry Andric const MemRegion *R = FB.getRegion(); 6900b57cec5SDimitry Andric // Do not show local variables belonging to a function other than 6910b57cec5SDimitry Andric // where the error is reported. 6920b57cec5SDimitry Andric if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace())) 6930b57cec5SDimitry Andric if (MR->getStackFrame() == LeakContext->getStackFrame()) 6940b57cec5SDimitry Andric FirstBinding = R; 6950b57cec5SDimitry Andric } 6960b57cec5SDimitry Andric 6970b57cec5SDimitry Andric // AllocationNode is the last node in which the symbol was tracked. 6980b57cec5SDimitry Andric AllocationNode = N; 6990b57cec5SDimitry Andric 7000b57cec5SDimitry Andric // AllocationNodeInCurrentContext, is the last node in the current or 7010b57cec5SDimitry Andric // parent context in which the symbol was tracked. 7020b57cec5SDimitry Andric // 7030b57cec5SDimitry Andric // Note that the allocation site might be in the parent context. For example, 7040b57cec5SDimitry Andric // the case where an allocation happens in a block that captures a reference 7050b57cec5SDimitry Andric // to it and that reference is overwritten/dropped by another call to 7060b57cec5SDimitry Andric // the block. 7070b57cec5SDimitry Andric if (NContext == LeakContext || NContext->isParentOf(LeakContext)) 7080b57cec5SDimitry Andric AllocationNodeInCurrentOrParentContext = N; 7090b57cec5SDimitry Andric 7100b57cec5SDimitry Andric // Find the last init that was called on the given symbol and store the 7110b57cec5SDimitry Andric // init method's location context. 7120b57cec5SDimitry Andric if (!InitMethodContext) 7130b57cec5SDimitry Andric if (auto CEP = N->getLocation().getAs<CallEnter>()) { 7140b57cec5SDimitry Andric const Stmt *CE = CEP->getCallExpr(); 7150b57cec5SDimitry Andric if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) { 7160b57cec5SDimitry Andric const Stmt *RecExpr = ME->getInstanceReceiver(); 7170b57cec5SDimitry Andric if (RecExpr) { 7180b57cec5SDimitry Andric SVal RecV = St->getSVal(RecExpr, NContext); 7190b57cec5SDimitry Andric if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym) 7200b57cec5SDimitry Andric InitMethodContext = CEP->getCalleeContext(); 7210b57cec5SDimitry Andric } 7220b57cec5SDimitry Andric } 7230b57cec5SDimitry Andric } 7240b57cec5SDimitry Andric 7250b57cec5SDimitry Andric N = N->getFirstPred(); 7260b57cec5SDimitry Andric } 7270b57cec5SDimitry Andric 7280b57cec5SDimitry Andric // If we are reporting a leak of the object that was allocated with alloc, 7290b57cec5SDimitry Andric // mark its init method as interesting. 7300b57cec5SDimitry Andric const LocationContext *InterestingMethodContext = nullptr; 7310b57cec5SDimitry Andric if (InitMethodContext) { 7320b57cec5SDimitry Andric const ProgramPoint AllocPP = AllocationNode->getLocation(); 733bdd1243dSDimitry Andric if (std::optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) 7340b57cec5SDimitry Andric if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>()) 7350b57cec5SDimitry Andric if (ME->getMethodFamily() == OMF_alloc) 7360b57cec5SDimitry Andric InterestingMethodContext = InitMethodContext; 7370b57cec5SDimitry Andric } 7380b57cec5SDimitry Andric 7390b57cec5SDimitry Andric // If allocation happened in a function different from the leak node context, 7400b57cec5SDimitry Andric // do not report the binding. 7410b57cec5SDimitry Andric assert(N && "Could not find allocation node"); 7420b57cec5SDimitry Andric 7430b57cec5SDimitry Andric if (AllocationNodeInCurrentOrParentContext && 7440b57cec5SDimitry Andric AllocationNodeInCurrentOrParentContext->getLocationContext() != 7450b57cec5SDimitry Andric LeakContext) 7460b57cec5SDimitry Andric FirstBinding = nullptr; 7470b57cec5SDimitry Andric 748a7dea167SDimitry Andric return AllocationInfo(AllocationNodeInCurrentOrParentContext, FirstBinding, 7490b57cec5SDimitry Andric InterestingMethodContext); 7500b57cec5SDimitry Andric } 7510b57cec5SDimitry Andric 752a7dea167SDimitry Andric PathDiagnosticPieceRef 7530b57cec5SDimitry Andric RefCountReportVisitor::getEndPath(BugReporterContext &BRC, 754a7dea167SDimitry Andric const ExplodedNode *EndN, 755a7dea167SDimitry Andric PathSensitiveBugReport &BR) { 7560b57cec5SDimitry Andric BR.markInteresting(Sym); 7570b57cec5SDimitry Andric return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR); 7580b57cec5SDimitry Andric } 7590b57cec5SDimitry Andric 760a7dea167SDimitry Andric PathDiagnosticPieceRef 7610b57cec5SDimitry Andric RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, 762a7dea167SDimitry Andric const ExplodedNode *EndN, 763a7dea167SDimitry Andric PathSensitiveBugReport &BR) { 7640b57cec5SDimitry Andric 7650b57cec5SDimitry Andric // Tell the BugReporterContext to report cases when the tracked symbol is 7660b57cec5SDimitry Andric // assigned to different variables, etc. 7670b57cec5SDimitry Andric BR.markInteresting(Sym); 7680b57cec5SDimitry Andric 769a7dea167SDimitry Andric PathDiagnosticLocation L = cast<RefLeakReport>(BR).getEndOfPath(); 7700b57cec5SDimitry Andric 7710b57cec5SDimitry Andric std::string sbuf; 7720b57cec5SDimitry Andric llvm::raw_string_ostream os(sbuf); 7730b57cec5SDimitry Andric 7740b57cec5SDimitry Andric os << "Object leaked: "; 7750b57cec5SDimitry Andric 776bdd1243dSDimitry Andric std::optional<std::string> RegionDescription = describeRegion(LastBinding); 7770b57cec5SDimitry Andric if (RegionDescription) { 7780b57cec5SDimitry Andric os << "object allocated and stored into '" << *RegionDescription << '\''; 7790b57cec5SDimitry Andric } else { 7800b57cec5SDimitry Andric os << "allocated object of type '" << getPrettyTypeName(Sym->getType()) 7810b57cec5SDimitry Andric << "'"; 7820b57cec5SDimitry Andric } 7830b57cec5SDimitry Andric 7840b57cec5SDimitry Andric // Get the retain count. 7850b57cec5SDimitry Andric const RefVal *RV = getRefBinding(EndN->getState(), Sym); 7860b57cec5SDimitry Andric assert(RV); 7870b57cec5SDimitry Andric 7880b57cec5SDimitry Andric if (RV->getKind() == RefVal::ErrorLeakReturned) { 7890b57cec5SDimitry Andric const Decl *D = &EndN->getCodeDecl(); 7900b57cec5SDimitry Andric 7910b57cec5SDimitry Andric os << (isa<ObjCMethodDecl>(D) ? " is returned from a method " 7920b57cec5SDimitry Andric : " is returned from a function "); 7930b57cec5SDimitry Andric 7940b57cec5SDimitry Andric if (D->hasAttr<CFReturnsNotRetainedAttr>()) { 7950b57cec5SDimitry Andric os << "that is annotated as CF_RETURNS_NOT_RETAINED"; 7960b57cec5SDimitry Andric } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) { 7970b57cec5SDimitry Andric os << "that is annotated as NS_RETURNS_NOT_RETAINED"; 7980b57cec5SDimitry Andric } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) { 7990b57cec5SDimitry Andric os << "that is annotated as OS_RETURNS_NOT_RETAINED"; 8000b57cec5SDimitry Andric } else { 8010b57cec5SDimitry Andric if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { 8020b57cec5SDimitry Andric if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) { 8030b57cec5SDimitry Andric os << "managed by Automatic Reference Counting"; 8040b57cec5SDimitry Andric } else { 8050b57cec5SDimitry Andric os << "whose name ('" << MD->getSelector().getAsString() 8060b57cec5SDimitry Andric << "') does not start with " 8070b57cec5SDimitry Andric "'copy', 'mutableCopy', 'alloc' or 'new'." 8080b57cec5SDimitry Andric " This violates the naming convention rules" 8090b57cec5SDimitry Andric " given in the Memory Management Guide for Cocoa"; 8100b57cec5SDimitry Andric } 8110b57cec5SDimitry Andric } else { 8120b57cec5SDimitry Andric const FunctionDecl *FD = cast<FunctionDecl>(D); 8130b57cec5SDimitry Andric ObjKind K = RV->getObjKind(); 8140b57cec5SDimitry Andric if (K == ObjKind::ObjC || K == ObjKind::CF) { 8150b57cec5SDimitry Andric os << "whose name ('" << *FD 8160b57cec5SDimitry Andric << "') does not contain 'Copy' or 'Create'. This violates the " 8170b57cec5SDimitry Andric "naming" 8180b57cec5SDimitry Andric " convention rules given in the Memory Management Guide for " 8190b57cec5SDimitry Andric "Core" 8200b57cec5SDimitry Andric " Foundation"; 8210b57cec5SDimitry Andric } else if (RV->getObjKind() == ObjKind::OS) { 8220b57cec5SDimitry Andric std::string FuncName = FD->getNameAsString(); 823fe6060f1SDimitry Andric os << "whose name ('" << FuncName << "') starts with '" 824fe6060f1SDimitry Andric << StringRef(FuncName).substr(0, 3) << "'"; 8250b57cec5SDimitry Andric } 8260b57cec5SDimitry Andric } 8270b57cec5SDimitry Andric } 8280b57cec5SDimitry Andric } else { 8290b57cec5SDimitry Andric os << " is not referenced later in this execution path and has a retain " 830fe6060f1SDimitry Andric "count of +" 831fe6060f1SDimitry Andric << RV->getCount(); 8320b57cec5SDimitry Andric } 8330b57cec5SDimitry Andric 834*0fca6ea1SDimitry Andric return std::make_shared<PathDiagnosticEventPiece>(L, sbuf); 8350b57cec5SDimitry Andric } 8360b57cec5SDimitry Andric 8370b57cec5SDimitry Andric RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, 838a7dea167SDimitry Andric ExplodedNode *n, SymbolRef sym, bool isLeak) 839a7dea167SDimitry Andric : PathSensitiveBugReport(D, D.getDescription(), n), Sym(sym), 840a7dea167SDimitry Andric isLeak(isLeak) { 8410b57cec5SDimitry Andric if (!isLeak) 842fe6060f1SDimitry Andric addVisitor<RefCountReportVisitor>(sym); 8430b57cec5SDimitry Andric } 8440b57cec5SDimitry Andric 8450b57cec5SDimitry Andric RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, 8460b57cec5SDimitry Andric ExplodedNode *n, SymbolRef sym, 8470b57cec5SDimitry Andric StringRef endText) 848a7dea167SDimitry Andric : PathSensitiveBugReport(D, D.getDescription(), endText, n) { 8490b57cec5SDimitry Andric 850fe6060f1SDimitry Andric addVisitor<RefCountReportVisitor>(sym); 8510b57cec5SDimitry Andric } 8520b57cec5SDimitry Andric 853fe6060f1SDimitry Andric void RefLeakReport::deriveParamLocation(CheckerContext &Ctx) { 8540b57cec5SDimitry Andric const SourceManager &SMgr = Ctx.getSourceManager(); 8550b57cec5SDimitry Andric 856fe6060f1SDimitry Andric if (!Sym->getOriginRegion()) 8570b57cec5SDimitry Andric return; 8580b57cec5SDimitry Andric 859fe6060f1SDimitry Andric auto *Region = dyn_cast<DeclRegion>(Sym->getOriginRegion()); 8600b57cec5SDimitry Andric if (Region) { 8610b57cec5SDimitry Andric const Decl *PDecl = Region->getDecl(); 862fe6060f1SDimitry Andric if (isa_and_nonnull<ParmVarDecl>(PDecl)) { 8630b57cec5SDimitry Andric PathDiagnosticLocation ParamLocation = 8640b57cec5SDimitry Andric PathDiagnosticLocation::create(PDecl, SMgr); 8650b57cec5SDimitry Andric Location = ParamLocation; 8660b57cec5SDimitry Andric UniqueingLocation = ParamLocation; 8670b57cec5SDimitry Andric UniqueingDecl = Ctx.getLocationContext()->getDecl(); 8680b57cec5SDimitry Andric } 8690b57cec5SDimitry Andric } 8700b57cec5SDimitry Andric } 8710b57cec5SDimitry Andric 872fe6060f1SDimitry Andric void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx) { 8730b57cec5SDimitry Andric // Most bug reports are cached at the location where they occurred. 8740b57cec5SDimitry Andric // With leaks, we want to unique them by the location where they were 8750b57cec5SDimitry Andric // allocated, and only report a single path. To do this, we need to find 8760b57cec5SDimitry Andric // the allocation site of a piece of tracked memory, which we do via a 8770b57cec5SDimitry Andric // call to GetAllocationSite. This will walk the ExplodedGraph backwards. 8780b57cec5SDimitry Andric // Note that this is *not* the trimmed graph; we are guaranteed, however, 8790b57cec5SDimitry Andric // that all ancestor nodes that represent the allocation site have the 8800b57cec5SDimitry Andric // same SourceLocation. 8810b57cec5SDimitry Andric const ExplodedNode *AllocNode = nullptr; 8820b57cec5SDimitry Andric 8830b57cec5SDimitry Andric const SourceManager &SMgr = Ctx.getSourceManager(); 8840b57cec5SDimitry Andric 8850b57cec5SDimitry Andric AllocationInfo AllocI = 886fe6060f1SDimitry Andric GetAllocationSite(Ctx.getStateManager(), getErrorNode(), Sym); 8870b57cec5SDimitry Andric 8880b57cec5SDimitry Andric AllocNode = AllocI.N; 889fe6060f1SDimitry Andric AllocFirstBinding = AllocI.R; 8900b57cec5SDimitry Andric markInteresting(AllocI.InterestingMethodContext); 8910b57cec5SDimitry Andric 8920b57cec5SDimitry Andric // Get the SourceLocation for the allocation site. 8930b57cec5SDimitry Andric // FIXME: This will crash the analyzer if an allocation comes from an 8940b57cec5SDimitry Andric // implicit call (ex: a destructor call). 8950b57cec5SDimitry Andric // (Currently there are no such allocations in Cocoa, though.) 896a7dea167SDimitry Andric AllocStmt = AllocNode->getStmtForDiagnostics(); 8970b57cec5SDimitry Andric 8980b57cec5SDimitry Andric if (!AllocStmt) { 899fe6060f1SDimitry Andric AllocFirstBinding = nullptr; 9000b57cec5SDimitry Andric return; 9010b57cec5SDimitry Andric } 9020b57cec5SDimitry Andric 903fe6060f1SDimitry Andric PathDiagnosticLocation AllocLocation = PathDiagnosticLocation::createBegin( 904fe6060f1SDimitry Andric AllocStmt, SMgr, AllocNode->getLocationContext()); 9050b57cec5SDimitry Andric Location = AllocLocation; 9060b57cec5SDimitry Andric 9070b57cec5SDimitry Andric // Set uniqieing info, which will be used for unique the bug reports. The 9080b57cec5SDimitry Andric // leaks should be uniqued on the allocation site. 9090b57cec5SDimitry Andric UniqueingLocation = AllocLocation; 9100b57cec5SDimitry Andric UniqueingDecl = AllocNode->getLocationContext()->getDecl(); 9110b57cec5SDimitry Andric } 9120b57cec5SDimitry Andric 9130b57cec5SDimitry Andric void RefLeakReport::createDescription(CheckerContext &Ctx) { 9140b57cec5SDimitry Andric assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid()); 9150b57cec5SDimitry Andric Description.clear(); 9160b57cec5SDimitry Andric llvm::raw_string_ostream os(Description); 9170b57cec5SDimitry Andric os << "Potential leak of an object"; 9180b57cec5SDimitry Andric 919bdd1243dSDimitry Andric std::optional<std::string> RegionDescription = 920fe6060f1SDimitry Andric describeRegion(AllocBindingToReport); 9210b57cec5SDimitry Andric if (RegionDescription) { 9220b57cec5SDimitry Andric os << " stored into '" << *RegionDescription << '\''; 9230b57cec5SDimitry Andric } else { 9240b57cec5SDimitry Andric 9250b57cec5SDimitry Andric // If we can't figure out the name, just supply the type information. 9260b57cec5SDimitry Andric os << " of type '" << getPrettyTypeName(Sym->getType()) << "'"; 9270b57cec5SDimitry Andric } 9280b57cec5SDimitry Andric } 9290b57cec5SDimitry Andric 930fe6060f1SDimitry Andric void RefLeakReport::findBindingToReport(CheckerContext &Ctx, 931fe6060f1SDimitry Andric ExplodedNode *Node) { 932fe6060f1SDimitry Andric if (!AllocFirstBinding) 933fe6060f1SDimitry Andric // If we don't have any bindings, we won't be able to find any 934fe6060f1SDimitry Andric // better binding to report. 935fe6060f1SDimitry Andric return; 9360b57cec5SDimitry Andric 937fe6060f1SDimitry Andric // If the original region still contains the leaking symbol... 938fe6060f1SDimitry Andric if (Node->getState()->getSVal(AllocFirstBinding).getAsSymbol() == Sym) { 939fe6060f1SDimitry Andric // ...it is the best binding to report. 940fe6060f1SDimitry Andric AllocBindingToReport = AllocFirstBinding; 941fe6060f1SDimitry Andric return; 942fe6060f1SDimitry Andric } 943fe6060f1SDimitry Andric 944fe6060f1SDimitry Andric // At this point, we know that the original region doesn't contain the leaking 945fe6060f1SDimitry Andric // when the actual leak happens. It means that it can be confusing for the 946fe6060f1SDimitry Andric // user to see such description in the message. 947fe6060f1SDimitry Andric // 948fe6060f1SDimitry Andric // Let's consider the following example: 949fe6060f1SDimitry Andric // Object *Original = allocate(...); 950fe6060f1SDimitry Andric // Object *New = Original; 951fe6060f1SDimitry Andric // Original = allocate(...); 952fe6060f1SDimitry Andric // Original->release(); 953fe6060f1SDimitry Andric // 954fe6060f1SDimitry Andric // Complaining about a leaking object "stored into Original" might cause a 955fe6060f1SDimitry Andric // rightful confusion because 'Original' is actually released. 956fe6060f1SDimitry Andric // We should complain about 'New' instead. 957fe6060f1SDimitry Andric Bindings AllVarBindings = 958fe6060f1SDimitry Andric getAllVarBindingsForSymbol(Ctx.getStateManager(), Node, Sym); 959fe6060f1SDimitry Andric 960fe6060f1SDimitry Andric // While looking for the last var bindings, we can still find 961fe6060f1SDimitry Andric // `AllocFirstBinding` to be one of them. In situations like this, 962fe6060f1SDimitry Andric // it would still be the easiest case to explain to our users. 963fe6060f1SDimitry Andric if (!AllVarBindings.empty() && 964fe6060f1SDimitry Andric llvm::count_if(AllVarBindings, 965fe6060f1SDimitry Andric [this](const std::pair<const MemRegion *, SVal> Binding) { 966fe6060f1SDimitry Andric return Binding.first == AllocFirstBinding; 967fe6060f1SDimitry Andric }) == 0) { 968fe6060f1SDimitry Andric // Let's pick one of them at random (if there is something to pick from). 969fe6060f1SDimitry Andric AllocBindingToReport = AllVarBindings[0].first; 970fe6060f1SDimitry Andric 971bdd1243dSDimitry Andric // Because 'AllocBindingToReport' is not the same as 972fe6060f1SDimitry Andric // 'AllocFirstBinding', we need to explain how the leaking object 973fe6060f1SDimitry Andric // got from one to another. 974fe6060f1SDimitry Andric // 975fe6060f1SDimitry Andric // NOTE: We use the actual SVal stored in AllocBindingToReport here because 976fe6060f1SDimitry Andric // trackStoredValue compares SVal's and it can get trickier for 977fe6060f1SDimitry Andric // something like derived regions if we want to construct SVal from 978fe6060f1SDimitry Andric // Sym. Instead, we take the value that is definitely stored in that 979fe6060f1SDimitry Andric // region, thus guaranteeing that trackStoredValue will work. 980*0fca6ea1SDimitry Andric bugreporter::trackStoredValue(AllVarBindings[0].second, 981fe6060f1SDimitry Andric AllocBindingToReport, *this); 982fe6060f1SDimitry Andric } else { 983fe6060f1SDimitry Andric AllocBindingToReport = AllocFirstBinding; 984fe6060f1SDimitry Andric } 985fe6060f1SDimitry Andric } 986fe6060f1SDimitry Andric 987fe6060f1SDimitry Andric RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, 988fe6060f1SDimitry Andric ExplodedNode *N, SymbolRef Sym, 989fe6060f1SDimitry Andric CheckerContext &Ctx) 990fe6060f1SDimitry Andric : RefCountReport(D, LOpts, N, Sym, /*isLeak=*/true) { 991fe6060f1SDimitry Andric 992fe6060f1SDimitry Andric deriveAllocLocation(Ctx); 993fe6060f1SDimitry Andric findBindingToReport(Ctx, N); 994fe6060f1SDimitry Andric 995fe6060f1SDimitry Andric if (!AllocFirstBinding) 996fe6060f1SDimitry Andric deriveParamLocation(Ctx); 9970b57cec5SDimitry Andric 9980b57cec5SDimitry Andric createDescription(Ctx); 9990b57cec5SDimitry Andric 1000fe6060f1SDimitry Andric addVisitor<RefLeakReportVisitor>(Sym, AllocBindingToReport); 10010b57cec5SDimitry Andric } 1002