1 //===- ObjCSuperDeallocChecker.cpp - Check correct use of [super dealloc] -===// 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 defines ObjCSuperDeallocChecker, a builtin check that warns when 11 // self is used after a call to [super dealloc] in MRR mode. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "ClangSACheckers.h" 16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17 #include "clang/StaticAnalyzer/Core/Checker.h" 18 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 22 23 using namespace clang; 24 using namespace ento; 25 26 namespace { 27 class ObjCSuperDeallocChecker 28 : public Checker<check::PostObjCMessage, check::PreObjCMessage, 29 check::PreCall, check::Location> { 30 31 mutable IdentifierInfo *IIdealloc, *IINSObject; 32 mutable Selector SELdealloc; 33 34 std::unique_ptr<BugType> DoubleSuperDeallocBugType; 35 36 void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; 37 38 bool isSuperDeallocMessage(const ObjCMethodCall &M) const; 39 40 public: 41 ObjCSuperDeallocChecker(); 42 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 43 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 44 45 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 46 47 void checkLocation(SVal l, bool isLoad, const Stmt *S, 48 CheckerContext &C) const; 49 50 private: 51 52 void diagnoseCallArguments(const CallEvent &CE, CheckerContext &C) const; 53 54 void reportUseAfterDealloc(SymbolRef Sym, StringRef Desc, const Stmt *S, 55 CheckerContext &C) const; 56 }; 57 58 } // End anonymous namespace. 59 60 // Remember whether [super dealloc] has previously been called on the 61 // SymbolRef for the receiver. 62 REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef) 63 64 namespace { 65 class SuperDeallocBRVisitor final 66 : public BugReporterVisitorImpl<SuperDeallocBRVisitor> { 67 68 SymbolRef ReceiverSymbol; 69 bool Satisfied; 70 71 public: 72 SuperDeallocBRVisitor(SymbolRef ReceiverSymbol) 73 : ReceiverSymbol(ReceiverSymbol), 74 Satisfied(false) {} 75 76 PathDiagnosticPiece *VisitNode(const ExplodedNode *Succ, 77 const ExplodedNode *Pred, 78 BugReporterContext &BRC, 79 BugReport &BR) override; 80 81 void Profile(llvm::FoldingSetNodeID &ID) const override { 82 ID.Add(ReceiverSymbol); 83 } 84 }; 85 } // End anonymous namespace. 86 87 void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M, 88 CheckerContext &C) const { 89 90 ProgramStateRef State = C.getState(); 91 SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol(); 92 if (!ReceiverSymbol) { 93 diagnoseCallArguments(M, C); 94 return; 95 } 96 97 bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol); 98 if (!AlreadyCalled) 99 return; 100 101 StringRef Desc; 102 103 if (isSuperDeallocMessage(M)) { 104 Desc = "[super dealloc] should not be called multiple times"; 105 } else { 106 Desc = StringRef(); 107 } 108 109 reportUseAfterDealloc(ReceiverSymbol, Desc, M.getOriginExpr(), C); 110 111 return; 112 } 113 114 void ObjCSuperDeallocChecker::checkPreCall(const CallEvent &Call, 115 CheckerContext &C) const { 116 diagnoseCallArguments(Call, C); 117 } 118 119 void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M, 120 CheckerContext &C) const { 121 // Check for [super dealloc] method call. 122 if (!isSuperDeallocMessage(M)) 123 return; 124 125 ProgramStateRef State = C.getState(); 126 SymbolRef ReceiverSymbol = M.getSelfSVal().getAsSymbol(); 127 assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?"); 128 129 // We add this transition in checkPostObjCMessage to avoid warning when 130 // we inline a call to [super dealloc] where the inlined call itself 131 // calls [super dealloc]. 132 State = State->add<CalledSuperDealloc>(ReceiverSymbol); 133 C.addTransition(State); 134 } 135 136 void ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S, 137 CheckerContext &C) const { 138 SymbolRef BaseSym = L.getLocSymbolInBase(); 139 if (!BaseSym) 140 return; 141 142 ProgramStateRef State = C.getState(); 143 144 if (!State->contains<CalledSuperDealloc>(BaseSym)) 145 return; 146 147 const MemRegion *R = L.getAsRegion(); 148 if (!R) 149 return; 150 151 // Climb the super regions to find the base symbol while recording 152 // the second-to-last region for error reporting. 153 const MemRegion *PriorSubRegion = nullptr; 154 while (const SubRegion *SR = dyn_cast<SubRegion>(R)) { 155 if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SR)) { 156 BaseSym = SymR->getSymbol(); 157 break; 158 } else { 159 R = SR->getSuperRegion(); 160 PriorSubRegion = SR; 161 } 162 } 163 164 StringRef Desc = StringRef(); 165 auto *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(PriorSubRegion); 166 167 std::string Buf; 168 llvm::raw_string_ostream OS(Buf); 169 if (IvarRegion) { 170 OS << "Use of instance variable '" << *IvarRegion->getDecl() << 171 "' after 'self' has been deallocated"; 172 Desc = OS.str(); 173 } 174 175 reportUseAfterDealloc(BaseSym, Desc, S, C); 176 } 177 178 /// Report a use-after-dealloc on Sym. If not empty, 179 /// Desc will be used to describe the error; otherwise, 180 /// a default warning will be used. 181 void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym, 182 StringRef Desc, 183 const Stmt *S, 184 CheckerContext &C) const { 185 // We have a use of self after free. 186 // This likely causes a crash, so stop exploring the 187 // path by generating a sink. 188 ExplodedNode *ErrNode = C.generateErrorNode(); 189 // If we've already reached this node on another path, return. 190 if (!ErrNode) 191 return; 192 193 if (Desc.empty()) 194 Desc = "use of 'self' after it has been deallocated"; 195 196 // Generate the report. 197 std::unique_ptr<BugReport> BR( 198 new BugReport(*DoubleSuperDeallocBugType, Desc, ErrNode)); 199 BR->addRange(S->getSourceRange()); 200 BR->addVisitor(llvm::make_unique<SuperDeallocBRVisitor>(Sym)); 201 C.emitReport(std::move(BR)); 202 } 203 204 /// Diagnose if any of the arguments to CE have already been 205 /// dealloc'd. 206 void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE, 207 CheckerContext &C) const { 208 ProgramStateRef State = C.getState(); 209 unsigned ArgCount = CE.getNumArgs(); 210 for (unsigned I = 0; I < ArgCount; I++) { 211 SymbolRef Sym = CE.getArgSVal(I).getAsSymbol(); 212 if (!Sym) 213 continue; 214 215 if (State->contains<CalledSuperDealloc>(Sym)) { 216 reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C); 217 return; 218 } 219 } 220 } 221 222 ObjCSuperDeallocChecker::ObjCSuperDeallocChecker() 223 : IIdealloc(nullptr), IINSObject(nullptr) { 224 225 DoubleSuperDeallocBugType.reset( 226 new BugType(this, "[super dealloc] should not be called more than once", 227 categories::CoreFoundationObjectiveC)); 228 } 229 230 void 231 ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const { 232 if (IIdealloc) 233 return; 234 235 IIdealloc = &Ctx.Idents.get("dealloc"); 236 IINSObject = &Ctx.Idents.get("NSObject"); 237 238 SELdealloc = Ctx.Selectors.getSelector(0, &IIdealloc); 239 } 240 241 bool 242 ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const { 243 if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance) 244 return false; 245 246 ASTContext &Ctx = M.getState()->getStateManager().getContext(); 247 initIdentifierInfoAndSelectors(Ctx); 248 249 return M.getSelector() == SELdealloc; 250 } 251 252 PathDiagnosticPiece *SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ, 253 const ExplodedNode *Pred, 254 BugReporterContext &BRC, 255 BugReport &BR) { 256 if (Satisfied) 257 return nullptr; 258 259 ProgramStateRef State = Succ->getState(); 260 261 bool CalledNow = 262 Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol); 263 bool CalledBefore = 264 Pred->getState()->contains<CalledSuperDealloc>(ReceiverSymbol); 265 266 // Is Succ the node on which the analyzer noted that [super dealloc] was 267 // called on ReceiverSymbol? 268 if (CalledNow && !CalledBefore) { 269 Satisfied = true; 270 271 ProgramPoint P = Succ->getLocation(); 272 PathDiagnosticLocation L = 273 PathDiagnosticLocation::create(P, BRC.getSourceManager()); 274 275 if (!L.isValid() || !L.asLocation().isValid()) 276 return nullptr; 277 278 return new PathDiagnosticEventPiece( 279 L, "[super dealloc] called here"); 280 } 281 282 return nullptr; 283 } 284 285 //===----------------------------------------------------------------------===// 286 // Checker Registration. 287 //===----------------------------------------------------------------------===// 288 289 void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) { 290 const LangOptions &LangOpts = Mgr.getLangOpts(); 291 if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount) 292 return; 293 Mgr.registerChecker<ObjCSuperDeallocChecker>(); 294 } 295