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