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