1 //==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- 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 checker analyzes Objective-C -dealloc methods and their callees 11 // to warn about improper releasing of instance variables that back synthesized 12 // properties. It warns about missing releases in the following cases: 13 // - When a class has a synthesized instance variable for a 'retain' or 'copy' 14 // property and lacks a -dealloc method in its implementation. 15 // - When a class has a synthesized instance variable for a 'retain'/'copy' 16 // property but the ivar is not released in -dealloc by either -release 17 // or by nilling out the property. 18 // 19 // It warns about extra releases in -dealloc (but not in callees) when a 20 // synthesized instance variable is released in the following cases: 21 // - When the property is 'assign' and is not 'readonly'. 22 // - When the property is 'weak'. 23 // 24 // This checker only warns for instance variables synthesized to back 25 // properties. Handling the more general case would require inferring whether 26 // an instance variable is stored retained or not. For synthesized properties, 27 // this is specified in the property declaration itself. 28 // 29 //===----------------------------------------------------------------------===// 30 31 #include "ClangSACheckers.h" 32 #include "clang/AST/Attr.h" 33 #include "clang/AST/DeclObjC.h" 34 #include "clang/AST/Expr.h" 35 #include "clang/AST/ExprObjC.h" 36 #include "clang/Basic/LangOptions.h" 37 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 38 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 39 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 40 #include "clang/StaticAnalyzer/Core/Checker.h" 41 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 42 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 43 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 44 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 45 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 46 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 47 #include "llvm/Support/raw_ostream.h" 48 49 using namespace clang; 50 using namespace ento; 51 52 /// Indicates whether an instance variable is required to be released in 53 /// -dealloc. 54 enum class ReleaseRequirement { 55 /// The instance variable must be released, either by calling 56 /// -release on it directly or by nilling it out with a property setter. 57 MustRelease, 58 59 /// The instance variable must not be directly released with -release. 60 MustNotReleaseDirectly, 61 62 /// The requirement for the instance variable could not be determined. 63 Unknown 64 }; 65 66 /// Returns true if the property implementation is synthesized and the 67 /// type of the property is retainable. 68 static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, 69 const ObjCIvarDecl **ID, 70 const ObjCPropertyDecl **PD) { 71 72 if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) 73 return false; 74 75 (*ID) = I->getPropertyIvarDecl(); 76 if (!(*ID)) 77 return false; 78 79 QualType T = (*ID)->getType(); 80 if (!T->isObjCRetainableType()) 81 return false; 82 83 (*PD) = I->getPropertyDecl(); 84 // Shouldn't be able to synthesize a property that doesn't exist. 85 assert(*PD); 86 87 return true; 88 } 89 90 namespace { 91 92 class ObjCDeallocChecker 93 : public Checker<check::ASTDecl<ObjCImplementationDecl>, 94 check::PreObjCMessage, check::PostObjCMessage, 95 check::BeginFunction, check::EndFunction, 96 check::PointerEscape, 97 check::PreStmt<ReturnStmt>> { 98 99 mutable IdentifierInfo *NSObjectII, *SenTestCaseII; 100 mutable Selector DeallocSel, ReleaseSel; 101 102 std::unique_ptr<BugType> MissingReleaseBugType; 103 std::unique_ptr<BugType> ExtraReleaseBugType; 104 105 public: 106 ObjCDeallocChecker(); 107 108 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, 109 BugReporter &BR) const; 110 void checkBeginFunction(CheckerContext &Ctx) const; 111 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 112 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 113 114 ProgramStateRef checkPointerEscape(ProgramStateRef State, 115 const InvalidatedSymbols &Escaped, 116 const CallEvent *Call, 117 PointerEscapeKind Kind) const; 118 void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; 119 void checkEndFunction(CheckerContext &Ctx) const; 120 121 private: 122 void diagnoseMissingReleases(CheckerContext &C) const; 123 124 bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M, 125 CheckerContext &C) const; 126 127 SymbolRef getValueExplicitlyReleased(const ObjCMethodCall &M, 128 CheckerContext &C) const; 129 SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M, 130 CheckerContext &C) const; 131 132 SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const; 133 134 ReleaseRequirement 135 getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const; 136 137 bool isInInstanceDealloc(const CheckerContext &C, SVal &SelfValOut) const; 138 bool isInInstanceDealloc(const CheckerContext &C, const LocationContext *LCtx, 139 SVal &SelfValOut) const; 140 bool instanceDeallocIsOnStack(const CheckerContext &C, 141 SVal &InstanceValOut) const; 142 143 bool isSuperDeallocMessage(const ObjCMethodCall &M) const; 144 145 const ObjCImplDecl *getContainingObjCImpl(const LocationContext *LCtx) const; 146 147 const ObjCPropertyDecl * 148 findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const; 149 150 ProgramStateRef removeValueRequiringRelease(ProgramStateRef State, 151 SymbolRef InstanceSym, 152 SymbolRef ValueSym) const; 153 154 void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; 155 156 bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const; 157 }; 158 } // End anonymous namespace. 159 160 typedef llvm::ImmutableSet<SymbolRef> SymbolSet; 161 162 /// Maps from the symbol for a class instance to the set of 163 /// symbols remaining that must be released in -dealloc. 164 REGISTER_MAP_WITH_PROGRAMSTATE(UnreleasedIvarMap, SymbolRef, SymbolSet); 165 166 template<> struct ProgramStateTrait<SymbolSet> 167 : public ProgramStatePartialTrait<SymbolSet> { 168 static void *GDMIndex() { static int index = 0; return &index; } 169 }; 170 171 172 /// An AST check that diagnose when the class requires a -dealloc method and 173 /// is missing one. 174 void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D, 175 AnalysisManager &Mgr, 176 BugReporter &BR) const { 177 assert(Mgr.getLangOpts().getGC() != LangOptions::GCOnly); 178 assert(!Mgr.getLangOpts().ObjCAutoRefCount); 179 initIdentifierInfoAndSelectors(Mgr.getASTContext()); 180 181 const ObjCInterfaceDecl *ID = D->getClassInterface(); 182 183 // Does the class contain any synthesized properties that are retainable? 184 // If not, skip the check entirely. 185 bool containsRetainedSynthesizedProperty = false; 186 for (const auto *I : D->property_impls()) { 187 if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) { 188 containsRetainedSynthesizedProperty = true; 189 break; 190 } 191 } 192 193 if (!containsRetainedSynthesizedProperty) 194 return; 195 196 // If the class is known to have a lifecycle with a separate teardown method 197 // then it may not require a -dealloc method. 198 if (classHasSeparateTeardown(ID)) 199 return; 200 201 const ObjCMethodDecl *MD = nullptr; 202 203 // Scan the instance methods for "dealloc". 204 for (const auto *I : D->instance_methods()) { 205 if (I->getSelector() == DeallocSel) { 206 MD = I; 207 break; 208 } 209 } 210 211 if (!MD) { // No dealloc found. 212 const char* Name = "Missing -dealloc"; 213 214 std::string Buf; 215 llvm::raw_string_ostream OS(Buf); 216 OS << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; 217 218 PathDiagnosticLocation DLoc = 219 PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); 220 221 BR.EmitBasicReport(D, this, Name, categories::CoreFoundationObjectiveC, 222 OS.str(), DLoc); 223 return; 224 } 225 } 226 227 /// If this is the beginning of -dealloc, mark the values initially stored in 228 /// instance variables that must be released by the end of -dealloc 229 /// as unreleased in the state. 230 void ObjCDeallocChecker::checkBeginFunction( 231 CheckerContext &C) const { 232 initIdentifierInfoAndSelectors(C.getASTContext()); 233 234 // Only do this if the current method is -dealloc. 235 SVal SelfVal; 236 if (!isInInstanceDealloc(C, SelfVal)) 237 return; 238 239 SymbolRef SelfSymbol = SelfVal.getAsSymbol(); 240 241 const LocationContext *LCtx = C.getLocationContext(); 242 ProgramStateRef InitialState = C.getState(); 243 244 ProgramStateRef State = InitialState; 245 246 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>(); 247 248 // Symbols that must be released by the end of the -dealloc; 249 SymbolSet RequiredReleases = F.getEmptySet(); 250 251 // If we're an inlined -dealloc, we should add our symbols to the existing 252 // set from our subclass. 253 if (const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol)) 254 RequiredReleases = *CurrSet; 255 256 for (auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) { 257 ReleaseRequirement Requirement = getDeallocReleaseRequirement(PropImpl); 258 if (Requirement != ReleaseRequirement::MustRelease) 259 continue; 260 261 SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal); 262 Optional<Loc> LValLoc = LVal.getAs<Loc>(); 263 if (!LValLoc) 264 continue; 265 266 SVal InitialVal = State->getSVal(LValLoc.getValue()); 267 SymbolRef Symbol = InitialVal.getAsSymbol(); 268 if (!Symbol || !isa<SymbolRegionValue>(Symbol)) 269 continue; 270 271 // Mark the value as requiring a release. 272 RequiredReleases = F.add(RequiredReleases, Symbol); 273 } 274 275 if (!RequiredReleases.isEmpty()) { 276 State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases); 277 } 278 279 if (State != InitialState) { 280 C.addTransition(State); 281 } 282 } 283 284 /// Given a symbol for an ivar, return a symbol for the instance containing 285 /// the ivar. Returns nullptr if the instance symbol cannot be found. 286 SymbolRef 287 ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const { 288 if (auto *SRV = dyn_cast<SymbolRegionValue>(IvarSym)) { 289 const TypedValueRegion *TVR = SRV->getRegion(); 290 const ObjCIvarRegion *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(TVR); 291 if (!IvarRegion) 292 return nullptr; 293 294 return IvarRegion->getSymbolicBase()->getSymbol(); 295 } 296 297 return nullptr; 298 } 299 300 /// If we are in -dealloc or -dealloc is on the stack, handle the call if it is 301 /// a release or a nilling-out property setter. 302 void ObjCDeallocChecker::checkPreObjCMessage( 303 const ObjCMethodCall &M, CheckerContext &C) const { 304 // Only run if -dealloc is on the stack. 305 SVal DeallocedInstance; 306 if (!instanceDeallocIsOnStack(C, DeallocedInstance)) 307 return; 308 309 SymbolRef ReleasedValue = getValueExplicitlyReleased(M, C); 310 311 if (ReleasedValue) { 312 // An instance variable symbol was released with -release: 313 // [_property release]; 314 if (diagnoseExtraRelease(ReleasedValue,M, C)) 315 return; 316 } else { 317 // An instance variable symbol was released nilling out its property: 318 // self.property = nil; 319 ReleasedValue = getValueReleasedByNillingOut(M, C); 320 } 321 322 if (!ReleasedValue) 323 return; 324 325 SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(ReleasedValue); 326 if (!InstanceSym) 327 return; 328 ProgramStateRef InitialState = C.getState(); 329 330 ProgramStateRef ReleasedState = 331 removeValueRequiringRelease(InitialState, InstanceSym, ReleasedValue); 332 333 if (ReleasedState != InitialState) { 334 C.addTransition(ReleasedState); 335 } 336 } 337 338 /// If the message was a call to '[super dealloc]', diagnose any missing 339 /// releases. 340 void ObjCDeallocChecker::checkPostObjCMessage( 341 const ObjCMethodCall &M, CheckerContext &C) const { 342 // We perform this check post-message so that if the super -dealloc 343 // calls a helper method and that this class overrides, any ivars released in 344 // the helper method will be recorded before checking. 345 if (isSuperDeallocMessage(M)) 346 diagnoseMissingReleases(C); 347 } 348 349 /// Check for missing releases even when -dealloc does not call 350 /// '[super dealloc]'. 351 void ObjCDeallocChecker::checkEndFunction( 352 CheckerContext &C) const { 353 diagnoseMissingReleases(C); 354 } 355 356 /// Check for missing releases on early return. 357 void ObjCDeallocChecker::checkPreStmt( 358 const ReturnStmt *RS, CheckerContext &C) const { 359 diagnoseMissingReleases(C); 360 } 361 362 /// If a symbol escapes conservatively assume unseen code released it. 363 ProgramStateRef ObjCDeallocChecker::checkPointerEscape( 364 ProgramStateRef State, const InvalidatedSymbols &Escaped, 365 const CallEvent *Call, PointerEscapeKind Kind) const { 366 367 // Don't treat calls to '[super dealloc]' as escaping for the purposes 368 // of this checker. Because the checker diagnoses missing releases in the 369 // post-message handler for '[super dealloc], escaping here would cause 370 // the checker to never warn. 371 auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call); 372 if (OMC && isSuperDeallocMessage(*OMC)) 373 return State; 374 375 for (const auto &Sym : Escaped) { 376 State = State->remove<UnreleasedIvarMap>(Sym); 377 378 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym); 379 if (!InstanceSymbol) 380 continue; 381 382 State = removeValueRequiringRelease(State, InstanceSymbol, Sym); 383 } 384 385 return State; 386 } 387 388 /// Report any unreleased instance variables for the current instance being 389 /// dealloced. 390 void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const { 391 ProgramStateRef State = C.getState(); 392 393 SVal SelfVal; 394 if (!isInInstanceDealloc(C, SelfVal)) 395 return; 396 397 const MemRegion *SelfRegion = SelfVal.castAs<loc::MemRegionVal>().getRegion(); 398 const LocationContext *LCtx = C.getLocationContext(); 399 400 ExplodedNode *ErrNode = nullptr; 401 402 SymbolRef SelfSym = SelfVal.getAsSymbol(); 403 if (!SelfSym) 404 return; 405 406 const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym); 407 if (!OldUnreleased) 408 return; 409 410 SymbolSet NewUnreleased = *OldUnreleased; 411 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>(); 412 413 ProgramStateRef InitialState = State; 414 415 for (auto *IvarSymbol : *OldUnreleased) { 416 const TypedValueRegion *TVR = 417 cast<SymbolRegionValue>(IvarSymbol)->getRegion(); 418 const ObjCIvarRegion *IvarRegion = cast<ObjCIvarRegion>(TVR); 419 420 // Don't warn if the ivar is not for this instance. 421 if (SelfRegion != IvarRegion->getSuperRegion()) 422 continue; 423 424 // Prevent an inlined call to -dealloc in a super class from warning 425 // about the values the subclass's -dealloc should release. 426 if (IvarRegion->getDecl()->getContainingInterface() != 427 cast<ObjCMethodDecl>(LCtx->getDecl())->getClassInterface()) 428 continue; 429 430 // Prevents diagnosing multiple times for the same instance variable 431 // at, for example, both a return and at the end of of the function. 432 NewUnreleased = F.remove(NewUnreleased, IvarSymbol); 433 434 if (State->getStateManager() 435 .getConstraintManager() 436 .isNull(State, IvarSymbol) 437 .isConstrainedTrue()) { 438 continue; 439 } 440 441 // A missing release manifests as a leak, so treat as a non-fatal error. 442 if (!ErrNode) 443 ErrNode = C.generateNonFatalErrorNode(); 444 // If we've already reached this node on another path, return without 445 // diagnosing. 446 if (!ErrNode) 447 return; 448 449 std::string Buf; 450 llvm::raw_string_ostream OS(Buf); 451 452 const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl(); 453 const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface(); 454 // If the class is known to have a lifecycle with teardown that is 455 // separate from -dealloc, do not warn about missing releases. We 456 // suppress here (rather than not tracking for instance variables in 457 // such classes) because these classes are rare. 458 if (classHasSeparateTeardown(Interface)) 459 return; 460 461 ObjCImplDecl *ImplDecl = Interface->getImplementation(); 462 463 const ObjCPropertyImplDecl *PropImpl = 464 ImplDecl->FindPropertyImplIvarDecl(IvarDecl->getIdentifier()); 465 466 const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl(); 467 468 assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Copy || 469 PropDecl->getSetterKind() == ObjCPropertyDecl::Retain); 470 471 OS << "The '" << *IvarDecl << "' ivar in '" << *ImplDecl 472 << "' was "; 473 474 if (PropDecl->getSetterKind() == ObjCPropertyDecl::Retain) 475 OS << "retained"; 476 else 477 OS << "copied"; 478 479 OS << " by a synthesized property but not released" 480 " before '[super dealloc]'"; 481 482 std::unique_ptr<BugReport> BR( 483 new BugReport(*MissingReleaseBugType, OS.str(), ErrNode)); 484 485 C.emitReport(std::move(BR)); 486 } 487 488 if (NewUnreleased.isEmpty()) { 489 State = State->remove<UnreleasedIvarMap>(SelfSym); 490 } else { 491 State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased); 492 } 493 494 if (ErrNode) { 495 C.addTransition(State, ErrNode); 496 } else if (State != InitialState) { 497 C.addTransition(State); 498 } 499 500 // Make sure that after checking in the top-most frame the list of 501 // tracked ivars is empty. This is intended to detect accidental leaks in 502 // the UnreleasedIvarMap program state. 503 assert(!LCtx->inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty()); 504 } 505 506 /// Emits a warning if the current context is -dealloc and \param ReleasedValue 507 /// must not be directly released in a -dealloc. Returns true if a diagnostic 508 /// was emitted. 509 bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, 510 const ObjCMethodCall &M, 511 CheckerContext &C) const { 512 SVal DeallocedInstance; 513 if (!isInInstanceDealloc(C, DeallocedInstance)) 514 return false; 515 516 // Try to get the region from which the the released value was loaded. 517 // Note that, unlike diagnosing for missing releases, here we don't track 518 // values that must not be released in the state. This is because even if 519 // these values escape, it is still an error under the rules of MRR to 520 // release them in -dealloc. 521 const MemRegion *RegionLoadedFrom = nullptr; 522 if (auto *DerivedSym = dyn_cast<SymbolDerived>(ReleasedValue)) 523 RegionLoadedFrom = DerivedSym->getRegion(); 524 else if (auto *RegionSym = dyn_cast<SymbolRegionValue>(ReleasedValue)) 525 RegionLoadedFrom = RegionSym->getRegion(); 526 else 527 return false; 528 529 auto *ReleasedIvar = dyn_cast<ObjCIvarRegion>(RegionLoadedFrom); 530 if (!ReleasedIvar) 531 return false; 532 533 if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() != 534 ReleasedIvar->getSuperRegion()) 535 return false; 536 537 const LocationContext *LCtx = C.getLocationContext(); 538 const ObjCIvarDecl *ReleasedIvarDecl = ReleasedIvar->getDecl(); 539 540 // If the ivar belongs to a property that must not be released directly 541 // in dealloc, emit a warning. 542 const ObjCImplDecl *Container = getContainingObjCImpl(LCtx); 543 const ObjCPropertyImplDecl *PropImpl = 544 Container->FindPropertyImplIvarDecl(ReleasedIvarDecl->getIdentifier()); 545 if (!PropImpl) 546 return false; 547 548 if (getDeallocReleaseRequirement(PropImpl) != 549 ReleaseRequirement::MustNotReleaseDirectly) { 550 return false; 551 } 552 553 // If the property is readwrite but it shadows a read-only property in its 554 // external interface, treat the property a read-only. If the outside 555 // world cannot write to a property then the internal implementation is free 556 // to make its own convention about whether the value is stored retained 557 // or not. We look up the shadow here rather than in 558 // getDeallocReleaseRequirement() because doing so can be expensive. 559 const ObjCPropertyDecl *PropDecl = findShadowedPropertyDecl(PropImpl); 560 if (PropDecl) { 561 if (PropDecl->isReadOnly()) 562 return false; 563 } else { 564 PropDecl = PropImpl->getPropertyDecl(); 565 } 566 567 ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); 568 if (!ErrNode) 569 return false; 570 571 std::string Buf; 572 llvm::raw_string_ostream OS(Buf); 573 574 assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak || 575 (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign && 576 !PropDecl->isReadOnly())); 577 578 OS << "The '" << *PropImpl->getPropertyIvarDecl() 579 << "' ivar in '" << *Container 580 << "' was synthesized for "; 581 582 if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak) 583 OS << "a weak"; 584 else 585 OS << "an assign, readwrite"; 586 587 OS << " property but was released in 'dealloc'"; 588 589 std::unique_ptr<BugReport> BR( 590 new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode)); 591 BR->addRange(M.getOriginExpr()->getSourceRange()); 592 593 C.emitReport(std::move(BR)); 594 595 return true; 596 } 597 598 599 ObjCDeallocChecker:: 600 ObjCDeallocChecker() 601 : NSObjectII(nullptr), SenTestCaseII(nullptr) { 602 603 MissingReleaseBugType.reset( 604 new BugType(this, "Missing ivar release (leak)", 605 categories::MemoryCoreFoundationObjectiveC)); 606 607 ExtraReleaseBugType.reset( 608 new BugType(this, "Extra ivar release", 609 categories::MemoryCoreFoundationObjectiveC)); 610 } 611 612 void ObjCDeallocChecker::initIdentifierInfoAndSelectors( 613 ASTContext &Ctx) const { 614 if (NSObjectII) 615 return; 616 617 NSObjectII = &Ctx.Idents.get("NSObject"); 618 SenTestCaseII = &Ctx.Idents.get("SenTestCase"); 619 620 IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc"); 621 IdentifierInfo *ReleaseII = &Ctx.Idents.get("release"); 622 DeallocSel = Ctx.Selectors.getSelector(0, &DeallocII); 623 ReleaseSel = Ctx.Selectors.getSelector(0, &ReleaseII); 624 } 625 626 /// Returns true if \param M is a call to '[super dealloc]'. 627 bool ObjCDeallocChecker::isSuperDeallocMessage( 628 const ObjCMethodCall &M) const { 629 if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance) 630 return false; 631 632 return M.getSelector() == DeallocSel; 633 } 634 635 /// Returns the ObjCImplDecl containing the method declaration in \param LCtx. 636 const ObjCImplDecl * 637 ObjCDeallocChecker::getContainingObjCImpl(const LocationContext *LCtx) const { 638 auto *MD = cast<ObjCMethodDecl>(LCtx->getDecl()); 639 return cast<ObjCImplDecl>(MD->getDeclContext()); 640 } 641 642 /// Returns the property that shadowed by \param PropImpl if one exists and 643 /// nullptr otherwise. 644 const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl( 645 const ObjCPropertyImplDecl *PropImpl) const { 646 const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl(); 647 648 // Only readwrite properties can shadow. 649 if (PropDecl->isReadOnly()) 650 return nullptr; 651 652 auto *CatDecl = dyn_cast<ObjCCategoryDecl>(PropDecl->getDeclContext()); 653 654 // Only class extensions can contain shadowing properties. 655 if (!CatDecl || !CatDecl->IsClassExtension()) 656 return nullptr; 657 658 IdentifierInfo *ID = PropDecl->getIdentifier(); 659 DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID); 660 for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) { 661 auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(*I); 662 if (!ShadowedPropDecl) 663 continue; 664 665 if (ShadowedPropDecl->isInstanceProperty()) { 666 assert(ShadowedPropDecl->isReadOnly()); 667 return ShadowedPropDecl; 668 } 669 } 670 671 return nullptr; 672 } 673 674 /// Remove the \param Value requiring a release from the tracked set for 675 /// \param Instance and return the resultant state. 676 ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease( 677 ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const { 678 assert(Instance); 679 assert(Value); 680 681 const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance); 682 if (!Unreleased) 683 return State; 684 685 // Mark the value as no longer requiring a release. 686 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>(); 687 SymbolSet NewUnreleased = F.remove(*Unreleased, Value); 688 689 if (NewUnreleased.isEmpty()) { 690 return State->remove<UnreleasedIvarMap>(Instance); 691 } 692 693 return State->set<UnreleasedIvarMap>(Instance, NewUnreleased); 694 } 695 696 /// Determines whether the instance variable for \p PropImpl must or must not be 697 /// released in -dealloc or whether it cannot be determined. 698 ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement( 699 const ObjCPropertyImplDecl *PropImpl) const { 700 const ObjCIvarDecl *IvarDecl; 701 const ObjCPropertyDecl *PropDecl; 702 if (!isSynthesizedRetainableProperty(PropImpl, &IvarDecl, &PropDecl)) 703 return ReleaseRequirement::Unknown; 704 705 ObjCPropertyDecl::SetterKind SK = PropDecl->getSetterKind(); 706 707 switch (SK) { 708 // Retain and copy setters retain/copy their values before storing and so 709 // the value in their instance variables must be released in -dealloc. 710 case ObjCPropertyDecl::Retain: 711 case ObjCPropertyDecl::Copy: 712 return ReleaseRequirement::MustRelease; 713 714 case ObjCPropertyDecl::Weak: 715 return ReleaseRequirement::MustNotReleaseDirectly; 716 717 case ObjCPropertyDecl::Assign: 718 // It is common for the ivars for read-only assign properties to 719 // always be stored retained, so their release requirement cannot be 720 // be determined. 721 if (PropDecl->isReadOnly()) 722 return ReleaseRequirement::Unknown; 723 724 return ReleaseRequirement::MustNotReleaseDirectly; 725 } 726 } 727 728 /// Returns the released value if \param M is a call to -release. Returns 729 /// nullptr otherwise. 730 SymbolRef 731 ObjCDeallocChecker::getValueExplicitlyReleased(const ObjCMethodCall &M, 732 CheckerContext &C) const { 733 if (M.getSelector() != ReleaseSel) 734 return nullptr; 735 736 return M.getReceiverSVal().getAsSymbol(); 737 } 738 739 /// Returns the released value if \param M is a call a setter that releases 740 /// and nils out its underlying instance variable. 741 SymbolRef 742 ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, 743 CheckerContext &C) const { 744 SVal ReceiverVal = M.getReceiverSVal(); 745 if (!ReceiverVal.isValid()) 746 return nullptr; 747 748 // Is the first argument nil? 749 if (M.getNumArgs() == 0) 750 return nullptr; 751 SVal Arg = M.getArgSVal(0); 752 ProgramStateRef notNilState, nilState; 753 std::tie(notNilState, nilState) = 754 M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>()); 755 if (!(nilState && !notNilState)) 756 return nullptr; 757 758 const ObjCPropertyDecl *Prop = M.getAccessedProperty(); 759 if (!Prop) 760 return nullptr; 761 762 ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl(); 763 if (!PropIvarDecl) 764 return nullptr; 765 766 ProgramStateRef State = C.getState(); 767 768 SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal); 769 Optional<Loc> LValLoc = LVal.getAs<Loc>(); 770 if (!LValLoc) 771 return nullptr; 772 773 SVal CurrentValInIvar = State->getSVal(LValLoc.getValue()); 774 return CurrentValInIvar.getAsSymbol(); 775 } 776 777 /// Returns true if the current context is a call to -dealloc and false 778 /// otherwise. If true, it also sets \param SelfValOut to the value of 779 /// 'self'. 780 bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, 781 SVal &SelfValOut) const { 782 return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut); 783 } 784 785 /// Returns true if \param LCtx is a call to -dealloc and false 786 /// otherwise. If true, it also sets \param SelfValOut to the value of 787 /// 'self'. 788 bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, 789 const LocationContext *LCtx, 790 SVal &SelfValOut) const { 791 auto *MD = dyn_cast<ObjCMethodDecl>(LCtx->getDecl()); 792 if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel) 793 return false; 794 795 const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl(); 796 assert(SelfDecl && "No self in -dealloc?"); 797 798 ProgramStateRef State = C.getState(); 799 SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx)); 800 return true; 801 } 802 803 /// Returns true if there is a call to -dealloc anywhere on the stack and false 804 /// otherwise. If true, it also sets \param InstanceValOut to the value of 805 /// 'self' in the frame for -dealloc. 806 bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C, 807 SVal &InstanceValOut) const { 808 const LocationContext *LCtx = C.getLocationContext(); 809 810 while (LCtx) { 811 if (isInInstanceDealloc(C, LCtx, InstanceValOut)) 812 return true; 813 814 LCtx = LCtx->getParent(); 815 } 816 817 return false; 818 } 819 820 /// Returns true if the \param ID is a class in which which is known to have 821 /// a separate teardown lifecycle. In this case, -dealloc warnings 822 /// about missing releases should be suppressed. 823 bool ObjCDeallocChecker::classHasSeparateTeardown( 824 const ObjCInterfaceDecl *ID) const { 825 // Suppress if the class is not a subclass of NSObject. 826 for ( ; ID ; ID = ID->getSuperClass()) { 827 IdentifierInfo *II = ID->getIdentifier(); 828 829 if (II == NSObjectII) 830 return false; 831 832 // FIXME: For now, ignore classes that subclass SenTestCase, as these don't 833 // need to implement -dealloc. They implement tear down in another way, 834 // which we should try and catch later. 835 // http://llvm.org/bugs/show_bug.cgi?id=3187 836 if (II == SenTestCaseII) 837 return true; 838 } 839 840 return true; 841 } 842 843 void ento::registerObjCDeallocChecker(CheckerManager &Mgr) { 844 const LangOptions &LangOpts = Mgr.getLangOpts(); 845 // These checker only makes sense under MRR. 846 if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount) 847 return; 848 849 Mgr.registerChecker<ObjCDeallocChecker>(); 850 } 851