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