xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp (revision 3a02247dc9e39bc62c5ca6bbe6a3c34d746b5407)
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