1 // RetainCountDiagnostics.cpp - Checks for leaks and other issues -*- C++ -*--// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines diagnostics for RetainCountChecker, which implements 10 // a reference count checker for Core Foundation and Cocoa on (Mac OS X). 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "RetainCountDiagnostics.h" 15 #include "RetainCountChecker.h" 16 17 using namespace clang; 18 using namespace ento; 19 using namespace retaincountchecker; 20 21 StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) { 22 switch (BT) { 23 case UseAfterRelease: 24 return "Use-after-release"; 25 case ReleaseNotOwned: 26 return "Bad release"; 27 case DeallocNotOwned: 28 return "-dealloc sent to non-exclusively owned object"; 29 case FreeNotOwned: 30 return "freeing non-exclusively owned object"; 31 case OverAutorelease: 32 return "Object autoreleased too many times"; 33 case ReturnNotOwnedForOwned: 34 return "Method should return an owned object"; 35 case LeakWithinFunction: 36 return "Leak"; 37 case LeakAtReturn: 38 return "Leak of returned object"; 39 } 40 llvm_unreachable("Unknown RefCountBugType"); 41 } 42 43 StringRef RefCountBug::getDescription() const { 44 switch (BT) { 45 case UseAfterRelease: 46 return "Reference-counted object is used after it is released"; 47 case ReleaseNotOwned: 48 return "Incorrect decrement of the reference count of an object that is " 49 "not owned at this point by the caller"; 50 case DeallocNotOwned: 51 return "-dealloc sent to object that may be referenced elsewhere"; 52 case FreeNotOwned: 53 return "'free' called on an object that may be referenced elsewhere"; 54 case OverAutorelease: 55 return "Object autoreleased too many times"; 56 case ReturnNotOwnedForOwned: 57 return "Object with a +0 retain count returned to caller where a +1 " 58 "(owning) retain count is expected"; 59 case LeakWithinFunction: 60 case LeakAtReturn: 61 return ""; 62 } 63 llvm_unreachable("Unknown RefCountBugType"); 64 } 65 66 RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT) 67 : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount, 68 /*SupressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn), 69 BT(BT), Checker(Checker) {} 70 71 static bool isNumericLiteralExpression(const Expr *E) { 72 // FIXME: This set of cases was copied from SemaExprObjC. 73 return isa<IntegerLiteral>(E) || 74 isa<CharacterLiteral>(E) || 75 isa<FloatingLiteral>(E) || 76 isa<ObjCBoolLiteralExpr>(E) || 77 isa<CXXBoolLiteralExpr>(E); 78 } 79 80 /// If type represents a pointer to CXXRecordDecl, 81 /// and is not a typedef, return the decl name. 82 /// Otherwise, return the serialization of type. 83 static std::string getPrettyTypeName(QualType QT) { 84 QualType PT = QT->getPointeeType(); 85 if (!PT.isNull() && !QT->getAs<TypedefType>()) 86 if (const auto *RD = PT->getAsCXXRecordDecl()) 87 return RD->getName(); 88 return QT.getAsString(); 89 } 90 91 /// Write information about the type state change to {@code os}, 92 /// return whether the note should be generated. 93 static bool shouldGenerateNote(llvm::raw_string_ostream &os, 94 const RefVal *PrevT, 95 const RefVal &CurrV, 96 bool DeallocSent) { 97 // Get the previous type state. 98 RefVal PrevV = *PrevT; 99 100 // Specially handle -dealloc. 101 if (DeallocSent) { 102 // Determine if the object's reference count was pushed to zero. 103 assert(!PrevV.hasSameState(CurrV) && "The state should have changed."); 104 // We may not have transitioned to 'release' if we hit an error. 105 // This case is handled elsewhere. 106 if (CurrV.getKind() == RefVal::Released) { 107 assert(CurrV.getCombinedCounts() == 0); 108 os << "Object released by directly sending the '-dealloc' message"; 109 return true; 110 } 111 } 112 113 // Determine if the typestate has changed. 114 if (!PrevV.hasSameState(CurrV)) 115 switch (CurrV.getKind()) { 116 case RefVal::Owned: 117 case RefVal::NotOwned: 118 if (PrevV.getCount() == CurrV.getCount()) { 119 // Did an autorelease message get sent? 120 if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount()) 121 return false; 122 123 assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount()); 124 os << "Object autoreleased"; 125 return true; 126 } 127 128 if (PrevV.getCount() > CurrV.getCount()) 129 os << "Reference count decremented."; 130 else 131 os << "Reference count incremented."; 132 133 if (unsigned Count = CurrV.getCount()) 134 os << " The object now has a +" << Count << " retain count."; 135 136 return true; 137 138 case RefVal::Released: 139 if (CurrV.getIvarAccessHistory() == 140 RefVal::IvarAccessHistory::ReleasedAfterDirectAccess && 141 CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) { 142 os << "Strong instance variable relinquished. "; 143 } 144 os << "Object released."; 145 return true; 146 147 case RefVal::ReturnedOwned: 148 // Autoreleases can be applied after marking a node ReturnedOwned. 149 if (CurrV.getAutoreleaseCount()) 150 return false; 151 152 os << "Object returned to caller as an owning reference (single " 153 "retain count transferred to caller)"; 154 return true; 155 156 case RefVal::ReturnedNotOwned: 157 os << "Object returned to caller with a +0 retain count"; 158 return true; 159 160 default: 161 return false; 162 } 163 return true; 164 } 165 166 /// Finds argument index of the out paramter in the call {@code S} 167 /// corresponding to the symbol {@code Sym}. 168 /// If none found, returns None. 169 static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt, 170 const LocationContext *LCtx, 171 SymbolRef &Sym, 172 Optional<CallEventRef<>> CE) { 173 if (!CE) 174 return None; 175 176 for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++) 177 if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion()) 178 if (const auto *TR = dyn_cast<TypedValueRegion>(MR)) 179 if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymExpr() == Sym) 180 return Idx; 181 182 return None; 183 } 184 185 Optional<std::string> findMetaClassAlloc(const Expr *Callee) { 186 if (const auto *ME = dyn_cast<MemberExpr>(Callee)) { 187 if (ME->getMemberDecl()->getNameAsString() != "alloc") 188 return None; 189 const Expr *This = ME->getBase()->IgnoreParenImpCasts(); 190 if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) { 191 const ValueDecl *VD = DRE->getDecl(); 192 if (VD->getNameAsString() != "metaClass") 193 return None; 194 195 if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext())) 196 return RD->getNameAsString(); 197 198 } 199 } 200 return None; 201 } 202 203 std::string findAllocatedObjectName(const Stmt *S, 204 QualType QT) { 205 if (const auto *CE = dyn_cast<CallExpr>(S)) 206 if (auto Out = findMetaClassAlloc(CE->getCallee())) 207 return *Out; 208 return getPrettyTypeName(QT); 209 } 210 211 static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, 212 const LocationContext *LCtx, 213 const RefVal &CurrV, SymbolRef &Sym, 214 const Stmt *S, 215 llvm::raw_string_ostream &os) { 216 CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager(); 217 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { 218 // Get the name of the callee (if it is available) 219 // from the tracked SVal. 220 SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); 221 const FunctionDecl *FD = X.getAsFunctionDecl(); 222 223 // If failed, try to get it from AST. 224 if (!FD) 225 FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl()); 226 227 if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) { 228 os << "Call to method '" << MD->getQualifiedNameAsString() << '\''; 229 } else if (FD) { 230 os << "Call to function '" << FD->getQualifiedNameAsString() << '\''; 231 } else { 232 os << "function call"; 233 } 234 } else if (isa<CXXNewExpr>(S)) { 235 os << "Operator 'new'"; 236 } else { 237 assert(isa<ObjCMessageExpr>(S)); 238 CallEventRef<ObjCMethodCall> Call = 239 Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx); 240 241 switch (Call->getMessageKind()) { 242 case OCM_Message: 243 os << "Method"; 244 break; 245 case OCM_PropertyAccess: 246 os << "Property"; 247 break; 248 case OCM_Subscript: 249 os << "Subscript"; 250 break; 251 } 252 } 253 254 Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx); 255 auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE); 256 257 // If index is not found, we assume that the symbol was returned. 258 if (!Idx) { 259 os << " returns "; 260 } else { 261 os << " writes "; 262 } 263 264 if (CurrV.getObjKind() == ObjKind::CF) { 265 os << "a Core Foundation object of type '" 266 << Sym->getType().getAsString() << "' with a "; 267 } else if (CurrV.getObjKind() == ObjKind::OS) { 268 os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType()) 269 << "' with a "; 270 } else if (CurrV.getObjKind() == ObjKind::Generalized) { 271 os << "an object of type '" << Sym->getType().getAsString() 272 << "' with a "; 273 } else { 274 assert(CurrV.getObjKind() == ObjKind::ObjC); 275 QualType T = Sym->getType(); 276 if (!isa<ObjCObjectPointerType>(T)) { 277 os << "an Objective-C object with a "; 278 } else { 279 const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T); 280 os << "an instance of " << PT->getPointeeType().getAsString() 281 << " with a "; 282 } 283 } 284 285 if (CurrV.isOwned()) { 286 os << "+1 retain count"; 287 } else { 288 assert(CurrV.isNotOwned()); 289 os << "+0 retain count"; 290 } 291 292 if (Idx) { 293 os << " into an out parameter '"; 294 const ParmVarDecl *PVD = (*CE)->parameters()[*Idx]; 295 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(), 296 /*Qualified=*/false); 297 os << "'"; 298 299 QualType RT = (*CE)->getResultType(); 300 if (!RT.isNull() && !RT->isVoidType()) { 301 SVal RV = (*CE)->getReturnValue(); 302 if (CurrSt->isNull(RV).isConstrainedTrue()) { 303 os << " (assuming the call returns zero)"; 304 } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) { 305 os << " (assuming the call returns non-zero)"; 306 } 307 308 } 309 } 310 } 311 312 namespace clang { 313 namespace ento { 314 namespace retaincountchecker { 315 316 class RefCountReportVisitor : public BugReporterVisitor { 317 protected: 318 SymbolRef Sym; 319 320 public: 321 RefCountReportVisitor(SymbolRef sym) : Sym(sym) {} 322 323 void Profile(llvm::FoldingSetNodeID &ID) const override { 324 static int x = 0; 325 ID.AddPointer(&x); 326 ID.AddPointer(Sym); 327 } 328 329 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, 330 BugReporterContext &BRC, 331 BugReport &BR) override; 332 333 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, 334 const ExplodedNode *N, 335 BugReport &BR) override; 336 }; 337 338 class RefLeakReportVisitor : public RefCountReportVisitor { 339 public: 340 RefLeakReportVisitor(SymbolRef sym) : RefCountReportVisitor(sym) {} 341 342 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, 343 const ExplodedNode *N, 344 BugReport &BR) override; 345 }; 346 347 } // end namespace retaincountchecker 348 } // end namespace ento 349 } // end namespace clang 350 351 352 /// Find the first node with the parent stack frame. 353 static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) { 354 const StackFrameContext *SC = Pred->getStackFrame(); 355 if (SC->inTopFrame()) 356 return nullptr; 357 const StackFrameContext *PC = SC->getParent()->getStackFrame(); 358 if (!PC) 359 return nullptr; 360 361 const ExplodedNode *N = Pred; 362 while (N && N->getStackFrame() != PC) { 363 N = N->getFirstPred(); 364 } 365 return N; 366 } 367 368 369 /// Insert a diagnostic piece at function exit 370 /// if a function parameter is annotated as "os_consumed", 371 /// but it does not actually consume the reference. 372 static std::shared_ptr<PathDiagnosticEventPiece> 373 annotateConsumedSummaryMismatch(const ExplodedNode *N, 374 CallExitBegin &CallExitLoc, 375 const SourceManager &SM, 376 CallEventManager &CEMgr) { 377 378 const ExplodedNode *CN = getCalleeNode(N); 379 if (!CN) 380 return nullptr; 381 382 CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState()); 383 384 std::string sbuf; 385 llvm::raw_string_ostream os(sbuf); 386 ArrayRef<const ParmVarDecl *> Parameters = Call->parameters(); 387 for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) { 388 const ParmVarDecl *PVD = Parameters[I]; 389 390 if (!PVD->hasAttr<OSConsumedAttr>()) 391 continue; 392 393 if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) { 394 const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR); 395 const RefVal *CountAtExit = getRefBinding(N->getState(), SR); 396 397 if (!CountBeforeCall || !CountAtExit) 398 continue; 399 400 unsigned CountBefore = CountBeforeCall->getCount(); 401 unsigned CountAfter = CountAtExit->getCount(); 402 403 bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1; 404 if (!AsExpected) { 405 os << "Parameter '"; 406 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(), 407 /*Qualified=*/false); 408 os << "' is marked as consuming, but the function did not consume " 409 << "the reference\n"; 410 } 411 } 412 } 413 414 if (os.str().empty()) 415 return nullptr; 416 417 PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM); 418 return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); 419 } 420 421 /// Annotate the parameter at the analysis entry point. 422 static std::shared_ptr<PathDiagnosticEventPiece> 423 annotateStartParameter(const ExplodedNode *N, SymbolRef Sym, 424 const SourceManager &SM) { 425 auto PP = N->getLocationAs<BlockEdge>(); 426 if (!PP) 427 return nullptr; 428 429 const CFGBlock *Src = PP->getSrc(); 430 const RefVal *CurrT = getRefBinding(N->getState(), Sym); 431 432 if (&Src->getParent()->getEntry() != Src || !CurrT || 433 getRefBinding(N->getFirstPred()->getState(), Sym)) 434 return nullptr; 435 436 const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion()); 437 const auto *PVD = cast<ParmVarDecl>(VR->getDecl()); 438 PathDiagnosticLocation L = PathDiagnosticLocation(PVD, SM); 439 440 std::string s; 441 llvm::raw_string_ostream os(s); 442 os << "Parameter '" << PVD->getNameAsString() << "' starts at +"; 443 if (CurrT->getCount() == 1) { 444 os << "1, as it is marked as consuming"; 445 } else { 446 assert(CurrT->getCount() == 0); 447 os << "0"; 448 } 449 return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); 450 } 451 452 std::shared_ptr<PathDiagnosticPiece> 453 RefCountReportVisitor::VisitNode(const ExplodedNode *N, 454 BugReporterContext &BRC, BugReport &BR) { 455 456 const auto &BT = static_cast<const RefCountBug&>(BR.getBugType()); 457 const auto *Checker = 458 static_cast<const RetainCountChecker *>(BT.getChecker()); 459 460 bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned || 461 BT.getBugType() == RefCountBug::DeallocNotOwned; 462 463 const SourceManager &SM = BRC.getSourceManager(); 464 CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager(); 465 if (auto CE = N->getLocationAs<CallExitBegin>()) 466 if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr)) 467 return PD; 468 469 if (auto PD = annotateStartParameter(N, Sym, SM)) 470 return PD; 471 472 // FIXME: We will eventually need to handle non-statement-based events 473 // (__attribute__((cleanup))). 474 if (!N->getLocation().getAs<StmtPoint>()) 475 return nullptr; 476 477 // Check if the type state has changed. 478 const ExplodedNode *PrevNode = N->getFirstPred(); 479 ProgramStateRef PrevSt = PrevNode->getState(); 480 ProgramStateRef CurrSt = N->getState(); 481 const LocationContext *LCtx = N->getLocationContext(); 482 483 const RefVal* CurrT = getRefBinding(CurrSt, Sym); 484 if (!CurrT) 485 return nullptr; 486 487 const RefVal &CurrV = *CurrT; 488 const RefVal *PrevT = getRefBinding(PrevSt, Sym); 489 490 // Create a string buffer to constain all the useful things we want 491 // to tell the user. 492 std::string sbuf; 493 llvm::raw_string_ostream os(sbuf); 494 495 if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) { 496 os << "Object is now not exclusively owned"; 497 auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM); 498 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); 499 } 500 501 // This is the allocation site since the previous node had no bindings 502 // for this symbol. 503 if (!PrevT) { 504 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); 505 506 if (isa<ObjCIvarRefExpr>(S) && 507 isSynthesizedAccessor(LCtx->getStackFrame())) { 508 S = LCtx->getStackFrame()->getCallSite(); 509 } 510 511 if (isa<ObjCArrayLiteral>(S)) { 512 os << "NSArray literal is an object with a +0 retain count"; 513 } else if (isa<ObjCDictionaryLiteral>(S)) { 514 os << "NSDictionary literal is an object with a +0 retain count"; 515 } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) { 516 if (isNumericLiteralExpression(BL->getSubExpr())) 517 os << "NSNumber literal is an object with a +0 retain count"; 518 else { 519 const ObjCInterfaceDecl *BoxClass = nullptr; 520 if (const ObjCMethodDecl *Method = BL->getBoxingMethod()) 521 BoxClass = Method->getClassInterface(); 522 523 // We should always be able to find the boxing class interface, 524 // but consider this future-proofing. 525 if (BoxClass) { 526 os << *BoxClass << " b"; 527 } else { 528 os << "B"; 529 } 530 531 os << "oxed expression produces an object with a +0 retain count"; 532 } 533 } else if (isa<ObjCIvarRefExpr>(S)) { 534 os << "Object loaded from instance variable"; 535 } else { 536 generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os); 537 } 538 539 PathDiagnosticLocation Pos(S, SM, N->getLocationContext()); 540 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); 541 } 542 543 // Gather up the effects that were performed on the object at this 544 // program point 545 bool DeallocSent = false; 546 547 const ProgramPointTag *Tag = N->getLocation().getTag(); 548 549 if (Tag == &Checker->getCastFailTag()) { 550 os << "Assuming dynamic cast returns null due to type mismatch"; 551 } 552 553 if (Tag == &Checker->getDeallocSentTag()) { 554 // We only have summaries attached to nodes after evaluating CallExpr and 555 // ObjCMessageExprs. 556 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); 557 558 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { 559 // Iterate through the parameter expressions and see if the symbol 560 // was ever passed as an argument. 561 unsigned i = 0; 562 563 for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) { 564 565 // Retrieve the value of the argument. Is it the symbol 566 // we are interested in? 567 if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym) 568 continue; 569 570 // We have an argument. Get the effect! 571 DeallocSent = true; 572 } 573 } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { 574 if (const Expr *receiver = ME->getInstanceReceiver()) { 575 if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx) 576 .getAsLocSymbol() == Sym) { 577 // The symbol we are tracking is the receiver. 578 DeallocSent = true; 579 } 580 } 581 } 582 } 583 584 if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent)) 585 return nullptr; 586 587 if (os.str().empty()) 588 return nullptr; // We have nothing to say! 589 590 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); 591 PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 592 N->getLocationContext()); 593 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); 594 595 // Add the range by scanning the children of the statement for any bindings 596 // to Sym. 597 for (const Stmt *Child : S->children()) 598 if (const Expr *Exp = dyn_cast_or_null<Expr>(Child)) 599 if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) { 600 P->addRange(Exp->getSourceRange()); 601 break; 602 } 603 604 return std::move(P); 605 } 606 607 static Optional<std::string> describeRegion(const MemRegion *MR) { 608 if (const auto *VR = dyn_cast_or_null<VarRegion>(MR)) 609 return std::string(VR->getDecl()->getName()); 610 // Once we support more storage locations for bindings, 611 // this would need to be improved. 612 return None; 613 } 614 615 namespace { 616 // Find the first node in the current function context that referred to the 617 // tracked symbol and the memory location that value was stored to. Note, the 618 // value is only reported if the allocation occurred in the same function as 619 // the leak. The function can also return a location context, which should be 620 // treated as interesting. 621 struct AllocationInfo { 622 const ExplodedNode* N; 623 const MemRegion *R; 624 const LocationContext *InterestingMethodContext; 625 AllocationInfo(const ExplodedNode *InN, 626 const MemRegion *InR, 627 const LocationContext *InInterestingMethodContext) : 628 N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {} 629 }; 630 } // end anonymous namespace 631 632 static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, 633 const ExplodedNode *N, SymbolRef Sym) { 634 const ExplodedNode *AllocationNode = N; 635 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N; 636 const MemRegion *FirstBinding = nullptr; 637 const LocationContext *LeakContext = N->getLocationContext(); 638 639 // The location context of the init method called on the leaked object, if 640 // available. 641 const LocationContext *InitMethodContext = nullptr; 642 643 while (N) { 644 ProgramStateRef St = N->getState(); 645 const LocationContext *NContext = N->getLocationContext(); 646 647 if (!getRefBinding(St, Sym)) 648 break; 649 650 StoreManager::FindUniqueBinding FB(Sym); 651 StateMgr.iterBindings(St, FB); 652 653 if (FB) { 654 const MemRegion *R = FB.getRegion(); 655 // Do not show local variables belonging to a function other than 656 // where the error is reported. 657 if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace())) 658 if (MR->getStackFrame() == LeakContext->getStackFrame()) 659 FirstBinding = R; 660 } 661 662 // AllocationNode is the last node in which the symbol was tracked. 663 AllocationNode = N; 664 665 // AllocationNodeInCurrentContext, is the last node in the current or 666 // parent context in which the symbol was tracked. 667 // 668 // Note that the allocation site might be in the parent context. For example, 669 // the case where an allocation happens in a block that captures a reference 670 // to it and that reference is overwritten/dropped by another call to 671 // the block. 672 if (NContext == LeakContext || NContext->isParentOf(LeakContext)) 673 AllocationNodeInCurrentOrParentContext = N; 674 675 // Find the last init that was called on the given symbol and store the 676 // init method's location context. 677 if (!InitMethodContext) 678 if (auto CEP = N->getLocation().getAs<CallEnter>()) { 679 const Stmt *CE = CEP->getCallExpr(); 680 if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) { 681 const Stmt *RecExpr = ME->getInstanceReceiver(); 682 if (RecExpr) { 683 SVal RecV = St->getSVal(RecExpr, NContext); 684 if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym) 685 InitMethodContext = CEP->getCalleeContext(); 686 } 687 } 688 } 689 690 N = N->getFirstPred(); 691 } 692 693 // If we are reporting a leak of the object that was allocated with alloc, 694 // mark its init method as interesting. 695 const LocationContext *InterestingMethodContext = nullptr; 696 if (InitMethodContext) { 697 const ProgramPoint AllocPP = AllocationNode->getLocation(); 698 if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) 699 if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>()) 700 if (ME->getMethodFamily() == OMF_alloc) 701 InterestingMethodContext = InitMethodContext; 702 } 703 704 // If allocation happened in a function different from the leak node context, 705 // do not report the binding. 706 assert(N && "Could not find allocation node"); 707 708 if (AllocationNodeInCurrentOrParentContext && 709 AllocationNodeInCurrentOrParentContext->getLocationContext() != 710 LeakContext) 711 FirstBinding = nullptr; 712 713 return AllocationInfo(AllocationNodeInCurrentOrParentContext, 714 FirstBinding, 715 InterestingMethodContext); 716 } 717 718 std::shared_ptr<PathDiagnosticPiece> 719 RefCountReportVisitor::getEndPath(BugReporterContext &BRC, 720 const ExplodedNode *EndN, BugReport &BR) { 721 BR.markInteresting(Sym); 722 return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR); 723 } 724 725 std::shared_ptr<PathDiagnosticPiece> 726 RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, 727 const ExplodedNode *EndN, BugReport &BR) { 728 729 // Tell the BugReporterContext to report cases when the tracked symbol is 730 // assigned to different variables, etc. 731 BR.markInteresting(Sym); 732 733 // We are reporting a leak. Walk up the graph to get to the first node where 734 // the symbol appeared, and also get the first VarDecl that tracked object 735 // is stored to. 736 AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym); 737 738 const MemRegion* FirstBinding = AllocI.R; 739 BR.markInteresting(AllocI.InterestingMethodContext); 740 741 SourceManager& SM = BRC.getSourceManager(); 742 743 // Compute an actual location for the leak. Sometimes a leak doesn't 744 // occur at an actual statement (e.g., transition between blocks; end 745 // of function) so we need to walk the graph and compute a real location. 746 const ExplodedNode *LeakN = EndN; 747 PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM); 748 749 std::string sbuf; 750 llvm::raw_string_ostream os(sbuf); 751 752 os << "Object leaked: "; 753 754 Optional<std::string> RegionDescription = describeRegion(FirstBinding); 755 if (RegionDescription) { 756 os << "object allocated and stored into '" << *RegionDescription << '\''; 757 } else { 758 os << "allocated object of type '" << getPrettyTypeName(Sym->getType()) 759 << "'"; 760 } 761 762 // Get the retain count. 763 const RefVal* RV = getRefBinding(EndN->getState(), Sym); 764 assert(RV); 765 766 if (RV->getKind() == RefVal::ErrorLeakReturned) { 767 // FIXME: Per comments in rdar://6320065, "create" only applies to CF 768 // objects. Only "copy", "alloc", "retain" and "new" transfer ownership 769 // to the caller for NS objects. 770 const Decl *D = &EndN->getCodeDecl(); 771 772 os << (isa<ObjCMethodDecl>(D) ? " is returned from a method " 773 : " is returned from a function "); 774 775 if (D->hasAttr<CFReturnsNotRetainedAttr>()) { 776 os << "that is annotated as CF_RETURNS_NOT_RETAINED"; 777 } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) { 778 os << "that is annotated as NS_RETURNS_NOT_RETAINED"; 779 } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) { 780 os << "that is annotated as OS_RETURNS_NOT_RETAINED"; 781 } else { 782 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { 783 if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) { 784 os << "managed by Automatic Reference Counting"; 785 } else { 786 os << "whose name ('" << MD->getSelector().getAsString() 787 << "') does not start with " 788 "'copy', 'mutableCopy', 'alloc' or 'new'." 789 " This violates the naming convention rules" 790 " given in the Memory Management Guide for Cocoa"; 791 } 792 } else { 793 const FunctionDecl *FD = cast<FunctionDecl>(D); 794 ObjKind K = RV->getObjKind(); 795 if (K == ObjKind::ObjC || K == ObjKind::CF) { 796 os << "whose name ('" << *FD 797 << "') does not contain 'Copy' or 'Create'. This violates the " 798 "naming" 799 " convention rules given in the Memory Management Guide for " 800 "Core" 801 " Foundation"; 802 } else if (RV->getObjKind() == ObjKind::OS) { 803 std::string FuncName = FD->getNameAsString(); 804 os << "whose name ('" << FuncName 805 << "') starts with '" << StringRef(FuncName).substr(0, 3) << "'"; 806 } 807 } 808 } 809 } else { 810 os << " is not referenced later in this execution path and has a retain " 811 "count of +" << RV->getCount(); 812 } 813 814 return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); 815 } 816 817 RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, 818 ExplodedNode *n, SymbolRef sym, 819 bool isLeak) 820 : BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) { 821 if (!isLeak) 822 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym)); 823 } 824 825 RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, 826 ExplodedNode *n, SymbolRef sym, 827 StringRef endText) 828 : BugReport(D, D.getDescription(), endText, n) { 829 830 addVisitor(llvm::make_unique<RefCountReportVisitor>(sym)); 831 } 832 833 void RefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) { 834 const SourceManager& SMgr = Ctx.getSourceManager(); 835 836 if (!sym->getOriginRegion()) 837 return; 838 839 auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion()); 840 if (Region) { 841 const Decl *PDecl = Region->getDecl(); 842 if (PDecl && isa<ParmVarDecl>(PDecl)) { 843 PathDiagnosticLocation ParamLocation = 844 PathDiagnosticLocation::create(PDecl, SMgr); 845 Location = ParamLocation; 846 UniqueingLocation = ParamLocation; 847 UniqueingDecl = Ctx.getLocationContext()->getDecl(); 848 } 849 } 850 } 851 852 void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx, 853 SymbolRef sym) { 854 // Most bug reports are cached at the location where they occurred. 855 // With leaks, we want to unique them by the location where they were 856 // allocated, and only report a single path. To do this, we need to find 857 // the allocation site of a piece of tracked memory, which we do via a 858 // call to GetAllocationSite. This will walk the ExplodedGraph backwards. 859 // Note that this is *not* the trimmed graph; we are guaranteed, however, 860 // that all ancestor nodes that represent the allocation site have the 861 // same SourceLocation. 862 const ExplodedNode *AllocNode = nullptr; 863 864 const SourceManager& SMgr = Ctx.getSourceManager(); 865 866 AllocationInfo AllocI = 867 GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); 868 869 AllocNode = AllocI.N; 870 AllocBinding = AllocI.R; 871 markInteresting(AllocI.InterestingMethodContext); 872 873 // Get the SourceLocation for the allocation site. 874 // FIXME: This will crash the analyzer if an allocation comes from an 875 // implicit call (ex: a destructor call). 876 // (Currently there are no such allocations in Cocoa, though.) 877 AllocStmt = PathDiagnosticLocation::getStmt(AllocNode); 878 879 if (!AllocStmt) { 880 AllocBinding = nullptr; 881 return; 882 } 883 884 PathDiagnosticLocation AllocLocation = 885 PathDiagnosticLocation::createBegin(AllocStmt, SMgr, 886 AllocNode->getLocationContext()); 887 Location = AllocLocation; 888 889 // Set uniqieing info, which will be used for unique the bug reports. The 890 // leaks should be uniqued on the allocation site. 891 UniqueingLocation = AllocLocation; 892 UniqueingDecl = AllocNode->getLocationContext()->getDecl(); 893 } 894 895 void RefLeakReport::createDescription(CheckerContext &Ctx) { 896 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid()); 897 Description.clear(); 898 llvm::raw_string_ostream os(Description); 899 os << "Potential leak of an object"; 900 901 Optional<std::string> RegionDescription = describeRegion(AllocBinding); 902 if (RegionDescription) { 903 os << " stored into '" << *RegionDescription << '\''; 904 } else { 905 906 // If we can't figure out the name, just supply the type information. 907 os << " of type '" << getPrettyTypeName(Sym->getType()) << "'"; 908 } 909 } 910 911 RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, 912 ExplodedNode *n, SymbolRef sym, 913 CheckerContext &Ctx) 914 : RefCountReport(D, LOpts, n, sym, /*isLeak=*/true) { 915 916 deriveAllocLocation(Ctx, sym); 917 if (!AllocBinding) 918 deriveParamLocation(Ctx, sym); 919 920 createDescription(Ctx); 921 922 addVisitor(llvm::make_unique<RefLeakReportVisitor>(sym)); 923 } 924