1 //==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- 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 file defines a CheckObjCDealloc, a checker that 11 // analyzes an Objective-C class's implementation to determine if it 12 // correctly implements -dealloc. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "ClangSACheckers.h" 17 #include "clang/AST/Attr.h" 18 #include "clang/AST/DeclObjC.h" 19 #include "clang/AST/Expr.h" 20 #include "clang/AST/ExprObjC.h" 21 #include "clang/Basic/LangOptions.h" 22 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 23 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 24 #include "clang/StaticAnalyzer/Core/Checker.h" 25 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 26 #include "llvm/Support/raw_ostream.h" 27 28 using namespace clang; 29 using namespace ento; 30 31 // FIXME: This was taken from IvarInvalidationChecker.cpp 32 static const Expr *peel(const Expr *E) { 33 E = E->IgnoreParenCasts(); 34 if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) 35 E = POE->getSyntacticForm()->IgnoreParenCasts(); 36 if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) 37 E = OVE->getSourceExpr()->IgnoreParenCasts(); 38 return E; 39 } 40 41 static bool scan_ivar_release(Stmt *S, const ObjCIvarDecl *ID, 42 const ObjCPropertyDecl *PD, 43 Selector Release, 44 IdentifierInfo* SelfII, 45 ASTContext &Ctx) { 46 47 // [mMyIvar release] 48 if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) 49 if (ME->getSelector() == Release) 50 if (ME->getInstanceReceiver()) 51 if (const Expr *Receiver = peel(ME->getInstanceReceiver())) 52 if (auto *E = dyn_cast<ObjCIvarRefExpr>(Receiver)) 53 if (E->getDecl() == ID) 54 return true; 55 56 // [self setMyIvar:nil]; 57 if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) 58 if (ME->getInstanceReceiver()) 59 if (const Expr *Receiver = peel(ME->getInstanceReceiver())) 60 if (auto *E = dyn_cast<DeclRefExpr>(Receiver)) 61 if (E->getDecl()->getIdentifier() == SelfII) 62 if (ME->getMethodDecl() == PD->getSetterMethodDecl() && 63 ME->getNumArgs() == 1 && 64 peel(ME->getArg(0))->isNullPointerConstant(Ctx, 65 Expr::NPC_ValueDependentIsNull)) 66 return true; 67 68 // self.myIvar = nil; 69 if (BinaryOperator* BO = dyn_cast<BinaryOperator>(S)) 70 if (BO->isAssignmentOp()) 71 if (auto *PRE = dyn_cast<ObjCPropertyRefExpr>(peel(BO->getLHS()))) 72 if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD) 73 if (peel(BO->getRHS())->isNullPointerConstant(Ctx, 74 Expr::NPC_ValueDependentIsNull)) { 75 // This is only a 'release' if the property kind is not 76 // 'assign'. 77 return PD->getSetterKind() != ObjCPropertyDecl::Assign; 78 } 79 80 // Recurse to children. 81 for (Stmt *SubStmt : S->children()) 82 if (SubStmt && scan_ivar_release(SubStmt, ID, PD, Release, SelfII, Ctx)) 83 return true; 84 85 return false; 86 } 87 88 static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, 89 const ObjCIvarDecl **ID, 90 const ObjCPropertyDecl **PD) { 91 92 if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) 93 return false; 94 95 (*ID) = I->getPropertyIvarDecl(); 96 if (!(*ID)) 97 return false; 98 99 QualType T = (*ID)->getType(); 100 if (!T->isObjCRetainableType()) 101 return false; 102 103 (*PD) = I->getPropertyDecl(); 104 // Shouldn't be able to synthesize a property that doesn't exist. 105 assert(*PD); 106 107 return true; 108 } 109 110 static bool synthesizedPropertyRequiresRelease(const ObjCPropertyDecl *PD) { 111 // A synthesized property must be released if and only if the kind of setter 112 // was neither 'assign' or 'weak'. 113 ObjCPropertyDecl::SetterKind SK = PD->getSetterKind(); 114 return (SK != ObjCPropertyDecl::Assign && SK != ObjCPropertyDecl::Weak); 115 } 116 117 static void checkObjCDealloc(const CheckerBase *Checker, 118 const ObjCImplementationDecl *D, 119 const LangOptions &LOpts, BugReporter &BR) { 120 121 assert(LOpts.getGC() != LangOptions::GCOnly); 122 assert(!LOpts.ObjCAutoRefCount); 123 124 ASTContext &Ctx = BR.getContext(); 125 const ObjCInterfaceDecl *ID = D->getClassInterface(); 126 127 // Does the class contain any synthesized properties that are retainable? 128 // If not, skip the check entirely. 129 bool containsRetainedSynthesizedProperty = false; 130 for (const auto *I : D->property_impls()) { 131 const ObjCIvarDecl *ID = nullptr; 132 const ObjCPropertyDecl *PD = nullptr; 133 if (!isSynthesizedRetainableProperty(I, &ID, &PD)) 134 continue; 135 136 if (synthesizedPropertyRequiresRelease(PD)) { 137 containsRetainedSynthesizedProperty = true; 138 break; 139 } 140 } 141 142 if (!containsRetainedSynthesizedProperty) 143 return; 144 145 // Determine if the class subclasses NSObject. 146 IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); 147 IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); 148 149 for ( ; ID ; ID = ID->getSuperClass()) { 150 IdentifierInfo *II = ID->getIdentifier(); 151 152 if (II == NSObjectII) 153 break; 154 155 // FIXME: For now, ignore classes that subclass SenTestCase, as these don't 156 // need to implement -dealloc. They implement tear down in another way, 157 // which we should try and catch later. 158 // http://llvm.org/bugs/show_bug.cgi?id=3187 159 if (II == SenTestCaseII) 160 return; 161 } 162 163 if (!ID) 164 return; 165 166 // Get the "dealloc" selector. 167 IdentifierInfo* II = &Ctx.Idents.get("dealloc"); 168 Selector S = Ctx.Selectors.getSelector(0, &II); 169 const ObjCMethodDecl *MD = nullptr; 170 171 // Scan the instance methods for "dealloc". 172 for (const auto *I : D->instance_methods()) { 173 if (I->getSelector() == S) { 174 MD = I; 175 break; 176 } 177 } 178 179 if (!MD) { // No dealloc found. 180 181 const char* name = LOpts.getGC() == LangOptions::NonGC 182 ? "missing -dealloc" 183 : "missing -dealloc (Hybrid MM, non-GC)"; 184 185 std::string buf; 186 llvm::raw_string_ostream os(buf); 187 os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; 188 189 PathDiagnosticLocation DLoc = 190 PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); 191 192 BR.EmitBasicReport(D, Checker, name, categories::CoreFoundationObjectiveC, 193 os.str(), DLoc); 194 return; 195 } 196 197 // Get the "release" selector. 198 IdentifierInfo* RII = &Ctx.Idents.get("release"); 199 Selector RS = Ctx.Selectors.getSelector(0, &RII); 200 201 // Get the "self" identifier 202 IdentifierInfo* SelfII = &Ctx.Idents.get("self"); 203 204 // Scan for missing and extra releases of ivars used by implementations 205 // of synthesized properties 206 for (const auto *I : D->property_impls()) { 207 const ObjCIvarDecl *ID = nullptr; 208 const ObjCPropertyDecl *PD = nullptr; 209 if (!isSynthesizedRetainableProperty(I, &ID, &PD)) 210 continue; 211 212 bool requiresRelease = synthesizedPropertyRequiresRelease(PD); 213 if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) 214 != requiresRelease) { 215 const char *name = nullptr; 216 std::string buf; 217 llvm::raw_string_ostream os(buf); 218 219 if (requiresRelease) { 220 name = LOpts.getGC() == LangOptions::NonGC 221 ? "missing ivar release (leak)" 222 : "missing ivar release (Hybrid MM, non-GC)"; 223 224 os << "The '" << *ID << "' instance variable in '" << *D 225 << "' was retained by a synthesized property " 226 "but was not released in 'dealloc'"; 227 } else { 228 // It is common for the ivars for read-only assign properties to 229 // always be stored retained, so don't warn for a release in 230 // dealloc for the ivar backing these properties. 231 if (PD->isReadOnly()) 232 continue; 233 234 name = LOpts.getGC() == LangOptions::NonGC 235 ? "extra ivar release (use-after-release)" 236 : "extra ivar release (Hybrid MM, non-GC)"; 237 238 os << "The '" << *ID << "' instance variable in '" << *D 239 << "' was not retained by a synthesized property " 240 "but was released in 'dealloc'"; 241 } 242 243 // If @synthesize statement is missing, fall back to @property statement. 244 const Decl *SPDecl = I->getLocation().isValid() 245 ? static_cast<const Decl *>(I) 246 : static_cast<const Decl *>(PD); 247 PathDiagnosticLocation SPLoc = 248 PathDiagnosticLocation::createBegin(SPDecl, BR.getSourceManager()); 249 250 BR.EmitBasicReport(MD, Checker, name, 251 categories::CoreFoundationObjectiveC, os.str(), SPLoc); 252 } 253 } 254 } 255 256 //===----------------------------------------------------------------------===// 257 // ObjCDeallocChecker 258 //===----------------------------------------------------------------------===// 259 260 namespace { 261 class ObjCDeallocChecker : public Checker< 262 check::ASTDecl<ObjCImplementationDecl> > { 263 public: 264 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, 265 BugReporter &BR) const { 266 if (mgr.getLangOpts().getGC() == LangOptions::GCOnly || 267 mgr.getLangOpts().ObjCAutoRefCount) 268 return; 269 checkObjCDealloc(this, cast<ObjCImplementationDecl>(D), mgr.getLangOpts(), 270 BR); 271 } 272 }; 273 } 274 275 void ento::registerObjCDeallocChecker(CheckerManager &mgr) { 276 mgr.registerChecker<ObjCDeallocChecker>(); 277 } 278