xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp (revision bfacf17b8b99f2c6d51bf570a11d6e6a5d3a2a39)
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     const ObjCMethodDecl *EnclosingMethod;
94 
95     /// The set of Ivars which need to be invalidated.
96     IvarSet &IVars;
97 
98     /// Flag is set as the result of a message send to another
99     /// invalidation method.
100     bool &CalledAnotherInvalidationMethod;
101 
102     /// Property setter to ivar mapping.
103     const MethToIvarMapTy &PropertySetterToIvarMap;
104 
105     /// Property getter to ivar mapping.
106     const MethToIvarMapTy &PropertyGetterToIvarMap;
107 
108     /// Property to ivar mapping.
109     const PropToIvarMapTy &PropertyToIvarMap;
110 
111     /// The invalidation method being currently processed.
112     const ObjCMethodDecl *InvalidationMethod;
113 
114     ASTContext &Ctx;
115 
116     /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
117     const Expr *peel(const Expr *E) const;
118 
119     /// Does this expression represent zero: '0'?
120     bool isZero(const Expr *E) const;
121 
122     /// Mark the given ivar as invalidated.
123     void markInvalidated(const ObjCIvarDecl *Iv);
124 
125     /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
126     /// invalidated.
127     void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
128 
129     /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
130     /// it as invalidated.
131     void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
132 
133     /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
134     /// if yes, marks it as invalidated.
135     void checkObjCMessageExpr(const ObjCMessageExpr *ME);
136 
137     /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
138     void check(const Expr *E);
139 
140   public:
141     MethodCrawler(const ObjCMethodDecl *InMeth,
142                   IvarSet &InIVars,
143                   bool &InCalledAnotherInvalidationMethod,
144                   const MethToIvarMapTy &InPropertySetterToIvarMap,
145                   const MethToIvarMapTy &InPropertyGetterToIvarMap,
146                   const PropToIvarMapTy &InPropertyToIvarMap,
147                   ASTContext &InCtx)
148     : EnclosingMethod(InMeth),
149       IVars(InIVars),
150       CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
151       PropertySetterToIvarMap(InPropertySetterToIvarMap),
152       PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
153       PropertyToIvarMap(InPropertyToIvarMap),
154       InvalidationMethod(0),
155       Ctx(InCtx) {}
156 
157     void VisitStmt(const Stmt *S) { VisitChildren(S); }
158 
159     void VisitBinaryOperator(const BinaryOperator *BO);
160 
161     void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
162 
163     void VisitChildren(const Stmt *S) {
164       for (Stmt::const_child_range I = S->children(); I; ++I) {
165         if (*I)
166           this->Visit(*I);
167         if (CalledAnotherInvalidationMethod)
168           return;
169       }
170     }
171   };
172 
173   /// Check if the any of the methods inside the interface are annotated with
174   /// the invalidation annotation, update the IvarInfo accordingly.
175   static void containsInvalidationMethod(const ObjCContainerDecl *D,
176                                          IvarInfo &Out);
177 
178   /// Check if ivar should be tracked and add to TrackedIvars if positive.
179   /// Returns true if ivar should be tracked.
180   static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars);
181 
182   /// Given the property declaration, and the list of tracked ivars, finds
183   /// the ivar backing the property when possible. Returns '0' when no such
184   /// ivar could be found.
185   static const ObjCIvarDecl *findPropertyBackingIvar(
186       const ObjCPropertyDecl *Prop,
187       const ObjCInterfaceDecl *InterfaceD,
188       IvarSet &TrackedIvars);
189 
190 public:
191   void checkASTDecl(const ObjCMethodDecl *D, AnalysisManager& Mgr,
192                     BugReporter &BR) const;
193 
194   // TODO: We are currently ignoring the ivars coming from class extensions.
195 };
196 
197 static bool isInvalidationMethod(const ObjCMethodDecl *M) {
198   for (specific_attr_iterator<AnnotateAttr>
199        AI = M->specific_attr_begin<AnnotateAttr>(),
200        AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) {
201     const AnnotateAttr *Ann = *AI;
202     if (Ann->getAnnotation() == "objc_instance_variable_invalidator")
203       return true;
204   }
205   return false;
206 }
207 
208 void IvarInvalidationChecker::containsInvalidationMethod(
209     const ObjCContainerDecl *D, IvarInfo &OutInfo) {
210 
211   // TODO: Cache the results.
212 
213   if (!D)
214     return;
215 
216   // Check all methods.
217   for (ObjCContainerDecl::method_iterator
218       I = D->meth_begin(),
219       E = D->meth_end(); I != E; ++I) {
220       const ObjCMethodDecl *MDI = *I;
221       if (isInvalidationMethod(MDI))
222         OutInfo.addInvalidationMethod(
223                                cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
224   }
225 
226   // If interface, check all parent protocols and super.
227   // TODO: Visit all categories in case the invalidation method is declared in
228   // a category.
229   if (const ObjCInterfaceDecl *InterfaceD = dyn_cast<ObjCInterfaceDecl>(D)) {
230     for (ObjCInterfaceDecl::protocol_iterator
231         I = InterfaceD->protocol_begin(),
232         E = InterfaceD->protocol_end(); I != E; ++I) {
233       containsInvalidationMethod(*I, OutInfo);
234     }
235     containsInvalidationMethod(InterfaceD->getSuperClass(), OutInfo);
236     return;
237   }
238 
239   // If protocol, check all parent protocols.
240   if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
241     for (ObjCInterfaceDecl::protocol_iterator
242         I = ProtD->protocol_begin(),
243         E = ProtD->protocol_end(); I != E; ++I) {
244       containsInvalidationMethod(*I, OutInfo);
245     }
246     return;
247   }
248 
249   llvm_unreachable("One of the casts above should have succeeded.");
250 }
251 
252 bool IvarInvalidationChecker::trackIvar(const ObjCIvarDecl *Iv,
253                                         IvarSet &TrackedIvars) {
254   QualType IvQTy = Iv->getType();
255   const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
256   if (!IvTy)
257     return false;
258   const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
259 
260   IvarInfo Info;
261   containsInvalidationMethod(IvInterf, Info);
262   if (Info.needsInvalidation()) {
263     TrackedIvars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = Info;
264     return true;
265   }
266   return false;
267 }
268 
269 const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar(
270                         const ObjCPropertyDecl *Prop,
271                         const ObjCInterfaceDecl *InterfaceD,
272                         IvarSet &TrackedIvars) {
273   const ObjCIvarDecl *IvarD = 0;
274 
275   // Lookup for the synthesized case.
276   IvarD = Prop->getPropertyIvarDecl();
277   if (IvarD) {
278     if (TrackedIvars.count(IvarD)) {
279       return IvarD;
280     }
281     // If the ivar is synthesized we still want to track it.
282     if (trackIvar(IvarD, TrackedIvars))
283       return IvarD;
284   }
285 
286   // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
287   StringRef PropName = Prop->getIdentifier()->getName();
288   for (IvarSet::const_iterator I = TrackedIvars.begin(),
289                                E = TrackedIvars.end(); I != E; ++I) {
290     const ObjCIvarDecl *Iv = I->first;
291     StringRef IvarName = Iv->getName();
292 
293     if (IvarName == PropName)
294       return Iv;
295 
296     SmallString<128> PropNameWithUnderscore;
297     {
298       llvm::raw_svector_ostream os(PropNameWithUnderscore);
299       os << '_' << PropName;
300     }
301     if (IvarName == PropNameWithUnderscore.str())
302       return Iv;
303   }
304 
305   // Note, this is a possible source of false positives. We could look at the
306   // getter implementation to find the ivar when its name is not derived from
307   // the property name.
308   return 0;
309 }
310 
311 void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D,
312                                           AnalysisManager& Mgr,
313                                           BugReporter &BR) const {
314   // We are only interested in checking the cleanup methods.
315   if (!D->hasBody() || !isInvalidationMethod(D))
316     return;
317 
318   // Collect all ivars that need cleanup.
319   IvarSet Ivars;
320   const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
321   for (ObjCInterfaceDecl::ivar_iterator
322       II = InterfaceD->ivar_begin(),
323       IE = InterfaceD->ivar_end(); II != IE; ++II) {
324     const ObjCIvarDecl *Iv = *II;
325     trackIvar(Iv, Ivars);
326   }
327 
328   // Construct Property/Property Accessor to Ivar maps to assist checking if an
329   // ivar which is backing a property has been reset.
330   MethToIvarMapTy PropSetterToIvarMap;
331   MethToIvarMapTy PropGetterToIvarMap;
332   PropToIvarMapTy PropertyToIvarMap;
333   IvarToPropMapTy IvarToPopertyMap;
334   for (ObjCInterfaceDecl::prop_iterator
335       I = InterfaceD->prop_begin(),
336       E = InterfaceD->prop_end(); I != E; ++I) {
337     const ObjCPropertyDecl *PD = *I;
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(D, 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() << " needs to be invalidated";
394       } else {
395         os << "Instance variable "<< IvarDecl->getName()
396              << " needs to be invalidated";
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 (const DeclRefExpr *RD =
522           dyn_cast<DeclRefExpr>(Receiver->IgnoreParenCasts())) {
523       if (RD->getDecl() == EnclosingMethod->getSelfDecl()) {
524         CalledAnotherInvalidationMethod = true;
525         return;
526       }
527     }
528 
529   // Check if we call a setter and set the property to 'nil'.
530   if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
531     MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
532     MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
533     if (IvI != PropertySetterToIvarMap.end()) {
534       markInvalidated(IvI->second);
535       return;
536     }
537   }
538 
539   // Check if we call the 'invalidation' routine on the ivar.
540   if (Receiver) {
541     InvalidationMethod = MD;
542     check(Receiver->IgnoreParenCasts());
543     InvalidationMethod = 0;
544   }
545 
546   VisitStmt(ME);
547 }
548 }
549 
550 // Register the checker.
551 void ento::registerIvarInvalidationChecker(CheckerManager &mgr) {
552   mgr.registerChecker<IvarInvalidationChecker>();
553 }
554