1 //===- IvarInvalidationChecker.cpp ------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This checker implements annotation driven invalidation checking. If a class 10 // contains a method annotated with 'objc_instance_variable_invalidator', 11 // - (void) foo 12 // __attribute__((annotate("objc_instance_variable_invalidator"))); 13 // all the "ivalidatable" instance variables of this class should be 14 // invalidated. We call an instance variable ivalidatable if it is an object of 15 // a class which contains an invalidation method. There could be multiple 16 // methods annotated with such annotations per class, either one can be used 17 // to invalidate the ivar. An ivar or property are considered to be 18 // invalidated if they are being assigned 'nil' or an invalidation method has 19 // been called on them. An invalidation method should either invalidate all 20 // the ivars or call another invalidation method (on self). 21 // 22 // Partial invalidor annotation allows to address cases when ivars are 23 // invalidated by other methods, which might or might not be called from 24 // the invalidation method. The checker checks that each invalidation 25 // method and all the partial methods cumulatively invalidate all ivars. 26 // __attribute__((annotate("objc_instance_variable_invalidator_partial"))); 27 // 28 //===----------------------------------------------------------------------===// 29 30 #include "clang/AST/Attr.h" 31 #include "clang/AST/DeclObjC.h" 32 #include "clang/AST/StmtVisitor.h" 33 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 34 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 35 #include "clang/StaticAnalyzer/Core/Checker.h" 36 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 37 #include "llvm/ADT/DenseMap.h" 38 #include "llvm/ADT/STLExtras.h" 39 #include "llvm/ADT/SetVector.h" 40 #include "llvm/ADT/SmallString.h" 41 42 using namespace clang; 43 using namespace ento; 44 45 namespace { 46 struct ChecksFilter { 47 /// Check for missing invalidation method declarations. 48 bool check_MissingInvalidationMethod = false; 49 /// Check that all ivars are invalidated. 50 bool check_InstanceVariableInvalidation = false; 51 52 CheckerNameRef checkName_MissingInvalidationMethod; 53 CheckerNameRef checkName_InstanceVariableInvalidation; 54 }; 55 56 class IvarInvalidationCheckerImpl { 57 typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet; 58 typedef llvm::DenseMap<const ObjCMethodDecl*, 59 const ObjCIvarDecl*> MethToIvarMapTy; 60 typedef llvm::DenseMap<const ObjCPropertyDecl*, 61 const ObjCIvarDecl*> PropToIvarMapTy; 62 typedef llvm::DenseMap<const ObjCIvarDecl*, 63 const ObjCPropertyDecl*> IvarToPropMapTy; 64 65 struct InvalidationInfo { 66 /// Has the ivar been invalidated? 67 bool IsInvalidated = false; 68 69 /// The methods which can be used to invalidate the ivar. 70 MethodSet InvalidationMethods; 71 72 InvalidationInfo() = default; 73 void addInvalidationMethod(const ObjCMethodDecl *MD) { 74 InvalidationMethods.insert(MD); 75 } 76 77 bool needsInvalidation() const { 78 return !InvalidationMethods.empty(); 79 } 80 81 bool hasMethod(const ObjCMethodDecl *MD) { 82 if (IsInvalidated) 83 return true; 84 for (const ObjCMethodDecl *Curr : InvalidationMethods) { 85 if (Curr == MD) { 86 IsInvalidated = true; 87 return true; 88 } 89 } 90 return false; 91 } 92 }; 93 94 typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet; 95 96 /// Statement visitor, which walks the method body and flags the ivars 97 /// referenced in it (either directly or via property). 98 class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { 99 /// The set of Ivars which need to be invalidated. 100 IvarSet &IVars; 101 102 /// Flag is set as the result of a message send to another 103 /// invalidation method. 104 bool &CalledAnotherInvalidationMethod; 105 106 /// Property setter to ivar mapping. 107 const MethToIvarMapTy &PropertySetterToIvarMap; 108 109 /// Property getter to ivar mapping. 110 const MethToIvarMapTy &PropertyGetterToIvarMap; 111 112 /// Property to ivar mapping. 113 const PropToIvarMapTy &PropertyToIvarMap; 114 115 /// The invalidation method being currently processed. 116 const ObjCMethodDecl *InvalidationMethod; 117 118 ASTContext &Ctx; 119 120 /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr. 121 const Expr *peel(const Expr *E) const; 122 123 /// Does this expression represent zero: '0'? 124 bool isZero(const Expr *E) const; 125 126 /// Mark the given ivar as invalidated. 127 void markInvalidated(const ObjCIvarDecl *Iv); 128 129 /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as 130 /// invalidated. 131 void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef); 132 133 /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks 134 /// it as invalidated. 135 void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA); 136 137 /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar, 138 /// if yes, marks it as invalidated. 139 void checkObjCMessageExpr(const ObjCMessageExpr *ME); 140 141 /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated. 142 void check(const Expr *E); 143 144 public: 145 MethodCrawler(IvarSet &InIVars, 146 bool &InCalledAnotherInvalidationMethod, 147 const MethToIvarMapTy &InPropertySetterToIvarMap, 148 const MethToIvarMapTy &InPropertyGetterToIvarMap, 149 const PropToIvarMapTy &InPropertyToIvarMap, 150 ASTContext &InCtx) 151 : IVars(InIVars), 152 CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod), 153 PropertySetterToIvarMap(InPropertySetterToIvarMap), 154 PropertyGetterToIvarMap(InPropertyGetterToIvarMap), 155 PropertyToIvarMap(InPropertyToIvarMap), 156 InvalidationMethod(nullptr), 157 Ctx(InCtx) {} 158 159 void VisitStmt(const Stmt *S) { VisitChildren(S); } 160 161 void VisitBinaryOperator(const BinaryOperator *BO); 162 163 void VisitObjCMessageExpr(const ObjCMessageExpr *ME); 164 165 void VisitChildren(const Stmt *S) { 166 for (const auto *Child : S->children()) { 167 if (Child) 168 this->Visit(Child); 169 if (CalledAnotherInvalidationMethod) 170 return; 171 } 172 } 173 }; 174 175 /// Check if the any of the methods inside the interface are annotated with 176 /// the invalidation annotation, update the IvarInfo accordingly. 177 /// \param LookForPartial is set when we are searching for partial 178 /// invalidators. 179 static void containsInvalidationMethod(const ObjCContainerDecl *D, 180 InvalidationInfo &Out, 181 bool LookForPartial); 182 183 /// Check if ivar should be tracked and add to TrackedIvars if positive. 184 /// Returns true if ivar should be tracked. 185 static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars, 186 const ObjCIvarDecl **FirstIvarDecl); 187 188 /// Given the property declaration, and the list of tracked ivars, finds 189 /// the ivar backing the property when possible. Returns '0' when no such 190 /// ivar could be found. 191 static const ObjCIvarDecl *findPropertyBackingIvar( 192 const ObjCPropertyDecl *Prop, 193 const ObjCInterfaceDecl *InterfaceD, 194 IvarSet &TrackedIvars, 195 const ObjCIvarDecl **FirstIvarDecl); 196 197 /// Print ivar name or the property if the given ivar backs a property. 198 static void printIvar(llvm::raw_svector_ostream &os, 199 const ObjCIvarDecl *IvarDecl, 200 const IvarToPropMapTy &IvarToPopertyMap); 201 202 void reportNoInvalidationMethod(CheckerNameRef CheckName, 203 const ObjCIvarDecl *FirstIvarDecl, 204 const IvarToPropMapTy &IvarToPopertyMap, 205 const ObjCInterfaceDecl *InterfaceD, 206 bool MissingDeclaration) const; 207 208 void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, 209 const IvarToPropMapTy &IvarToPopertyMap, 210 const ObjCMethodDecl *MethodD) const; 211 212 AnalysisManager& Mgr; 213 BugReporter &BR; 214 /// Filter on the checks performed. 215 const ChecksFilter &Filter; 216 217 public: 218 IvarInvalidationCheckerImpl(AnalysisManager& InMgr, 219 BugReporter &InBR, 220 const ChecksFilter &InFilter) : 221 Mgr (InMgr), BR(InBR), Filter(InFilter) {} 222 223 void visit(const ObjCImplementationDecl *D) const; 224 }; 225 226 static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) { 227 for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) { 228 if (!LookForPartial && 229 Ann->getAnnotation() == "objc_instance_variable_invalidator") 230 return true; 231 if (LookForPartial && 232 Ann->getAnnotation() == "objc_instance_variable_invalidator_partial") 233 return true; 234 } 235 return false; 236 } 237 238 void IvarInvalidationCheckerImpl::containsInvalidationMethod( 239 const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) { 240 241 if (!D) 242 return; 243 244 assert(!isa<ObjCImplementationDecl>(D)); 245 // TODO: Cache the results. 246 247 // Check all methods. 248 for (const auto *MDI : D->methods()) 249 if (isInvalidationMethod(MDI, Partial)) 250 OutInfo.addInvalidationMethod( 251 cast<ObjCMethodDecl>(MDI->getCanonicalDecl())); 252 253 // If interface, check all parent protocols and super. 254 if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) { 255 256 // Visit all protocols. 257 for (const auto *I : InterfD->protocols()) 258 containsInvalidationMethod(I->getDefinition(), OutInfo, Partial); 259 260 // Visit all categories in case the invalidation method is declared in 261 // a category. 262 for (const auto *Ext : InterfD->visible_extensions()) 263 containsInvalidationMethod(Ext, OutInfo, Partial); 264 265 containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial); 266 return; 267 } 268 269 // If protocol, check all parent protocols. 270 if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) { 271 for (const auto *I : ProtD->protocols()) { 272 containsInvalidationMethod(I->getDefinition(), OutInfo, Partial); 273 } 274 return; 275 } 276 } 277 278 bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv, 279 IvarSet &TrackedIvars, 280 const ObjCIvarDecl **FirstIvarDecl) { 281 QualType IvQTy = Iv->getType(); 282 const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>(); 283 if (!IvTy) 284 return false; 285 const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl(); 286 287 InvalidationInfo Info; 288 containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false); 289 if (Info.needsInvalidation()) { 290 const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl()); 291 TrackedIvars[I] = Info; 292 if (!*FirstIvarDecl) 293 *FirstIvarDecl = I; 294 return true; 295 } 296 return false; 297 } 298 299 const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar( 300 const ObjCPropertyDecl *Prop, 301 const ObjCInterfaceDecl *InterfaceD, 302 IvarSet &TrackedIvars, 303 const ObjCIvarDecl **FirstIvarDecl) { 304 const ObjCIvarDecl *IvarD = nullptr; 305 306 // Lookup for the synthesized case. 307 IvarD = Prop->getPropertyIvarDecl(); 308 // We only track the ivars/properties that are defined in the current 309 // class (not the parent). 310 if (IvarD && IvarD->getContainingInterface() == InterfaceD) { 311 if (TrackedIvars.count(IvarD)) { 312 return IvarD; 313 } 314 // If the ivar is synthesized we still want to track it. 315 if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl)) 316 return IvarD; 317 } 318 319 // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars. 320 StringRef PropName = Prop->getIdentifier()->getName(); 321 for (const ObjCIvarDecl *Iv : llvm::make_first_range(TrackedIvars)) { 322 StringRef IvarName = Iv->getName(); 323 324 if (IvarName == PropName) 325 return Iv; 326 327 SmallString<128> PropNameWithUnderscore; 328 { 329 llvm::raw_svector_ostream os(PropNameWithUnderscore); 330 os << '_' << PropName; 331 } 332 if (IvarName == PropNameWithUnderscore) 333 return Iv; 334 } 335 336 // Note, this is a possible source of false positives. We could look at the 337 // getter implementation to find the ivar when its name is not derived from 338 // the property name. 339 return nullptr; 340 } 341 342 void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os, 343 const ObjCIvarDecl *IvarDecl, 344 const IvarToPropMapTy &IvarToPopertyMap) { 345 if (IvarDecl->getSynthesize()) { 346 const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl); 347 assert(PD &&"Do we synthesize ivars for something other than properties?"); 348 os << "Property "<< PD->getName() << " "; 349 } else { 350 os << "Instance variable "<< IvarDecl->getName() << " "; 351 } 352 } 353 354 // Check that the invalidatable interfaces with ivars/properties implement the 355 // invalidation methods. 356 void IvarInvalidationCheckerImpl:: 357 visit(const ObjCImplementationDecl *ImplD) const { 358 // Collect all ivars that need cleanup. 359 IvarSet Ivars; 360 // Record the first Ivar needing invalidation; used in reporting when only 361 // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure 362 // deterministic output. 363 const ObjCIvarDecl *FirstIvarDecl = nullptr; 364 const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface(); 365 366 // Collect ivars declared in this class, its extensions and its implementation 367 ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD); 368 for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv; 369 Iv= Iv->getNextIvar()) 370 trackIvar(Iv, Ivars, &FirstIvarDecl); 371 372 // Construct Property/Property Accessor to Ivar maps to assist checking if an 373 // ivar which is backing a property has been reset. 374 MethToIvarMapTy PropSetterToIvarMap; 375 MethToIvarMapTy PropGetterToIvarMap; 376 PropToIvarMapTy PropertyToIvarMap; 377 IvarToPropMapTy IvarToPopertyMap; 378 379 ObjCInterfaceDecl::PropertyMap PropMap; 380 InterfaceD->collectPropertiesToImplement(PropMap); 381 382 for (const ObjCPropertyDecl *PD : llvm::make_second_range(PropMap)) { 383 if (PD->isClassProperty()) 384 continue; 385 386 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars, 387 &FirstIvarDecl); 388 if (!ID) 389 continue; 390 391 // Store the mappings. 392 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); 393 PropertyToIvarMap[PD] = ID; 394 IvarToPopertyMap[ID] = PD; 395 396 // Find the setter and the getter. 397 const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl(); 398 if (SetterD) { 399 SetterD = SetterD->getCanonicalDecl(); 400 PropSetterToIvarMap[SetterD] = ID; 401 } 402 403 const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl(); 404 if (GetterD) { 405 GetterD = GetterD->getCanonicalDecl(); 406 PropGetterToIvarMap[GetterD] = ID; 407 } 408 } 409 410 // If no ivars need invalidation, there is nothing to check here. 411 if (Ivars.empty()) 412 return; 413 414 // Find all partial invalidation methods. 415 InvalidationInfo PartialInfo; 416 containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true); 417 418 // Remove ivars invalidated by the partial invalidation methods. They do not 419 // need to be invalidated in the regular invalidation methods. 420 bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false; 421 for (const ObjCMethodDecl *InterfD : PartialInfo.InvalidationMethods) { 422 // Get the corresponding method in the @implementation. 423 const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), 424 InterfD->isInstanceMethod()); 425 if (D && D->hasBody()) { 426 AtImplementationContainsAtLeastOnePartialInvalidationMethod = true; 427 428 bool CalledAnotherInvalidationMethod = false; 429 // The MethodCrowler is going to remove the invalidated ivars. 430 MethodCrawler(Ivars, 431 CalledAnotherInvalidationMethod, 432 PropSetterToIvarMap, 433 PropGetterToIvarMap, 434 PropertyToIvarMap, 435 BR.getContext()).VisitStmt(D->getBody()); 436 // If another invalidation method was called, trust that full invalidation 437 // has occurred. 438 if (CalledAnotherInvalidationMethod) 439 Ivars.clear(); 440 } 441 } 442 443 // If all ivars have been invalidated by partial invalidators, there is 444 // nothing to check here. 445 if (Ivars.empty()) 446 return; 447 448 // Find all invalidation methods in this @interface declaration and parents. 449 InvalidationInfo Info; 450 containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false); 451 452 // Report an error in case none of the invalidation methods are declared. 453 if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) { 454 if (Filter.check_MissingInvalidationMethod) 455 reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod, 456 FirstIvarDecl, IvarToPopertyMap, InterfaceD, 457 /*MissingDeclaration*/ true); 458 // If there are no invalidation methods, there is no ivar validation work 459 // to be done. 460 return; 461 } 462 463 // Only check if Ivars are invalidated when InstanceVariableInvalidation 464 // has been requested. 465 if (!Filter.check_InstanceVariableInvalidation) 466 return; 467 468 // Check that all ivars are invalidated by the invalidation methods. 469 bool AtImplementationContainsAtLeastOneInvalidationMethod = false; 470 for (const ObjCMethodDecl *InterfD : Info.InvalidationMethods) { 471 // Get the corresponding method in the @implementation. 472 const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), 473 InterfD->isInstanceMethod()); 474 if (D && D->hasBody()) { 475 AtImplementationContainsAtLeastOneInvalidationMethod = true; 476 477 // Get a copy of ivars needing invalidation. 478 IvarSet IvarsI = Ivars; 479 480 bool CalledAnotherInvalidationMethod = false; 481 MethodCrawler(IvarsI, 482 CalledAnotherInvalidationMethod, 483 PropSetterToIvarMap, 484 PropGetterToIvarMap, 485 PropertyToIvarMap, 486 BR.getContext()).VisitStmt(D->getBody()); 487 // If another invalidation method was called, trust that full invalidation 488 // has occurred. 489 if (CalledAnotherInvalidationMethod) 490 continue; 491 492 // Warn on the ivars that were not invalidated by the method. 493 for (const ObjCIvarDecl *Ivar : llvm::make_first_range(IvarsI)) 494 reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap, D); 495 } 496 } 497 498 // Report an error in case none of the invalidation methods are implemented. 499 if (!AtImplementationContainsAtLeastOneInvalidationMethod) { 500 if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) { 501 // Warn on the ivars that were not invalidated by the prrtial 502 // invalidation methods. 503 for (const ObjCIvarDecl *Ivar : llvm::make_first_range(Ivars)) 504 reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap, nullptr); 505 } else { 506 // Otherwise, no invalidation methods were implemented. 507 reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation, 508 FirstIvarDecl, IvarToPopertyMap, InterfaceD, 509 /*MissingDeclaration*/ false); 510 } 511 } 512 } 513 514 void IvarInvalidationCheckerImpl::reportNoInvalidationMethod( 515 CheckerNameRef CheckName, const ObjCIvarDecl *FirstIvarDecl, 516 const IvarToPropMapTy &IvarToPopertyMap, 517 const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const { 518 SmallString<128> sbuf; 519 llvm::raw_svector_ostream os(sbuf); 520 assert(FirstIvarDecl); 521 printIvar(os, FirstIvarDecl, IvarToPopertyMap); 522 os << "needs to be invalidated; "; 523 if (MissingDeclaration) 524 os << "no invalidation method is declared for "; 525 else 526 os << "no invalidation method is defined in the @implementation for "; 527 os << InterfaceD->getName(); 528 529 PathDiagnosticLocation IvarDecLocation = 530 PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager()); 531 532 BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation", 533 categories::CoreFoundationObjectiveC, os.str(), 534 IvarDecLocation); 535 } 536 537 void IvarInvalidationCheckerImpl:: 538 reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, 539 const IvarToPropMapTy &IvarToPopertyMap, 540 const ObjCMethodDecl *MethodD) const { 541 SmallString<128> sbuf; 542 llvm::raw_svector_ostream os(sbuf); 543 printIvar(os, IvarD, IvarToPopertyMap); 544 os << "needs to be invalidated or set to nil"; 545 if (MethodD) { 546 PathDiagnosticLocation MethodDecLocation = 547 PathDiagnosticLocation::createEnd(MethodD->getBody(), 548 BR.getSourceManager(), 549 Mgr.getAnalysisDeclContext(MethodD)); 550 BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation, 551 "Incomplete invalidation", 552 categories::CoreFoundationObjectiveC, os.str(), 553 MethodDecLocation); 554 } else { 555 BR.EmitBasicReport( 556 IvarD, Filter.checkName_InstanceVariableInvalidation, 557 "Incomplete invalidation", categories::CoreFoundationObjectiveC, 558 os.str(), 559 PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager())); 560 } 561 } 562 563 void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated( 564 const ObjCIvarDecl *Iv) { 565 IvarSet::iterator I = IVars.find(Iv); 566 if (I != IVars.end()) { 567 // If InvalidationMethod is present, we are processing the message send and 568 // should ensure we are invalidating with the appropriate method, 569 // otherwise, we are processing setting to 'nil'. 570 if (!InvalidationMethod || I->second.hasMethod(InvalidationMethod)) 571 IVars.erase(I); 572 } 573 } 574 575 const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const { 576 E = E->IgnoreParenCasts(); 577 if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) 578 E = POE->getSyntacticForm()->IgnoreParenCasts(); 579 if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) 580 E = OVE->getSourceExpr()->IgnoreParenCasts(); 581 return E; 582 } 583 584 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr( 585 const ObjCIvarRefExpr *IvarRef) { 586 if (const Decl *D = IvarRef->getDecl()) 587 markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl())); 588 } 589 590 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr( 591 const ObjCMessageExpr *ME) { 592 const ObjCMethodDecl *MD = ME->getMethodDecl(); 593 if (MD) { 594 MD = MD->getCanonicalDecl(); 595 MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD); 596 if (IvI != PropertyGetterToIvarMap.end()) 597 markInvalidated(IvI->second); 598 } 599 } 600 601 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr( 602 const ObjCPropertyRefExpr *PA) { 603 604 if (PA->isExplicitProperty()) { 605 const ObjCPropertyDecl *PD = PA->getExplicitProperty(); 606 if (PD) { 607 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); 608 PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD); 609 if (IvI != PropertyToIvarMap.end()) 610 markInvalidated(IvI->second); 611 return; 612 } 613 } 614 615 if (PA->isImplicitProperty()) { 616 const ObjCMethodDecl *MD = PA->getImplicitPropertySetter(); 617 if (MD) { 618 MD = MD->getCanonicalDecl(); 619 MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD); 620 if (IvI != PropertyGetterToIvarMap.end()) 621 markInvalidated(IvI->second); 622 return; 623 } 624 } 625 } 626 627 bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const { 628 E = peel(E); 629 630 return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull) 631 != Expr::NPCK_NotNull); 632 } 633 634 void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) { 635 E = peel(E); 636 637 if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) { 638 checkObjCIvarRefExpr(IvarRef); 639 return; 640 } 641 642 if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) { 643 checkObjCPropertyRefExpr(PropRef); 644 return; 645 } 646 647 if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) { 648 checkObjCMessageExpr(MsgExpr); 649 return; 650 } 651 } 652 653 void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator( 654 const BinaryOperator *BO) { 655 VisitStmt(BO); 656 657 // Do we assign/compare against zero? If yes, check the variable we are 658 // assigning to. 659 BinaryOperatorKind Opcode = BO->getOpcode(); 660 if (Opcode != BO_Assign && 661 Opcode != BO_EQ && 662 Opcode != BO_NE) 663 return; 664 665 if (isZero(BO->getRHS())) { 666 check(BO->getLHS()); 667 return; 668 } 669 670 if (Opcode != BO_Assign && isZero(BO->getLHS())) { 671 check(BO->getRHS()); 672 return; 673 } 674 } 675 676 void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr( 677 const ObjCMessageExpr *ME) { 678 const ObjCMethodDecl *MD = ME->getMethodDecl(); 679 const Expr *Receiver = ME->getInstanceReceiver(); 680 681 // Stop if we are calling '[self invalidate]'. 682 if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false)) 683 if (Receiver->isObjCSelfExpr()) { 684 CalledAnotherInvalidationMethod = true; 685 return; 686 } 687 688 // Check if we call a setter and set the property to 'nil'. 689 if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) { 690 MD = MD->getCanonicalDecl(); 691 MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD); 692 if (IvI != PropertySetterToIvarMap.end()) { 693 markInvalidated(IvI->second); 694 return; 695 } 696 } 697 698 // Check if we call the 'invalidation' routine on the ivar. 699 if (Receiver) { 700 InvalidationMethod = MD; 701 check(Receiver->IgnoreParenCasts()); 702 InvalidationMethod = nullptr; 703 } 704 705 VisitStmt(ME); 706 } 707 } // end anonymous namespace 708 709 // Register the checkers. 710 namespace { 711 class IvarInvalidationChecker : 712 public Checker<check::ASTDecl<ObjCImplementationDecl> > { 713 public: 714 ChecksFilter Filter; 715 public: 716 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, 717 BugReporter &BR) const { 718 IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter); 719 Walker.visit(D); 720 } 721 }; 722 } // end anonymous namespace 723 724 void ento::registerIvarInvalidationModeling(CheckerManager &mgr) { 725 mgr.registerChecker<IvarInvalidationChecker>(); 726 } 727 728 bool ento::shouldRegisterIvarInvalidationModeling(const CheckerManager &mgr) { 729 return true; 730 } 731 732 #define REGISTER_CHECKER(name) \ 733 void ento::register##name(CheckerManager &mgr) { \ 734 IvarInvalidationChecker *checker = \ 735 mgr.getChecker<IvarInvalidationChecker>(); \ 736 checker->Filter.check_##name = true; \ 737 checker->Filter.checkName_##name = mgr.getCurrentCheckerName(); \ 738 } \ 739 \ 740 bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } 741 742 REGISTER_CHECKER(InstanceVariableInvalidation) 743 REGISTER_CHECKER(MissingInvalidationMethod) 744