DirectIvarAssignment.cpp 8.0 KB

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