1 //=- IvarInvalidationChecker.cpp - -*- 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 implements annotation driven invalidation checking. If a class 11 // contains a method annotated with 'objc_instance_variable_invalidator', 12 // - (void) foo 13 // __attribute__((annotate("objc_instance_variable_invalidator"))); 14 // all the "ivalidatable" instance variables of this class should be 15 // invalidated. We call an instance variable ivalidatable if it is an object of 16 // a class which contains an invalidation method. There could be multiple 17 // methods annotated with such annotations per class, either one can be used 18 // to invalidate the ivar. An ivar or property are considered to be 19 // invalidated if they are being assigned 'nil' or an invalidation method has 20 // been called on them. An invalidation method should either invalidate all 21 // the ivars or call another invalidation method (on self). 22 // 23 //===----------------------------------------------------------------------===// 24 25 #include "ClangSACheckers.h" 26 #include "clang/StaticAnalyzer/Core/Checker.h" 27 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 28 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 29 #include "clang/AST/DeclObjC.h" 30 #include "clang/AST/StmtVisitor.h" 31 #include "llvm/ADT/DenseMap.h" 32 #include "llvm/ADT/SmallString.h" 33 34 using namespace clang; 35 using namespace ento; 36 37 namespace { 38 class IvarInvalidationChecker : 39 public Checker<check::ASTDecl<ObjCMethodDecl> > { 40 41 typedef llvm::DenseSet<const ObjCMethodDecl*> MethodSet; 42 typedef llvm::DenseMap<const ObjCMethodDecl*, 43 const ObjCIvarDecl*> MethToIvarMapTy; 44 typedef llvm::DenseMap<const ObjCPropertyDecl*, 45 const ObjCIvarDecl*> PropToIvarMapTy; 46 typedef llvm::DenseMap<const ObjCIvarDecl*, 47 const ObjCPropertyDecl*> IvarToPropMapTy; 48 49 50 struct IvarInfo { 51 /// Has the ivar been invalidated? 52 bool IsInvalidated; 53 54 /// The methods which can be used to invalidate the ivar. 55 MethodSet InvalidationMethods; 56 57 IvarInfo() : IsInvalidated(false) {} 58 void addInvalidationMethod(const ObjCMethodDecl *MD) { 59 InvalidationMethods.insert(MD); 60 } 61 62 bool needsInvalidation() const { 63 return !InvalidationMethods.empty(); 64 } 65 66 void markInvalidated() { 67 IsInvalidated = true; 68 } 69 70 bool markInvalidated(const ObjCMethodDecl *MD) { 71 if (IsInvalidated) 72 return true; 73 for (MethodSet::iterator I = InvalidationMethods.begin(), 74 E = InvalidationMethods.end(); I != E; ++I) { 75 if (*I == MD) { 76 IsInvalidated = true; 77 return true; 78 } 79 } 80 return false; 81 } 82 83 bool isInvalidated() const { 84 return IsInvalidated; 85 } 86 }; 87 88 typedef llvm::DenseMap<const ObjCIvarDecl*, IvarInfo> IvarSet; 89 90 /// Statement visitor, which walks the method body and flags the ivars 91 /// referenced in it (either directly or via property). 92 class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { 93 /// The set of Ivars which need to be invalidated. 94 IvarSet &IVars; 95 96 /// Flag is set as the result of a message send to another 97 /// invalidation method. 98 bool &CalledAnotherInvalidationMethod; 99 100 /// Property setter to ivar mapping. 101 const MethToIvarMapTy &PropertySetterToIvarMap; 102 103 /// Property getter to ivar mapping. 104 const MethToIvarMapTy &PropertyGetterToIvarMap; 105 106 /// Property to ivar mapping. 107 const PropToIvarMapTy &PropertyToIvarMap; 108 109 /// The invalidation method being currently processed. 110 const ObjCMethodDecl *InvalidationMethod; 111 112 ASTContext &Ctx; 113 114 /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr. 115 const Expr *peel(const Expr *E) const; 116 117 /// Does this expression represent zero: '0'? 118 bool isZero(const Expr *E) const; 119 120 /// Mark the given ivar as invalidated. 121 void markInvalidated(const ObjCIvarDecl *Iv); 122 123 /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as 124 /// invalidated. 125 void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef); 126 127 /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks 128 /// it as invalidated. 129 void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA); 130 131 /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar, 132 /// if yes, marks it as invalidated. 133 void checkObjCMessageExpr(const ObjCMessageExpr *ME); 134 135 /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated. 136 void check(const Expr *E); 137 138 public: 139 MethodCrawler(IvarSet &InIVars, 140 bool &InCalledAnotherInvalidationMethod, 141 const MethToIvarMapTy &InPropertySetterToIvarMap, 142 const MethToIvarMapTy &InPropertyGetterToIvarMap, 143 const PropToIvarMapTy &InPropertyToIvarMap, 144 ASTContext &InCtx) 145 : IVars(InIVars), 146 CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod), 147 PropertySetterToIvarMap(InPropertySetterToIvarMap), 148 PropertyGetterToIvarMap(InPropertyGetterToIvarMap), 149 PropertyToIvarMap(InPropertyToIvarMap), 150 InvalidationMethod(0), 151 Ctx(InCtx) {} 152 153 void VisitStmt(const Stmt *S) { VisitChildren(S); } 154 155 void VisitBinaryOperator(const BinaryOperator *BO); 156 157 void VisitObjCMessageExpr(const ObjCMessageExpr *ME); 158 159 void VisitChildren(const Stmt *S) { 160 for (Stmt::const_child_range I = S->children(); I; ++I) { 161 if (*I) 162 this->Visit(*I); 163 if (CalledAnotherInvalidationMethod) 164 return; 165 } 166 } 167 }; 168 169 /// Check if the any of the methods inside the interface are annotated with 170 /// the invalidation annotation, update the IvarInfo accordingly. 171 static void containsInvalidationMethod(const ObjCContainerDecl *D, 172 IvarInfo &Out); 173 174 /// Check if ivar should be tracked and add to TrackedIvars if positive. 175 /// Returns true if ivar should be tracked. 176 static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars); 177 178 /// Given the property declaration, and the list of tracked ivars, finds 179 /// the ivar backing the property when possible. Returns '0' when no such 180 /// ivar could be found. 181 static const ObjCIvarDecl *findPropertyBackingIvar( 182 const ObjCPropertyDecl *Prop, 183 const ObjCInterfaceDecl *InterfaceD, 184 IvarSet &TrackedIvars); 185 186 public: 187 void checkASTDecl(const ObjCMethodDecl *D, AnalysisManager& Mgr, 188 BugReporter &BR) const; 189 190 // TODO: We are currently ignoring the ivars coming from class extensions. 191 }; 192 193 static bool isInvalidationMethod(const ObjCMethodDecl *M) { 194 for (specific_attr_iterator<AnnotateAttr> 195 AI = M->specific_attr_begin<AnnotateAttr>(), 196 AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) { 197 const AnnotateAttr *Ann = *AI; 198 if (Ann->getAnnotation() == "objc_instance_variable_invalidator") 199 return true; 200 } 201 return false; 202 } 203 204 void IvarInvalidationChecker::containsInvalidationMethod( 205 const ObjCContainerDecl *D, IvarInfo &OutInfo) { 206 207 // TODO: Cache the results. 208 209 if (!D) 210 return; 211 212 // Check all methods. 213 for (ObjCContainerDecl::method_iterator 214 I = D->meth_begin(), 215 E = D->meth_end(); I != E; ++I) { 216 const ObjCMethodDecl *MDI = *I; 217 if (isInvalidationMethod(MDI)) 218 OutInfo.addInvalidationMethod( 219 cast<ObjCMethodDecl>(MDI->getCanonicalDecl())); 220 } 221 222 // If interface, check all parent protocols and super. 223 // TODO: Visit all categories in case the invalidation method is declared in 224 // a category. 225 if (const ObjCInterfaceDecl *InterfaceD = dyn_cast<ObjCInterfaceDecl>(D)) { 226 for (ObjCInterfaceDecl::protocol_iterator 227 I = InterfaceD->protocol_begin(), 228 E = InterfaceD->protocol_end(); I != E; ++I) { 229 containsInvalidationMethod(*I, OutInfo); 230 } 231 containsInvalidationMethod(InterfaceD->getSuperClass(), OutInfo); 232 return; 233 } 234 235 // If protocol, check all parent protocols. 236 if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) { 237 for (ObjCInterfaceDecl::protocol_iterator 238 I = ProtD->protocol_begin(), 239 E = ProtD->protocol_end(); I != E; ++I) { 240 containsInvalidationMethod(*I, OutInfo); 241 } 242 return; 243 } 244 245 llvm_unreachable("One of the casts above should have succeeded."); 246 } 247 248 bool IvarInvalidationChecker::trackIvar(const ObjCIvarDecl *Iv, 249 IvarSet &TrackedIvars) { 250 QualType IvQTy = Iv->getType(); 251 const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>(); 252 if (!IvTy) 253 return false; 254 const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl(); 255 256 IvarInfo Info; 257 containsInvalidationMethod(IvInterf, Info); 258 if (Info.needsInvalidation()) { 259 TrackedIvars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = Info; 260 return true; 261 } 262 return false; 263 } 264 265 const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar( 266 const ObjCPropertyDecl *Prop, 267 const ObjCInterfaceDecl *InterfaceD, 268 IvarSet &TrackedIvars) { 269 const ObjCIvarDecl *IvarD = 0; 270 271 // Lookup for the synthesized case. 272 IvarD = Prop->getPropertyIvarDecl(); 273 if (IvarD) { 274 if (TrackedIvars.count(IvarD)) { 275 return IvarD; 276 } 277 // If the ivar is synthesized we still want to track it. 278 if (trackIvar(IvarD, TrackedIvars)) 279 return IvarD; 280 } 281 282 // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars. 283 StringRef PropName = Prop->getIdentifier()->getName(); 284 for (IvarSet::const_iterator I = TrackedIvars.begin(), 285 E = TrackedIvars.end(); I != E; ++I) { 286 const ObjCIvarDecl *Iv = I->first; 287 StringRef IvarName = Iv->getName(); 288 289 if (IvarName == PropName) 290 return Iv; 291 292 SmallString<128> PropNameWithUnderscore; 293 { 294 llvm::raw_svector_ostream os(PropNameWithUnderscore); 295 os << '_' << PropName; 296 } 297 if (IvarName == PropNameWithUnderscore.str()) 298 return Iv; 299 } 300 301 // Note, this is a possible source of false positives. We could look at the 302 // getter implementation to find the ivar when its name is not derived from 303 // the property name. 304 return 0; 305 } 306 307 void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D, 308 AnalysisManager& Mgr, 309 BugReporter &BR) const { 310 // We are only interested in checking the cleanup methods. 311 if (!D->hasBody() || !isInvalidationMethod(D)) 312 return; 313 314 // Collect all ivars that need cleanup. 315 IvarSet Ivars; 316 const ObjCInterfaceDecl *InterfaceD = D->getClassInterface(); 317 318 // Collect ivars declared in this class, its extensions and its implementation 319 ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD); 320 for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv; 321 Iv= Iv->getNextIvar()) 322 trackIvar(Iv, Ivars); 323 324 // Construct Property/Property Accessor to Ivar maps to assist checking if an 325 // ivar which is backing a property has been reset. 326 MethToIvarMapTy PropSetterToIvarMap; 327 MethToIvarMapTy PropGetterToIvarMap; 328 PropToIvarMapTy PropertyToIvarMap; 329 IvarToPropMapTy IvarToPopertyMap; 330 331 ObjCInterfaceDecl::PropertyMap PropMap; 332 InterfaceD->collectPropertiesToImplement(PropMap); 333 334 for (ObjCInterfaceDecl::PropertyMap::iterator 335 I = PropMap.begin(), E = PropMap.end(); I != E; ++I) { 336 const ObjCPropertyDecl *PD = I->second; 337 338 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars); 339 if (!ID) { 340 continue; 341 } 342 343 // Store the mappings. 344 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); 345 PropertyToIvarMap[PD] = ID; 346 IvarToPopertyMap[ID] = PD; 347 348 // Find the setter and the getter. 349 const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl(); 350 if (SetterD) { 351 SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl()); 352 PropSetterToIvarMap[SetterD] = ID; 353 } 354 355 const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl(); 356 if (GetterD) { 357 GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl()); 358 PropGetterToIvarMap[GetterD] = ID; 359 } 360 } 361 362 363 // Check which ivars have been invalidated in the method body. 364 bool CalledAnotherInvalidationMethod = false; 365 MethodCrawler(Ivars, 366 CalledAnotherInvalidationMethod, 367 PropSetterToIvarMap, 368 PropGetterToIvarMap, 369 PropertyToIvarMap, 370 BR.getContext()).VisitStmt(D->getBody()); 371 372 if (CalledAnotherInvalidationMethod) 373 return; 374 375 // Warn on the ivars that were not accessed by the method. 376 for (IvarSet::const_iterator I = Ivars.begin(), E = Ivars.end(); I != E; ++I){ 377 if (!I->second.isInvalidated()) { 378 const ObjCIvarDecl *IvarDecl = I->first; 379 380 PathDiagnosticLocation IvarDecLocation = 381 PathDiagnosticLocation::createEnd(D->getBody(), BR.getSourceManager(), 382 Mgr.getAnalysisDeclContext(D)); 383 384 SmallString<128> sbuf; 385 llvm::raw_svector_ostream os(sbuf); 386 387 // Construct the warning message. 388 if (IvarDecl->getSynthesize()) { 389 const ObjCPropertyDecl *PD = IvarToPopertyMap[IvarDecl]; 390 assert(PD && 391 "Do we synthesize ivars for something other than properties?"); 392 os << "Property "<< PD->getName() << 393 " needs to be invalidated or set to nil"; 394 } else { 395 os << "Instance variable "<< IvarDecl->getName() 396 << " needs to be invalidated or set to nil"; 397 } 398 399 BR.EmitBasicReport(D, 400 "Incomplete invalidation", 401 categories::CoreFoundationObjectiveC, os.str(), 402 IvarDecLocation); 403 } 404 } 405 } 406 407 void IvarInvalidationChecker::MethodCrawler::markInvalidated( 408 const ObjCIvarDecl *Iv) { 409 IvarSet::iterator I = IVars.find(Iv); 410 if (I != IVars.end()) { 411 // If InvalidationMethod is present, we are processing the message send and 412 // should ensure we are invalidating with the appropriate method, 413 // otherwise, we are processing setting to 'nil'. 414 if (InvalidationMethod) 415 I->second.markInvalidated(InvalidationMethod); 416 else 417 I->second.markInvalidated(); 418 } 419 } 420 421 const Expr *IvarInvalidationChecker::MethodCrawler::peel(const Expr *E) const { 422 E = E->IgnoreParenCasts(); 423 if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) 424 E = POE->getSyntacticForm()->IgnoreParenCasts(); 425 if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) 426 E = OVE->getSourceExpr()->IgnoreParenCasts(); 427 return E; 428 } 429 430 void IvarInvalidationChecker::MethodCrawler::checkObjCIvarRefExpr( 431 const ObjCIvarRefExpr *IvarRef) { 432 if (const Decl *D = IvarRef->getDecl()) 433 markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl())); 434 } 435 436 void IvarInvalidationChecker::MethodCrawler::checkObjCMessageExpr( 437 const ObjCMessageExpr *ME) { 438 const ObjCMethodDecl *MD = ME->getMethodDecl(); 439 if (MD) { 440 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl()); 441 MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD); 442 if (IvI != PropertyGetterToIvarMap.end()) 443 markInvalidated(IvI->second); 444 } 445 } 446 447 void IvarInvalidationChecker::MethodCrawler::checkObjCPropertyRefExpr( 448 const ObjCPropertyRefExpr *PA) { 449 450 if (PA->isExplicitProperty()) { 451 const ObjCPropertyDecl *PD = PA->getExplicitProperty(); 452 if (PD) { 453 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); 454 PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD); 455 if (IvI != PropertyToIvarMap.end()) 456 markInvalidated(IvI->second); 457 return; 458 } 459 } 460 461 if (PA->isImplicitProperty()) { 462 const ObjCMethodDecl *MD = PA->getImplicitPropertySetter(); 463 if (MD) { 464 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl()); 465 MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD); 466 if (IvI != PropertyGetterToIvarMap.end()) 467 markInvalidated(IvI->second); 468 return; 469 } 470 } 471 } 472 473 bool IvarInvalidationChecker::MethodCrawler::isZero(const Expr *E) const { 474 E = peel(E); 475 476 return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull) 477 != Expr::NPCK_NotNull); 478 } 479 480 void IvarInvalidationChecker::MethodCrawler::check(const Expr *E) { 481 E = peel(E); 482 483 if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) { 484 checkObjCIvarRefExpr(IvarRef); 485 return; 486 } 487 488 if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) { 489 checkObjCPropertyRefExpr(PropRef); 490 return; 491 } 492 493 if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) { 494 checkObjCMessageExpr(MsgExpr); 495 return; 496 } 497 } 498 499 void IvarInvalidationChecker::MethodCrawler::VisitBinaryOperator( 500 const BinaryOperator *BO) { 501 VisitStmt(BO); 502 503 if (BO->getOpcode() != BO_Assign) 504 return; 505 506 // Do we assign zero? 507 if (!isZero(BO->getRHS())) 508 return; 509 510 // Check the variable we are assigning to. 511 check(BO->getLHS()); 512 } 513 514 void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr( 515 const ObjCMessageExpr *ME) { 516 const ObjCMethodDecl *MD = ME->getMethodDecl(); 517 const Expr *Receiver = ME->getInstanceReceiver(); 518 519 // Stop if we are calling '[self invalidate]'. 520 if (Receiver && isInvalidationMethod(MD)) 521 if (Receiver->isObjCSelfExpr()) { 522 CalledAnotherInvalidationMethod = true; 523 return; 524 } 525 526 // Check if we call a setter and set the property to 'nil'. 527 if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) { 528 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl()); 529 MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD); 530 if (IvI != PropertySetterToIvarMap.end()) { 531 markInvalidated(IvI->second); 532 return; 533 } 534 } 535 536 // Check if we call the 'invalidation' routine on the ivar. 537 if (Receiver) { 538 InvalidationMethod = MD; 539 check(Receiver->IgnoreParenCasts()); 540 InvalidationMethod = 0; 541 } 542 543 VisitStmt(ME); 544 } 545 } 546 547 // Register the checker. 548 void ento::registerIvarInvalidationChecker(CheckerManager &mgr) { 549 mgr.registerChecker<IvarInvalidationChecker>(); 550 } 551