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