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