194742Sobrien//===- ObjCSuperDeallocChecker.cpp - Check correct use of [super dealloc] -===// 294742Sobrien// 3146890Speter// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4146890Speter// See https://llvm.org/LICENSE.txt for license information. 5179626Speter// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6146890Speter// 7146890Speter//===----------------------------------------------------------------------===// 8146890Speter// 9146890Speter// This defines ObjCSuperDeallocChecker, a builtin check that warns when 10146890Speter// self is used after a call to [super dealloc] in MRR mode. 11146890Speter// 12146890Speter//===----------------------------------------------------------------------===// 13146890Speter 14146890Speter#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15146890Speter#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 16146890Speter#include "clang/StaticAnalyzer/Core/Checker.h" 17146890Speter#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 18146890Speter#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19146890Speter#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 20146890Speter#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 2194742Sobrien 2295253Sruusing namespace clang; 23159014Sjmgusing namespace ento; 2494742Sobrien 2596991Srwatsonnamespace { 2696991Srwatsonclass ObjCSuperDeallocChecker 2796991Srwatson : public Checker<check::PostObjCMessage, check::PreObjCMessage, 28102773Srwatson check::PreCall, check::Location> { 29102773Srwatson 30156279Srwatson mutable IdentifierInfo *IIdealloc, *IINSObject; 31156279Srwatson mutable Selector SELdealloc; 3294917Simp 33126445Sobrien std::unique_ptr<BugType> DoubleSuperDeallocBugType; 3494917Simp 35146933Simp void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; 3694847Sjhb 3794847Sjhb bool isSuperDeallocMessage(const ObjCMethodCall &M) const; 3894847Sjhb 3994915Skenpublic: 4099607Smjacob ObjCSuperDeallocChecker(); 4194915Sken void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 4294915Sken void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 4394915Sken 4494915Sken void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 4594915Sken 4694915Sken void checkLocation(SVal l, bool isLoad, const Stmt *S, 4794915Sken CheckerContext &C) const; 4894915Sken 49169922Sjfvprivate: 5097611Sbillf 5194918Sgshapiro void diagnoseCallArguments(const CallEvent &CE, CheckerContext &C) const; 5294918Sgshapiro 5394918Sgshapiro void reportUseAfterDealloc(SymbolRef Sym, StringRef Desc, const Stmt *S, 5494918Sgshapiro CheckerContext &C) const; 5594918Sgshapiro}; 56106187Sdes 57106187Sdes} // End anonymous namespace. 5895455Sdes 5998750Sdes// Remember whether [super dealloc] has previously been called on the 60205686Sdes// SymbolRef for the receiver. 61205686SdesREGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef) 62205686Sdes 6396268Sgadnamespace { 6496268Sgadclass SuperDeallocBRVisitor final : public BugReporterVisitor { 65116233Sgad SymbolRef ReceiverSymbol; 6696332Speter bool Satisfied; 6796332Speter 6896332Speterpublic: 69100314Sru SuperDeallocBRVisitor(SymbolRef ReceiverSymbol) 70146921Sru : ReceiverSymbol(ReceiverSymbol), Satisfied(false) {} 71146921Sru 7297611Sbillf PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, 73190171Srnoland BugReporterContext &BRC, 74115825Sfanf PathSensitiveBugReport &BR) override; 75126445Sobrien 76117645Sdwmalone void Profile(llvm::FoldingSetNodeID &ID) const override { 77118204Sbp ID.Add(ReceiverSymbol); 78118204Sbp } 79118204Sbp}; 80118204Sbp} // End anonymous namespace. 81127337Smlaier 82126445Sobrienvoid ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M, 83146837Sobrien CheckerContext &C) const { 84146837Sobrien 85146837Sobrien ProgramStateRef State = C.getState(); 86146837Sobrien SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol(); 87133182Spjd if (!ReceiverSymbol) { 88148779Spjd diagnoseCallArguments(M, C); 89133182Spjd return; 90133182Spjd } 91133182Spjd 92133182Spjd bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol); 93133841Spjd if (!AlreadyCalled) 94143521Spjd return; 95133182Spjd 96148779Spjd StringRef Desc; 97133182Spjd 98168419Spjd if (isSuperDeallocMessage(M)) { 99132311Salfred Desc = "[super dealloc] should not be called multiple times"; 100132311Salfred } else { 101132311Salfred Desc = StringRef(); 102132268Salfred } 103158323Srodrigc 104158323Srodrigc reportUseAfterDealloc(ReceiverSymbol, Desc, M.getOriginExpr(), C); 105195405Sflz} 106146960Simp 107148772Scpercivavoid ObjCSuperDeallocChecker::checkPreCall(const CallEvent &Call, 108148871Scperciva CheckerContext &C) const { 109161748Scperciva diagnoseCallArguments(Call, C); 110162226Ssimon} 111149464Semax 112149464Semaxvoid ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M, 113149464Semax CheckerContext &C) const { 114149464Semax // Check for [super dealloc] method call. 115149464Semax if (!isSuperDeallocMessage(M)) 116151618Sceri return; 117179534Srafan 118200194Scperciva ProgramStateRef State = C.getState(); 119200194Scperciva SymbolRef ReceiverSymbol = M.getSelfSVal().getAsSymbol(); 120204061Sedwin assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?"); 121204061Sedwin 122204061Sedwin // We add this transition in checkPostObjCMessage to avoid warning when 123204061Sedwin // we inline a call to [super dealloc] where the inlined call itself 124204061Sedwin // calls [super dealloc]. 125204061Sedwin State = State->add<CalledSuperDealloc>(ReceiverSymbol); 126206396Scperciva C.addTransition(State); 127206396Scperciva} 128115822Sdougb 129115822Sdougbvoid ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S, 130115822Sdougb CheckerContext &C) const { 131115822Sdougb SymbolRef BaseSym = L.getLocSymbolInBase(); 132115822Sdougb if (!BaseSym) 133115822Sdougb return; 134115822Sdougb 135146629Sschweikh ProgramStateRef State = C.getState(); 136146629Sschweikh 137146629Sschweikh if (!State->contains<CalledSuperDealloc>(BaseSym)) 138115822Sdougb return; 139115822Sdougb 140146586Sschweikh const MemRegion *R = L.getAsRegion(); 141 if (!R) 142 return; 143 144 // Climb the super regions to find the base symbol while recording 145 // the second-to-last region for error reporting. 146 const MemRegion *PriorSubRegion = nullptr; 147 while (const SubRegion *SR = dyn_cast<SubRegion>(R)) { 148 if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SR)) { 149 BaseSym = SymR->getSymbol(); 150 break; 151 } else { 152 R = SR->getSuperRegion(); 153 PriorSubRegion = SR; 154 } 155 } 156 157 StringRef Desc = StringRef(); 158 auto *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(PriorSubRegion); 159 160 std::string Buf; 161 llvm::raw_string_ostream OS(Buf); 162 if (IvarRegion) { 163 OS << "Use of instance variable '" << *IvarRegion->getDecl() << 164 "' after 'self' has been deallocated"; 165 Desc = OS.str(); 166 } 167 168 reportUseAfterDealloc(BaseSym, Desc, S, C); 169} 170 171/// Report a use-after-dealloc on Sym. If not empty, 172/// Desc will be used to describe the error; otherwise, 173/// a default warning will be used. 174void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym, 175 StringRef Desc, 176 const Stmt *S, 177 CheckerContext &C) const { 178 // We have a use of self after free. 179 // This likely causes a crash, so stop exploring the 180 // path by generating a sink. 181 ExplodedNode *ErrNode = C.generateErrorNode(); 182 // If we've already reached this node on another path, return. 183 if (!ErrNode) 184 return; 185 186 if (Desc.empty()) 187 Desc = "Use of 'self' after it has been deallocated"; 188 189 // Generate the report. 190 auto BR = std::make_unique<PathSensitiveBugReport>(*DoubleSuperDeallocBugType, 191 Desc, ErrNode); 192 BR->addRange(S->getSourceRange()); 193 BR->addVisitor(std::make_unique<SuperDeallocBRVisitor>(Sym)); 194 C.emitReport(std::move(BR)); 195} 196 197/// Diagnose if any of the arguments to CE have already been 198/// dealloc'd. 199void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE, 200 CheckerContext &C) const { 201 ProgramStateRef State = C.getState(); 202 unsigned ArgCount = CE.getNumArgs(); 203 for (unsigned I = 0; I < ArgCount; I++) { 204 SymbolRef Sym = CE.getArgSVal(I).getAsSymbol(); 205 if (!Sym) 206 continue; 207 208 if (State->contains<CalledSuperDealloc>(Sym)) { 209 reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C); 210 return; 211 } 212 } 213} 214 215ObjCSuperDeallocChecker::ObjCSuperDeallocChecker() 216 : IIdealloc(nullptr), IINSObject(nullptr) { 217 218 DoubleSuperDeallocBugType.reset( 219 new BugType(this, "[super dealloc] should not be called more than once", 220 categories::CoreFoundationObjectiveC)); 221} 222 223void 224ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const { 225 if (IIdealloc) 226 return; 227 228 IIdealloc = &Ctx.Idents.get("dealloc"); 229 IINSObject = &Ctx.Idents.get("NSObject"); 230 231 SELdealloc = Ctx.Selectors.getSelector(0, &IIdealloc); 232} 233 234bool 235ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const { 236 if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance) 237 return false; 238 239 ASTContext &Ctx = M.getState()->getStateManager().getContext(); 240 initIdentifierInfoAndSelectors(Ctx); 241 242 return M.getSelector() == SELdealloc; 243} 244 245PathDiagnosticPieceRef 246SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ, 247 BugReporterContext &BRC, 248 PathSensitiveBugReport &) { 249 if (Satisfied) 250 return nullptr; 251 252 ProgramStateRef State = Succ->getState(); 253 254 bool CalledNow = 255 Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol); 256 bool CalledBefore = 257 Succ->getFirstPred()->getState()->contains<CalledSuperDealloc>( 258 ReceiverSymbol); 259 260 // Is Succ the node on which the analyzer noted that [super dealloc] was 261 // called on ReceiverSymbol? 262 if (CalledNow && !CalledBefore) { 263 Satisfied = true; 264 265 ProgramPoint P = Succ->getLocation(); 266 PathDiagnosticLocation L = 267 PathDiagnosticLocation::create(P, BRC.getSourceManager()); 268 269 if (!L.isValid() || !L.asLocation().isValid()) 270 return nullptr; 271 272 return std::make_shared<PathDiagnosticEventPiece>( 273 L, "[super dealloc] called here"); 274 } 275 276 return nullptr; 277} 278 279//===----------------------------------------------------------------------===// 280// Checker Registration. 281//===----------------------------------------------------------------------===// 282 283void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) { 284 Mgr.registerChecker<ObjCSuperDeallocChecker>(); 285} 286 287bool ento::shouldRegisterObjCSuperDeallocChecker(const LangOptions &LO) { 288 return true; 289} 290