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