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