1 // RetainCountDiagnostics.cpp - Checks for leaks and other issues -*- C++ -*--// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file defines diagnostics for RetainCountChecker, which implements 11 // a reference count checker for Core Foundation and Cocoa on (Mac OS X). 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "RetainCountDiagnostics.h" 16 #include "RetainCountChecker.h" 17 18 using namespace clang; 19 using namespace ento; 20 using namespace retaincountchecker; 21 22 static bool isNumericLiteralExpression(const Expr *E) { 23 // FIXME: This set of cases was copied from SemaExprObjC. 24 return isa<IntegerLiteral>(E) || 25 isa<CharacterLiteral>(E) || 26 isa<FloatingLiteral>(E) || 27 isa<ObjCBoolLiteralExpr>(E) || 28 isa<CXXBoolLiteralExpr>(E); 29 } 30 31 /// If type represents a pointer to CXXRecordDecl, 32 /// and is not a typedef, return the decl name. 33 /// Otherwise, return the serialization of type. 34 static std::string getPrettyTypeName(QualType QT) { 35 QualType PT = QT->getPointeeType(); 36 if (!PT.isNull() && !QT->getAs<TypedefType>()) 37 if (const auto *RD = PT->getAsCXXRecordDecl()) 38 return RD->getName(); 39 return QT.getAsString(); 40 } 41 42 /// Write information about the type state change to {@code os}, 43 /// return whether the note should be generated. 44 static bool shouldGenerateNote(llvm::raw_string_ostream &os, 45 const RefVal *PrevT, const RefVal &CurrV, 46 SmallVector<ArgEffect, 2> &AEffects) { 47 // Get the previous type state. 48 RefVal PrevV = *PrevT; 49 50 // Specially handle -dealloc. 51 if (std::find(AEffects.begin(), AEffects.end(), Dealloc) != AEffects.end()) { 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: 67 case RefVal::NotOwned: 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 88 case RefVal::Released: 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 97 case RefVal::ReturnedOwned: 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 106 case RefVal::ReturnedNotOwned: 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 static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, 117 const LocationContext *LCtx, 118 const RefVal &CurrV, SymbolRef &Sym, 119 const Stmt *S, 120 llvm::raw_string_ostream &os) { 121 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { 122 // Get the name of the callee (if it is available) 123 // from the tracked SVal. 124 SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); 125 const FunctionDecl *FD = X.getAsFunctionDecl(); 126 127 // If failed, try to get it from AST. 128 if (!FD) 129 FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl()); 130 131 if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) { 132 os << "Call to method '" << MD->getQualifiedNameAsString() << '\''; 133 } else if (FD) { 134 os << "Call to function '" << FD->getQualifiedNameAsString() << '\''; 135 } else { 136 os << "function call"; 137 } 138 } else if (isa<CXXNewExpr>(S)) { 139 os << "Operator 'new'"; 140 } else { 141 assert(isa<ObjCMessageExpr>(S)); 142 CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager(); 143 CallEventRef<ObjCMethodCall> Call = 144 Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx); 145 146 switch (Call->getMessageKind()) { 147 case OCM_Message: 148 os << "Method"; 149 break; 150 case OCM_PropertyAccess: 151 os << "Property"; 152 break; 153 case OCM_Subscript: 154 os << "Subscript"; 155 break; 156 } 157 } 158 159 if (CurrV.getObjKind() == ObjKind::CF) { 160 os << " returns a Core Foundation object of type " 161 << Sym->getType().getAsString() << " with a "; 162 } else if (CurrV.getObjKind() == ObjKind::OS) { 163 os << " returns an OSObject of type " << getPrettyTypeName(Sym->getType()) 164 << " with a "; 165 } else if (CurrV.getObjKind() == ObjKind::Generalized) { 166 os << " returns an object of type " << Sym->getType().getAsString() 167 << " with a "; 168 } else { 169 assert(CurrV.getObjKind() == ObjKind::ObjC); 170 QualType T = Sym->getType(); 171 if (!isa<ObjCObjectPointerType>(T)) { 172 os << " returns an Objective-C object with a "; 173 } else { 174 const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T); 175 os << " returns an instance of " << PT->getPointeeType().getAsString() 176 << " with a "; 177 } 178 } 179 180 if (CurrV.isOwned()) { 181 os << "+1 retain count"; 182 } else { 183 assert(CurrV.isNotOwned()); 184 os << "+0 retain count"; 185 } 186 } 187 188 namespace clang { 189 namespace ento { 190 namespace retaincountchecker { 191 192 class CFRefReportVisitor : public BugReporterVisitor { 193 protected: 194 SymbolRef Sym; 195 const SummaryLogTy &SummaryLog; 196 197 public: 198 CFRefReportVisitor(SymbolRef sym, const SummaryLogTy &log) 199 : Sym(sym), SummaryLog(log) {} 200 201 void Profile(llvm::FoldingSetNodeID &ID) const override { 202 static int x = 0; 203 ID.AddPointer(&x); 204 ID.AddPointer(Sym); 205 } 206 207 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, 208 BugReporterContext &BRC, 209 BugReport &BR) override; 210 211 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, 212 const ExplodedNode *N, 213 BugReport &BR) override; 214 }; 215 216 class CFRefLeakReportVisitor : public CFRefReportVisitor { 217 public: 218 CFRefLeakReportVisitor(SymbolRef sym, 219 const SummaryLogTy &log) 220 : CFRefReportVisitor(sym, log) {} 221 222 std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, 223 const ExplodedNode *N, 224 BugReport &BR) override; 225 }; 226 227 } // end namespace retaincountchecker 228 } // end namespace ento 229 } // end namespace clang 230 231 232 /// Find the first node with the parent stack frame. 233 static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) { 234 const StackFrameContext *SC = Pred->getStackFrame(); 235 if (SC->inTopFrame()) 236 return nullptr; 237 const StackFrameContext *PC = SC->getParent()->getStackFrame(); 238 if (!PC) 239 return nullptr; 240 241 const ExplodedNode *N = Pred; 242 while (N && N->getStackFrame() != PC) { 243 N = N->getFirstPred(); 244 } 245 return N; 246 } 247 248 249 /// Insert a diagnostic piece at function exit 250 /// if a function parameter is annotated as "os_consumed", 251 /// but it does not actually consume the reference. 252 static std::shared_ptr<PathDiagnosticEventPiece> 253 annotateConsumedSummaryMismatch(const ExplodedNode *N, 254 CallExitBegin &CallExitLoc, 255 const SourceManager &SM, 256 CallEventManager &CEMgr) { 257 258 const ExplodedNode *CN = getCalleeNode(N); 259 if (!CN) 260 return nullptr; 261 262 CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState()); 263 264 std::string sbuf; 265 llvm::raw_string_ostream os(sbuf); 266 ArrayRef<const ParmVarDecl *> Parameters = Call->parameters(); 267 for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) { 268 const ParmVarDecl *PVD = Parameters[I]; 269 270 if (!PVD->hasAttr<OSConsumedAttr>()) 271 continue; 272 273 if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) { 274 const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR); 275 const RefVal *CountAtExit = getRefBinding(N->getState(), SR); 276 277 if (!CountBeforeCall || !CountAtExit) 278 continue; 279 280 unsigned CountBefore = CountBeforeCall->getCount(); 281 unsigned CountAfter = CountAtExit->getCount(); 282 283 bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1; 284 if (!AsExpected) { 285 os << "Parameter '"; 286 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(), 287 /*Qualified=*/false); 288 os << "' is marked as consuming, but the function did not consume " 289 << "the reference\n"; 290 } 291 } 292 } 293 294 if (os.str().empty()) 295 return nullptr; 296 297 // FIXME: remove the code duplication with NoStoreFuncVisitor. 298 PathDiagnosticLocation L; 299 if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) { 300 L = PathDiagnosticLocation::createBegin(RS, SM, N->getLocationContext()); 301 } else { 302 L = PathDiagnosticLocation( 303 Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM); 304 } 305 306 return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); 307 } 308 309 std::shared_ptr<PathDiagnosticPiece> 310 CFRefReportVisitor::VisitNode(const ExplodedNode *N, 311 BugReporterContext &BRC, BugReport &BR) { 312 const SourceManager &SM = BRC.getSourceManager(); 313 CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager(); 314 if (auto CE = N->getLocationAs<CallExitBegin>()) 315 if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr)) 316 return PD; 317 318 // FIXME: We will eventually need to handle non-statement-based events 319 // (__attribute__((cleanup))). 320 if (!N->getLocation().getAs<StmtPoint>()) 321 return nullptr; 322 323 // Check if the type state has changed. 324 const ExplodedNode *PrevNode = N->getFirstPred(); 325 ProgramStateRef PrevSt = PrevNode->getState(); 326 ProgramStateRef CurrSt = N->getState(); 327 const LocationContext *LCtx = N->getLocationContext(); 328 329 const RefVal* CurrT = getRefBinding(CurrSt, Sym); 330 if (!CurrT) return nullptr; 331 332 const RefVal &CurrV = *CurrT; 333 const RefVal *PrevT = getRefBinding(PrevSt, Sym); 334 335 // Create a string buffer to constain all the useful things we want 336 // to tell the user. 337 std::string sbuf; 338 llvm::raw_string_ostream os(sbuf); 339 340 // This is the allocation site since the previous node had no bindings 341 // for this symbol. 342 if (!PrevT) { 343 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); 344 345 if (isa<ObjCIvarRefExpr>(S) && 346 isSynthesizedAccessor(LCtx->getStackFrame())) { 347 S = LCtx->getStackFrame()->getCallSite(); 348 } 349 350 if (isa<ObjCArrayLiteral>(S)) { 351 os << "NSArray literal is an object with a +0 retain count"; 352 } else if (isa<ObjCDictionaryLiteral>(S)) { 353 os << "NSDictionary literal is an object with a +0 retain count"; 354 } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) { 355 if (isNumericLiteralExpression(BL->getSubExpr())) 356 os << "NSNumber literal is an object with a +0 retain count"; 357 else { 358 const ObjCInterfaceDecl *BoxClass = nullptr; 359 if (const ObjCMethodDecl *Method = BL->getBoxingMethod()) 360 BoxClass = Method->getClassInterface(); 361 362 // We should always be able to find the boxing class interface, 363 // but consider this future-proofing. 364 if (BoxClass) { 365 os << *BoxClass << " b"; 366 } else { 367 os << "B"; 368 } 369 370 os << "oxed expression produces an object with a +0 retain count"; 371 } 372 } else if (isa<ObjCIvarRefExpr>(S)) { 373 os << "Object loaded from instance variable"; 374 } else { 375 generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os); 376 } 377 378 PathDiagnosticLocation Pos(S, SM, N->getLocationContext()); 379 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); 380 } 381 382 // Gather up the effects that were performed on the object at this 383 // program point 384 SmallVector<ArgEffect, 2> AEffects; 385 const ExplodedNode *OrigNode = BRC.getNodeResolver().getOriginalNode(N); 386 if (const RetainSummary *Summ = SummaryLog.lookup(OrigNode)) { 387 // We only have summaries attached to nodes after evaluating CallExpr and 388 // ObjCMessageExprs. 389 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); 390 391 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { 392 // Iterate through the parameter expressions and see if the symbol 393 // was ever passed as an argument. 394 unsigned i = 0; 395 396 for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) { 397 398 // Retrieve the value of the argument. Is it the symbol 399 // we are interested in? 400 if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym) 401 continue; 402 403 // We have an argument. Get the effect! 404 AEffects.push_back(Summ->getArg(i)); 405 } 406 } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { 407 if (const Expr *receiver = ME->getInstanceReceiver()) { 408 if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx) 409 .getAsLocSymbol() == Sym) { 410 // The symbol we are tracking is the receiver. 411 AEffects.push_back(Summ->getReceiverEffect()); 412 } 413 } 414 } 415 } 416 417 if (!shouldGenerateNote(os, PrevT, CurrV, AEffects)) 418 return nullptr; 419 420 if (os.str().empty()) 421 return nullptr; // We have nothing to say! 422 423 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); 424 PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 425 N->getLocationContext()); 426 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); 427 428 // Add the range by scanning the children of the statement for any bindings 429 // to Sym. 430 for (const Stmt *Child : S->children()) 431 if (const Expr *Exp = dyn_cast_or_null<Expr>(Child)) 432 if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) { 433 P->addRange(Exp->getSourceRange()); 434 break; 435 } 436 437 return std::move(P); 438 } 439 440 static Optional<std::string> describeRegion(const MemRegion *MR) { 441 if (const auto *VR = dyn_cast_or_null<VarRegion>(MR)) 442 return std::string(VR->getDecl()->getName()); 443 // Once we support more storage locations for bindings, 444 // this would need to be improved. 445 return None; 446 } 447 448 namespace { 449 // Find the first node in the current function context that referred to the 450 // tracked symbol and the memory location that value was stored to. Note, the 451 // value is only reported if the allocation occurred in the same function as 452 // the leak. The function can also return a location context, which should be 453 // treated as interesting. 454 struct AllocationInfo { 455 const ExplodedNode* N; 456 const MemRegion *R; 457 const LocationContext *InterestingMethodContext; 458 AllocationInfo(const ExplodedNode *InN, 459 const MemRegion *InR, 460 const LocationContext *InInterestingMethodContext) : 461 N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {} 462 }; 463 } // end anonymous namespace 464 465 static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, 466 const ExplodedNode *N, SymbolRef Sym) { 467 const ExplodedNode *AllocationNode = N; 468 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N; 469 const MemRegion *FirstBinding = nullptr; 470 const LocationContext *LeakContext = N->getLocationContext(); 471 472 // The location context of the init method called on the leaked object, if 473 // available. 474 const LocationContext *InitMethodContext = nullptr; 475 476 while (N) { 477 ProgramStateRef St = N->getState(); 478 const LocationContext *NContext = N->getLocationContext(); 479 480 if (!getRefBinding(St, Sym)) 481 break; 482 483 StoreManager::FindUniqueBinding FB(Sym); 484 StateMgr.iterBindings(St, FB); 485 486 if (FB) { 487 const MemRegion *R = FB.getRegion(); 488 // Do not show local variables belonging to a function other than 489 // where the error is reported. 490 if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace())) 491 if (MR->getStackFrame() == LeakContext->getStackFrame()) 492 FirstBinding = R; 493 } 494 495 // AllocationNode is the last node in which the symbol was tracked. 496 AllocationNode = N; 497 498 // AllocationNodeInCurrentContext, is the last node in the current or 499 // parent context in which the symbol was tracked. 500 // 501 // Note that the allocation site might be in the parent context. For example, 502 // the case where an allocation happens in a block that captures a reference 503 // to it and that reference is overwritten/dropped by another call to 504 // the block. 505 if (NContext == LeakContext || NContext->isParentOf(LeakContext)) 506 AllocationNodeInCurrentOrParentContext = N; 507 508 // Find the last init that was called on the given symbol and store the 509 // init method's location context. 510 if (!InitMethodContext) 511 if (auto CEP = N->getLocation().getAs<CallEnter>()) { 512 const Stmt *CE = CEP->getCallExpr(); 513 if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) { 514 const Stmt *RecExpr = ME->getInstanceReceiver(); 515 if (RecExpr) { 516 SVal RecV = St->getSVal(RecExpr, NContext); 517 if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym) 518 InitMethodContext = CEP->getCalleeContext(); 519 } 520 } 521 } 522 523 N = N->getFirstPred(); 524 } 525 526 // If we are reporting a leak of the object that was allocated with alloc, 527 // mark its init method as interesting. 528 const LocationContext *InterestingMethodContext = nullptr; 529 if (InitMethodContext) { 530 const ProgramPoint AllocPP = AllocationNode->getLocation(); 531 if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) 532 if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>()) 533 if (ME->getMethodFamily() == OMF_alloc) 534 InterestingMethodContext = InitMethodContext; 535 } 536 537 // If allocation happened in a function different from the leak node context, 538 // do not report the binding. 539 assert(N && "Could not find allocation node"); 540 541 if (AllocationNodeInCurrentOrParentContext && 542 AllocationNodeInCurrentOrParentContext->getLocationContext() != 543 LeakContext) 544 FirstBinding = nullptr; 545 546 return AllocationInfo(AllocationNodeInCurrentOrParentContext, 547 FirstBinding, 548 InterestingMethodContext); 549 } 550 551 std::shared_ptr<PathDiagnosticPiece> 552 CFRefReportVisitor::getEndPath(BugReporterContext &BRC, 553 const ExplodedNode *EndN, BugReport &BR) { 554 BR.markInteresting(Sym); 555 return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR); 556 } 557 558 std::shared_ptr<PathDiagnosticPiece> 559 CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, 560 const ExplodedNode *EndN, BugReport &BR) { 561 562 // Tell the BugReporterContext to report cases when the tracked symbol is 563 // assigned to different variables, etc. 564 BR.markInteresting(Sym); 565 566 // We are reporting a leak. Walk up the graph to get to the first node where 567 // the symbol appeared, and also get the first VarDecl that tracked object 568 // is stored to. 569 AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym); 570 571 const MemRegion* FirstBinding = AllocI.R; 572 BR.markInteresting(AllocI.InterestingMethodContext); 573 574 SourceManager& SM = BRC.getSourceManager(); 575 576 // Compute an actual location for the leak. Sometimes a leak doesn't 577 // occur at an actual statement (e.g., transition between blocks; end 578 // of function) so we need to walk the graph and compute a real location. 579 const ExplodedNode *LeakN = EndN; 580 PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM); 581 582 std::string sbuf; 583 llvm::raw_string_ostream os(sbuf); 584 585 os << "Object leaked: "; 586 587 Optional<std::string> RegionDescription = describeRegion(FirstBinding); 588 if (RegionDescription) { 589 os << "object allocated and stored into '" << *RegionDescription << '\''; 590 } else { 591 os << "allocated object of type " << getPrettyTypeName(Sym->getType()); 592 } 593 594 // Get the retain count. 595 const RefVal* RV = getRefBinding(EndN->getState(), Sym); 596 assert(RV); 597 598 if (RV->getKind() == RefVal::ErrorLeakReturned) { 599 // FIXME: Per comments in rdar://6320065, "create" only applies to CF 600 // objects. Only "copy", "alloc", "retain" and "new" transfer ownership 601 // to the caller for NS objects. 602 const Decl *D = &EndN->getCodeDecl(); 603 604 os << (isa<ObjCMethodDecl>(D) ? " is returned from a method " 605 : " is returned from a function "); 606 607 if (D->hasAttr<CFReturnsNotRetainedAttr>()) { 608 os << "that is annotated as CF_RETURNS_NOT_RETAINED"; 609 } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) { 610 os << "that is annotated as NS_RETURNS_NOT_RETAINED"; 611 } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) { 612 os << "that is annotated as OS_RETURNS_NOT_RETAINED"; 613 } else { 614 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { 615 if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) { 616 os << "managed by Automatic Reference Counting"; 617 } else { 618 os << "whose name ('" << MD->getSelector().getAsString() 619 << "') does not start with " 620 "'copy', 'mutableCopy', 'alloc' or 'new'." 621 " This violates the naming convention rules" 622 " given in the Memory Management Guide for Cocoa"; 623 } 624 } else { 625 const FunctionDecl *FD = cast<FunctionDecl>(D); 626 os << "whose name ('" << *FD 627 << "') does not contain 'Copy' or 'Create'. This violates the naming" 628 " convention rules given in the Memory Management Guide for Core" 629 " Foundation"; 630 } 631 } 632 } else { 633 os << " is not referenced later in this execution path and has a retain " 634 "count of +" << RV->getCount(); 635 } 636 637 return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); 638 } 639 640 CFRefReport::CFRefReport(CFRefBug &D, const LangOptions &LOpts, 641 const SummaryLogTy &Log, ExplodedNode *n, 642 SymbolRef sym, bool registerVisitor) 643 : BugReport(D, D.getDescription(), n), Sym(sym) { 644 if (registerVisitor) 645 addVisitor(llvm::make_unique<CFRefReportVisitor>(sym, Log)); 646 } 647 648 CFRefReport::CFRefReport(CFRefBug &D, const LangOptions &LOpts, 649 const SummaryLogTy &Log, ExplodedNode *n, 650 SymbolRef sym, StringRef endText) 651 : BugReport(D, D.getDescription(), endText, n) { 652 653 addVisitor(llvm::make_unique<CFRefReportVisitor>(sym, Log)); 654 } 655 656 void CFRefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) { 657 const SourceManager& SMgr = Ctx.getSourceManager(); 658 659 if (!sym->getOriginRegion()) 660 return; 661 662 auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion()); 663 if (Region) { 664 const Decl *PDecl = Region->getDecl(); 665 if (PDecl && isa<ParmVarDecl>(PDecl)) { 666 PathDiagnosticLocation ParamLocation = PathDiagnosticLocation::create(PDecl, SMgr); 667 Location = ParamLocation; 668 UniqueingLocation = ParamLocation; 669 UniqueingDecl = Ctx.getLocationContext()->getDecl(); 670 } 671 } 672 } 673 674 void CFRefLeakReport::deriveAllocLocation(CheckerContext &Ctx, 675 SymbolRef sym) { 676 // Most bug reports are cached at the location where they occurred. 677 // With leaks, we want to unique them by the location where they were 678 // allocated, and only report a single path. To do this, we need to find 679 // the allocation site of a piece of tracked memory, which we do via a 680 // call to GetAllocationSite. This will walk the ExplodedGraph backwards. 681 // Note that this is *not* the trimmed graph; we are guaranteed, however, 682 // that all ancestor nodes that represent the allocation site have the 683 // same SourceLocation. 684 const ExplodedNode *AllocNode = nullptr; 685 686 const SourceManager& SMgr = Ctx.getSourceManager(); 687 688 AllocationInfo AllocI = 689 GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); 690 691 AllocNode = AllocI.N; 692 AllocBinding = AllocI.R; 693 markInteresting(AllocI.InterestingMethodContext); 694 695 // Get the SourceLocation for the allocation site. 696 // FIXME: This will crash the analyzer if an allocation comes from an 697 // implicit call (ex: a destructor call). 698 // (Currently there are no such allocations in Cocoa, though.) 699 AllocStmt = PathDiagnosticLocation::getStmt(AllocNode); 700 701 if (!AllocStmt) { 702 AllocBinding = nullptr; 703 return; 704 } 705 706 PathDiagnosticLocation AllocLocation = 707 PathDiagnosticLocation::createBegin(AllocStmt, SMgr, 708 AllocNode->getLocationContext()); 709 Location = AllocLocation; 710 711 // Set uniqieing info, which will be used for unique the bug reports. The 712 // leaks should be uniqued on the allocation site. 713 UniqueingLocation = AllocLocation; 714 UniqueingDecl = AllocNode->getLocationContext()->getDecl(); 715 } 716 717 void CFRefLeakReport::createDescription(CheckerContext &Ctx) { 718 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid()); 719 Description.clear(); 720 llvm::raw_string_ostream os(Description); 721 os << "Potential leak of an object"; 722 723 Optional<std::string> RegionDescription = describeRegion(AllocBinding); 724 if (RegionDescription) { 725 os << " stored into '" << *RegionDescription << '\''; 726 } else { 727 728 // If we can't figure out the name, just supply the type information. 729 os << " of type " << getPrettyTypeName(Sym->getType()); 730 } 731 } 732 733 CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, 734 const SummaryLogTy &Log, 735 ExplodedNode *n, SymbolRef sym, 736 CheckerContext &Ctx) 737 : CFRefReport(D, LOpts, Log, n, sym, false) { 738 739 deriveAllocLocation(Ctx, sym); 740 if (!AllocBinding) 741 deriveParamLocation(Ctx, sym); 742 743 createDescription(Ctx); 744 745 addVisitor(llvm::make_unique<CFRefLeakReportVisitor>(sym, Log)); 746 } 747