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