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 StringRef 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 std::shared_ptr<PathDiagnosticPiece> 117 CFRefReportVisitor::VisitNode(const ExplodedNode *N, 118 BugReporterContext &BRC, BugReport &BR) { 119 // FIXME: We will eventually need to handle non-statement-based events 120 // (__attribute__((cleanup))). 121 if (!N->getLocation().getAs<StmtPoint>()) 122 return nullptr; 123 124 // Check if the type state has changed. 125 ProgramStateRef PrevSt = N->getFirstPred()->getState(); 126 ProgramStateRef CurrSt = N->getState(); 127 const LocationContext *LCtx = N->getLocationContext(); 128 129 const RefVal* CurrT = getRefBinding(CurrSt, Sym); 130 if (!CurrT) return nullptr; 131 132 const RefVal &CurrV = *CurrT; 133 const RefVal *PrevT = getRefBinding(PrevSt, Sym); 134 135 // Create a string buffer to constain all the useful things we want 136 // to tell the user. 137 std::string sbuf; 138 llvm::raw_string_ostream os(sbuf); 139 140 // This is the allocation site since the previous node had no bindings 141 // for this symbol. 142 if (!PrevT) { 143 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); 144 145 if (isa<ObjCIvarRefExpr>(S) && 146 isSynthesizedAccessor(LCtx->getStackFrame())) { 147 S = LCtx->getStackFrame()->getCallSite(); 148 } 149 150 if (isa<ObjCArrayLiteral>(S)) { 151 os << "NSArray literal is an object with a +0 retain count"; 152 } else if (isa<ObjCDictionaryLiteral>(S)) { 153 os << "NSDictionary literal is an object with a +0 retain count"; 154 } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) { 155 if (isNumericLiteralExpression(BL->getSubExpr())) 156 os << "NSNumber literal is an object with a +0 retain count"; 157 else { 158 const ObjCInterfaceDecl *BoxClass = nullptr; 159 if (const ObjCMethodDecl *Method = BL->getBoxingMethod()) 160 BoxClass = Method->getClassInterface(); 161 162 // We should always be able to find the boxing class interface, 163 // but consider this future-proofing. 164 if (BoxClass) { 165 os << *BoxClass << " b"; 166 } else { 167 os << "B"; 168 } 169 170 os << "oxed expression produces an object with a +0 retain count"; 171 } 172 } else if (isa<ObjCIvarRefExpr>(S)) { 173 os << "Object loaded from instance variable"; 174 } else { 175 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { 176 // Get the name of the callee (if it is available). 177 SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); 178 if (const FunctionDecl *FD = X.getAsFunctionDecl()) { 179 os << "Call to function '" << *FD << '\''; 180 } else { 181 os << "function call"; 182 } 183 } else { 184 assert(isa<ObjCMessageExpr>(S)); 185 CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager(); 186 CallEventRef<ObjCMethodCall> Call 187 = Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx); 188 189 switch (Call->getMessageKind()) { 190 case OCM_Message: 191 os << "Method"; 192 break; 193 case OCM_PropertyAccess: 194 os << "Property"; 195 break; 196 case OCM_Subscript: 197 os << "Subscript"; 198 break; 199 } 200 } 201 202 if (CurrV.getObjKind() == RetEffect::CF) { 203 os << " returns a Core Foundation object of type " 204 << Sym->getType().getAsString() << " with a "; 205 } else if (CurrV.getObjKind() == RetEffect::OS) { 206 os << " returns an OSObject of type " 207 << getPrettyTypeName(Sym->getType()) << " with a "; 208 } else if (CurrV.getObjKind() == RetEffect::Generalized) { 209 os << " returns an object of type " << Sym->getType().getAsString() 210 << " with a "; 211 } else { 212 assert (CurrV.getObjKind() == RetEffect::ObjC); 213 QualType T = Sym->getType(); 214 if (!isa<ObjCObjectPointerType>(T)) { 215 os << " returns an Objective-C object with a "; 216 } else { 217 const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T); 218 os << " returns an instance of " 219 << PT->getPointeeType().getAsString() << " with a "; 220 } 221 } 222 223 if (CurrV.isOwned()) { 224 os << "+1 retain count"; 225 } else { 226 assert (CurrV.isNotOwned()); 227 os << "+0 retain count"; 228 } 229 } 230 231 PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 232 N->getLocationContext()); 233 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); 234 } 235 236 // Gather up the effects that were performed on the object at this 237 // program point 238 SmallVector<ArgEffect, 2> AEffects; 239 240 const ExplodedNode *OrigNode = BRC.getNodeResolver().getOriginalNode(N); 241 if (const RetainSummary *Summ = SummaryLog.lookup(OrigNode)) { 242 // We only have summaries attached to nodes after evaluating CallExpr and 243 // ObjCMessageExprs. 244 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); 245 246 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { 247 // Iterate through the parameter expressions and see if the symbol 248 // was ever passed as an argument. 249 unsigned i = 0; 250 251 for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) { 252 253 // Retrieve the value of the argument. Is it the symbol 254 // we are interested in? 255 if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym) 256 continue; 257 258 // We have an argument. Get the effect! 259 AEffects.push_back(Summ->getArg(i)); 260 } 261 } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { 262 if (const Expr *receiver = ME->getInstanceReceiver()) { 263 if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx) 264 .getAsLocSymbol() == Sym) { 265 // The symbol we are tracking is the receiver. 266 AEffects.push_back(Summ->getReceiverEffect()); 267 } 268 } 269 } 270 } 271 272 if (!shouldGenerateNote(os, PrevT, CurrV, AEffects)) 273 return nullptr; 274 275 if (os.str().empty()) 276 return nullptr; // We have nothing to say! 277 278 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); 279 PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 280 N->getLocationContext()); 281 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); 282 283 // Add the range by scanning the children of the statement for any bindings 284 // to Sym. 285 for (const Stmt *Child : S->children()) 286 if (const Expr *Exp = dyn_cast_or_null<Expr>(Child)) 287 if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) { 288 P->addRange(Exp->getSourceRange()); 289 break; 290 } 291 292 return std::move(P); 293 } 294 295 static Optional<std::string> describeRegion(const MemRegion *MR) { 296 if (const auto *VR = dyn_cast_or_null<VarRegion>(MR)) 297 return std::string(VR->getDecl()->getName()); 298 // Once we support more storage locations for bindings, 299 // this would need to be improved. 300 return None; 301 } 302 303 namespace { 304 // Find the first node in the current function context that referred to the 305 // tracked symbol and the memory location that value was stored to. Note, the 306 // value is only reported if the allocation occurred in the same function as 307 // the leak. The function can also return a location context, which should be 308 // treated as interesting. 309 struct AllocationInfo { 310 const ExplodedNode* N; 311 const MemRegion *R; 312 const LocationContext *InterestingMethodContext; 313 AllocationInfo(const ExplodedNode *InN, 314 const MemRegion *InR, 315 const LocationContext *InInterestingMethodContext) : 316 N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {} 317 }; 318 } // end anonymous namespace 319 320 static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, 321 const ExplodedNode *N, SymbolRef Sym) { 322 const ExplodedNode *AllocationNode = N; 323 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N; 324 const MemRegion *FirstBinding = nullptr; 325 const LocationContext *LeakContext = N->getLocationContext(); 326 327 // The location context of the init method called on the leaked object, if 328 // available. 329 const LocationContext *InitMethodContext = nullptr; 330 331 while (N) { 332 ProgramStateRef St = N->getState(); 333 const LocationContext *NContext = N->getLocationContext(); 334 335 if (!getRefBinding(St, Sym)) 336 break; 337 338 StoreManager::FindUniqueBinding FB(Sym); 339 StateMgr.iterBindings(St, FB); 340 341 if (FB) { 342 const MemRegion *R = FB.getRegion(); 343 const VarRegion *VR = R->getBaseRegion()->getAs<VarRegion>(); 344 // Do not show local variables belonging to a function other than 345 // where the error is reported. 346 if (!VR || VR->getStackFrame() == LeakContext->getStackFrame()) 347 FirstBinding = R; 348 } 349 350 // AllocationNode is the last node in which the symbol was tracked. 351 AllocationNode = N; 352 353 // AllocationNodeInCurrentContext, is the last node in the current or 354 // parent context in which the symbol was tracked. 355 // 356 // Note that the allocation site might be in the parent conext. For example, 357 // the case where an allocation happens in a block that captures a reference 358 // to it and that reference is overwritten/dropped by another call to 359 // the block. 360 if (NContext == LeakContext || NContext->isParentOf(LeakContext)) 361 AllocationNodeInCurrentOrParentContext = N; 362 363 // Find the last init that was called on the given symbol and store the 364 // init method's location context. 365 if (!InitMethodContext) 366 if (auto CEP = N->getLocation().getAs<CallEnter>()) { 367 const Stmt *CE = CEP->getCallExpr(); 368 if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) { 369 const Stmt *RecExpr = ME->getInstanceReceiver(); 370 if (RecExpr) { 371 SVal RecV = St->getSVal(RecExpr, NContext); 372 if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym) 373 InitMethodContext = CEP->getCalleeContext(); 374 } 375 } 376 } 377 378 N = N->getFirstPred(); 379 } 380 381 // If we are reporting a leak of the object that was allocated with alloc, 382 // mark its init method as interesting. 383 const LocationContext *InterestingMethodContext = nullptr; 384 if (InitMethodContext) { 385 const ProgramPoint AllocPP = AllocationNode->getLocation(); 386 if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) 387 if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>()) 388 if (ME->getMethodFamily() == OMF_alloc) 389 InterestingMethodContext = InitMethodContext; 390 } 391 392 // If allocation happened in a function different from the leak node context, 393 // do not report the binding. 394 assert(N && "Could not find allocation node"); 395 396 if (AllocationNodeInCurrentOrParentContext && 397 AllocationNodeInCurrentOrParentContext->getLocationContext() != 398 LeakContext) 399 FirstBinding = nullptr; 400 401 return AllocationInfo(AllocationNodeInCurrentOrParentContext, 402 FirstBinding, 403 InterestingMethodContext); 404 } 405 406 std::shared_ptr<PathDiagnosticPiece> 407 CFRefReportVisitor::getEndPath(BugReporterContext &BRC, 408 const ExplodedNode *EndN, BugReport &BR) { 409 BR.markInteresting(Sym); 410 return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR); 411 } 412 413 std::shared_ptr<PathDiagnosticPiece> 414 CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, 415 const ExplodedNode *EndN, BugReport &BR) { 416 417 // Tell the BugReporterContext to report cases when the tracked symbol is 418 // assigned to different variables, etc. 419 BR.markInteresting(Sym); 420 421 // We are reporting a leak. Walk up the graph to get to the first node where 422 // the symbol appeared, and also get the first VarDecl that tracked object 423 // is stored to. 424 AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym); 425 426 const MemRegion* FirstBinding = AllocI.R; 427 BR.markInteresting(AllocI.InterestingMethodContext); 428 429 SourceManager& SM = BRC.getSourceManager(); 430 431 // Compute an actual location for the leak. Sometimes a leak doesn't 432 // occur at an actual statement (e.g., transition between blocks; end 433 // of function) so we need to walk the graph and compute a real location. 434 const ExplodedNode *LeakN = EndN; 435 PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM); 436 437 std::string sbuf; 438 llvm::raw_string_ostream os(sbuf); 439 440 os << "Object leaked: "; 441 442 Optional<std::string> RegionDescription = describeRegion(FirstBinding); 443 if (RegionDescription) { 444 os << "object allocated and stored into '" << *RegionDescription << '\''; 445 } else { 446 os << "allocated object of type " << getPrettyTypeName(Sym->getType()); 447 } 448 449 // Get the retain count. 450 const RefVal* RV = getRefBinding(EndN->getState(), Sym); 451 assert(RV); 452 453 if (RV->getKind() == RefVal::ErrorLeakReturned) { 454 // FIXME: Per comments in rdar://6320065, "create" only applies to CF 455 // objects. Only "copy", "alloc", "retain" and "new" transfer ownership 456 // to the caller for NS objects. 457 const Decl *D = &EndN->getCodeDecl(); 458 459 os << (isa<ObjCMethodDecl>(D) ? " is returned from a method " 460 : " is returned from a function "); 461 462 if (D->hasAttr<CFReturnsNotRetainedAttr>()) { 463 os << "that is annotated as CF_RETURNS_NOT_RETAINED"; 464 } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) { 465 os << "that is annotated as NS_RETURNS_NOT_RETAINED"; 466 // TODO: once the patch is ready, insert a case for OS_RETURNS_NOT_RETAINED 467 } else { 468 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { 469 if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) { 470 os << "managed by Automatic Reference Counting"; 471 } else { 472 os << "whose name ('" << MD->getSelector().getAsString() 473 << "') does not start with " 474 "'copy', 'mutableCopy', 'alloc' or 'new'." 475 " This violates the naming convention rules" 476 " given in the Memory Management Guide for Cocoa"; 477 } 478 } else { 479 const FunctionDecl *FD = cast<FunctionDecl>(D); 480 os << "whose name ('" << *FD 481 << "') does not contain 'Copy' or 'Create'. This violates the naming" 482 " convention rules given in the Memory Management Guide for Core" 483 " Foundation"; 484 } 485 } 486 } else { 487 os << " is not referenced later in this execution path and has a retain " 488 "count of +" << RV->getCount(); 489 } 490 491 return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); 492 } 493 494 void CFRefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) { 495 const SourceManager& SMgr = Ctx.getSourceManager(); 496 497 if (!sym->getOriginRegion()) 498 return; 499 500 auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion()); 501 if (Region) { 502 const Decl *PDecl = Region->getDecl(); 503 if (PDecl && isa<ParmVarDecl>(PDecl)) { 504 PathDiagnosticLocation ParamLocation = PathDiagnosticLocation::create(PDecl, SMgr); 505 Location = ParamLocation; 506 UniqueingLocation = ParamLocation; 507 UniqueingDecl = Ctx.getLocationContext()->getDecl(); 508 } 509 } 510 } 511 512 void CFRefLeakReport::deriveAllocLocation(CheckerContext &Ctx, 513 SymbolRef sym) { 514 // Most bug reports are cached at the location where they occurred. 515 // With leaks, we want to unique them by the location where they were 516 // allocated, and only report a single path. To do this, we need to find 517 // the allocation site of a piece of tracked memory, which we do via a 518 // call to GetAllocationSite. This will walk the ExplodedGraph backwards. 519 // Note that this is *not* the trimmed graph; we are guaranteed, however, 520 // that all ancestor nodes that represent the allocation site have the 521 // same SourceLocation. 522 const ExplodedNode *AllocNode = nullptr; 523 524 const SourceManager& SMgr = Ctx.getSourceManager(); 525 526 AllocationInfo AllocI = 527 GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); 528 529 AllocNode = AllocI.N; 530 AllocBinding = AllocI.R; 531 markInteresting(AllocI.InterestingMethodContext); 532 533 // Get the SourceLocation for the allocation site. 534 // FIXME: This will crash the analyzer if an allocation comes from an 535 // implicit call (ex: a destructor call). 536 // (Currently there are no such allocations in Cocoa, though.) 537 AllocStmt = PathDiagnosticLocation::getStmt(AllocNode); 538 539 if (!AllocStmt) { 540 AllocBinding = nullptr; 541 return; 542 } 543 544 PathDiagnosticLocation AllocLocation = 545 PathDiagnosticLocation::createBegin(AllocStmt, SMgr, 546 AllocNode->getLocationContext()); 547 Location = AllocLocation; 548 549 // Set uniqieing info, which will be used for unique the bug reports. The 550 // leaks should be uniqued on the allocation site. 551 UniqueingLocation = AllocLocation; 552 UniqueingDecl = AllocNode->getLocationContext()->getDecl(); 553 } 554 555 void CFRefLeakReport::createDescription(CheckerContext &Ctx, 556 bool IncludeAllocationLine) { 557 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid()); 558 Description.clear(); 559 llvm::raw_string_ostream os(Description); 560 os << "Potential leak of an object"; 561 562 Optional<std::string> RegionDescription = describeRegion(AllocBinding); 563 if (RegionDescription) { 564 os << " stored into '" << *RegionDescription << '\''; 565 if (IncludeAllocationLine) { 566 FullSourceLoc SL(AllocStmt->getBeginLoc(), Ctx.getSourceManager()); 567 os << " (allocated on line " << SL.getSpellingLineNumber() << ")"; 568 } 569 } else { 570 571 // If we can't figure out the name, just supply the type information. 572 os << " of type " << getPrettyTypeName(Sym->getType()); 573 } 574 } 575 576 CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, 577 const SummaryLogTy &Log, 578 ExplodedNode *n, SymbolRef sym, 579 CheckerContext &Ctx, 580 bool IncludeAllocationLine) 581 : CFRefReport(D, LOpts, Log, n, sym, false) { 582 583 deriveAllocLocation(Ctx, sym); 584 if (!AllocBinding) 585 deriveParamLocation(Ctx, sym); 586 587 createDescription(Ctx, IncludeAllocationLine); 588 589 addVisitor(llvm::make_unique<CFRefLeakReportVisitor>(sym, Log)); 590 } 591