Home | History | Annotate | Line # | Download | only in Checkers
      1      1.1  joerg //=- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ ----*-==//
      2      1.1  joerg //
      3      1.1  joerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4      1.1  joerg // See https://llvm.org/LICENSE.txt for license information.
      5      1.1  joerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6      1.1  joerg //
      7      1.1  joerg //===----------------------------------------------------------------------===//
      8      1.1  joerg //
      9      1.1  joerg //  Check that Objective C properties are set with the setter, not though a
     10      1.1  joerg //      direct assignment.
     11      1.1  joerg //
     12      1.1  joerg //  Two versions of a checker exist: one that checks all methods and the other
     13      1.1  joerg //      that only checks the methods annotated with
     14      1.1  joerg //      __attribute__((annotate("objc_no_direct_instance_variable_assignment")))
     15      1.1  joerg //
     16      1.1  joerg //  The checker does not warn about assignments to Ivars, annotated with
     17      1.1  joerg //       __attribute__((objc_allow_direct_instance_variable_assignment"))). This
     18      1.1  joerg //      annotation serves as a false positive suppression mechanism for the
     19      1.1  joerg //      checker. The annotation is allowed on properties and Ivars.
     20      1.1  joerg //
     21      1.1  joerg //===----------------------------------------------------------------------===//
     22      1.1  joerg 
     23      1.1  joerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
     24      1.1  joerg #include "clang/AST/Attr.h"
     25      1.1  joerg #include "clang/AST/DeclObjC.h"
     26      1.1  joerg #include "clang/AST/StmtVisitor.h"
     27      1.1  joerg #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
     28      1.1  joerg #include "clang/StaticAnalyzer/Core/Checker.h"
     29      1.1  joerg #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
     30      1.1  joerg #include "llvm/ADT/DenseMap.h"
     31      1.1  joerg 
     32      1.1  joerg using namespace clang;
     33      1.1  joerg using namespace ento;
     34      1.1  joerg 
     35      1.1  joerg namespace {
     36      1.1  joerg 
     37      1.1  joerg /// The default method filter, which is used to filter out the methods on which
     38      1.1  joerg /// the check should not be performed.
     39      1.1  joerg ///
     40      1.1  joerg /// Checks for the init, dealloc, and any other functions that might be allowed
     41      1.1  joerg /// to perform direct instance variable assignment based on their name.
     42      1.1  joerg static bool DefaultMethodFilter(const ObjCMethodDecl *M) {
     43      1.1  joerg   return M->getMethodFamily() == OMF_init ||
     44      1.1  joerg          M->getMethodFamily() == OMF_dealloc ||
     45      1.1  joerg          M->getMethodFamily() == OMF_copy ||
     46      1.1  joerg          M->getMethodFamily() == OMF_mutableCopy ||
     47      1.1  joerg          M->getSelector().getNameForSlot(0).find("init") != StringRef::npos ||
     48      1.1  joerg          M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos;
     49      1.1  joerg }
     50      1.1  joerg 
     51      1.1  joerg class DirectIvarAssignment :
     52      1.1  joerg   public Checker<check::ASTDecl<ObjCImplementationDecl> > {
     53      1.1  joerg 
     54      1.1  joerg   typedef llvm::DenseMap<const ObjCIvarDecl*,
     55      1.1  joerg                          const ObjCPropertyDecl*> IvarToPropertyMapTy;
     56      1.1  joerg 
     57      1.1  joerg   /// A helper class, which walks the AST and locates all assignments to ivars
     58      1.1  joerg   /// in the given function.
     59      1.1  joerg   class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
     60      1.1  joerg     const IvarToPropertyMapTy &IvarToPropMap;
     61      1.1  joerg     const ObjCMethodDecl *MD;
     62      1.1  joerg     const ObjCInterfaceDecl *InterfD;
     63      1.1  joerg     BugReporter &BR;
     64      1.1  joerg     const CheckerBase *Checker;
     65      1.1  joerg     LocationOrAnalysisDeclContext DCtx;
     66      1.1  joerg 
     67      1.1  joerg   public:
     68      1.1  joerg     MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD,
     69      1.1  joerg                   const ObjCInterfaceDecl *InID, BugReporter &InBR,
     70      1.1  joerg                   const CheckerBase *Checker, AnalysisDeclContext *InDCtx)
     71      1.1  joerg         : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR),
     72      1.1  joerg           Checker(Checker), DCtx(InDCtx) {}
     73      1.1  joerg 
     74      1.1  joerg     void VisitStmt(const Stmt *S) { VisitChildren(S); }
     75      1.1  joerg 
     76      1.1  joerg     void VisitBinaryOperator(const BinaryOperator *BO);
     77      1.1  joerg 
     78      1.1  joerg     void VisitChildren(const Stmt *S) {
     79      1.1  joerg       for (const Stmt *Child : S->children())
     80      1.1  joerg         if (Child)
     81      1.1  joerg           this->Visit(Child);
     82      1.1  joerg     }
     83      1.1  joerg   };
     84      1.1  joerg 
     85      1.1  joerg public:
     86      1.1  joerg   bool (*ShouldSkipMethod)(const ObjCMethodDecl *);
     87      1.1  joerg 
     88      1.1  joerg   DirectIvarAssignment() : ShouldSkipMethod(&DefaultMethodFilter) {}
     89      1.1  joerg 
     90      1.1  joerg   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
     91      1.1  joerg                     BugReporter &BR) const;
     92      1.1  joerg };
     93      1.1  joerg 
     94      1.1  joerg static const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl *PD,
     95      1.1  joerg                                                const ObjCInterfaceDecl *InterD,
     96      1.1  joerg                                                ASTContext &Ctx) {
     97      1.1  joerg   // Check for synthesized ivars.
     98      1.1  joerg   ObjCIvarDecl *ID = PD->getPropertyIvarDecl();
     99      1.1  joerg   if (ID)
    100      1.1  joerg     return ID;
    101      1.1  joerg 
    102      1.1  joerg   ObjCInterfaceDecl *NonConstInterD = const_cast<ObjCInterfaceDecl*>(InterD);
    103      1.1  joerg 
    104      1.1  joerg   // Check for existing "_PropName".
    105      1.1  joerg   ID = NonConstInterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx));
    106      1.1  joerg   if (ID)
    107      1.1  joerg     return ID;
    108      1.1  joerg 
    109      1.1  joerg   // Check for existing "PropName".
    110      1.1  joerg   IdentifierInfo *PropIdent = PD->getIdentifier();
    111      1.1  joerg   ID = NonConstInterD->lookupInstanceVariable(PropIdent);
    112      1.1  joerg 
    113      1.1  joerg   return ID;
    114      1.1  joerg }
    115      1.1  joerg 
    116      1.1  joerg void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D,
    117      1.1  joerg                                        AnalysisManager& Mgr,
    118      1.1  joerg                                        BugReporter &BR) const {
    119      1.1  joerg   const ObjCInterfaceDecl *InterD = D->getClassInterface();
    120      1.1  joerg 
    121      1.1  joerg 
    122      1.1  joerg   IvarToPropertyMapTy IvarToPropMap;
    123      1.1  joerg 
    124      1.1  joerg   // Find all properties for this class.
    125      1.1  joerg   for (const auto *PD : InterD->instance_properties()) {
    126      1.1  joerg     // Find the corresponding IVar.
    127      1.1  joerg     const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD,
    128      1.1  joerg                                                      Mgr.getASTContext());
    129      1.1  joerg 
    130      1.1  joerg     if (!ID)
    131      1.1  joerg       continue;
    132      1.1  joerg 
    133      1.1  joerg     // Store the IVar to property mapping.
    134      1.1  joerg     IvarToPropMap[ID] = PD;
    135      1.1  joerg   }
    136      1.1  joerg 
    137      1.1  joerg   if (IvarToPropMap.empty())
    138      1.1  joerg     return;
    139      1.1  joerg 
    140      1.1  joerg   for (const auto *M : D->instance_methods()) {
    141      1.1  joerg     AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);
    142      1.1  joerg 
    143      1.1  joerg     if ((*ShouldSkipMethod)(M))
    144      1.1  joerg       continue;
    145      1.1  joerg 
    146      1.1  joerg     const Stmt *Body = M->getBody();
    147  1.1.1.2  joerg     if (M->isSynthesizedAccessorStub())
    148  1.1.1.2  joerg       continue;
    149      1.1  joerg     assert(Body);
    150      1.1  joerg 
    151      1.1  joerg     MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this,
    152      1.1  joerg                      DCtx);
    153      1.1  joerg     MC.VisitStmt(Body);
    154      1.1  joerg   }
    155      1.1  joerg }
    156      1.1  joerg 
    157      1.1  joerg static bool isAnnotatedToAllowDirectAssignment(const Decl *D) {
    158      1.1  joerg   for (const auto *Ann : D->specific_attrs<AnnotateAttr>())
    159      1.1  joerg     if (Ann->getAnnotation() ==
    160      1.1  joerg         "objc_allow_direct_instance_variable_assignment")
    161      1.1  joerg       return true;
    162      1.1  joerg   return false;
    163      1.1  joerg }
    164      1.1  joerg 
    165      1.1  joerg void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator(
    166      1.1  joerg                                                     const BinaryOperator *BO) {
    167      1.1  joerg   if (!BO->isAssignmentOp())
    168      1.1  joerg     return;
    169      1.1  joerg 
    170      1.1  joerg   const ObjCIvarRefExpr *IvarRef =
    171      1.1  joerg           dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts());
    172      1.1  joerg 
    173      1.1  joerg   if (!IvarRef)
    174      1.1  joerg     return;
    175      1.1  joerg 
    176      1.1  joerg   if (const ObjCIvarDecl *D = IvarRef->getDecl()) {
    177      1.1  joerg     IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D);
    178      1.1  joerg 
    179      1.1  joerg     if (I != IvarToPropMap.end()) {
    180      1.1  joerg       const ObjCPropertyDecl *PD = I->second;
    181      1.1  joerg       // Skip warnings on Ivars, annotated with
    182      1.1  joerg       // objc_allow_direct_instance_variable_assignment. This annotation serves
    183      1.1  joerg       // as a false positive suppression mechanism for the checker. The
    184      1.1  joerg       // annotation is allowed on properties and ivars.
    185      1.1  joerg       if (isAnnotatedToAllowDirectAssignment(PD) ||
    186      1.1  joerg           isAnnotatedToAllowDirectAssignment(D))
    187      1.1  joerg         return;
    188      1.1  joerg 
    189      1.1  joerg       ObjCMethodDecl *GetterMethod =
    190      1.1  joerg           InterfD->getInstanceMethod(PD->getGetterName());
    191      1.1  joerg       ObjCMethodDecl *SetterMethod =
    192      1.1  joerg           InterfD->getInstanceMethod(PD->getSetterName());
    193      1.1  joerg 
    194      1.1  joerg       if (SetterMethod && SetterMethod->getCanonicalDecl() == MD)
    195      1.1  joerg         return;
    196      1.1  joerg 
    197      1.1  joerg       if (GetterMethod && GetterMethod->getCanonicalDecl() == MD)
    198      1.1  joerg         return;
    199      1.1  joerg 
    200      1.1  joerg       BR.EmitBasicReport(
    201      1.1  joerg           MD, Checker, "Property access", categories::CoreFoundationObjectiveC,
    202      1.1  joerg           "Direct assignment to an instance variable backing a property; "
    203      1.1  joerg           "use the setter instead",
    204      1.1  joerg           PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx));
    205      1.1  joerg     }
    206      1.1  joerg   }
    207      1.1  joerg }
    208      1.1  joerg }
    209      1.1  joerg 
    210      1.1  joerg // Register the checker that checks for direct accesses in functions annotated
    211      1.1  joerg // with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))).
    212      1.1  joerg static bool AttrFilter(const ObjCMethodDecl *M) {
    213      1.1  joerg   for (const auto *Ann : M->specific_attrs<AnnotateAttr>())
    214      1.1  joerg     if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment")
    215      1.1  joerg       return false;
    216      1.1  joerg   return true;
    217      1.1  joerg }
    218      1.1  joerg 
    219      1.1  joerg // Register the checker that checks for direct accesses in all functions,
    220      1.1  joerg // except for the initialization and copy routines.
    221      1.1  joerg void ento::registerDirectIvarAssignment(CheckerManager &mgr) {
    222  1.1.1.2  joerg   auto Chk = mgr.registerChecker<DirectIvarAssignment>();
    223  1.1.1.2  joerg   if (mgr.getAnalyzerOptions().getCheckerBooleanOption(Chk,
    224  1.1.1.2  joerg                                                        "AnnotatedFunctions"))
    225  1.1.1.2  joerg     Chk->ShouldSkipMethod = &AttrFilter;
    226      1.1  joerg }
    227      1.1  joerg 
    228  1.1.1.2  joerg bool ento::shouldRegisterDirectIvarAssignment(const CheckerManager &mgr) {
    229      1.1  joerg   return true;
    230      1.1  joerg }
    231