10b57cec5SDimitry Andric //===- ObjCSuperDeallocChecker.cpp - Check correct use of [super dealloc] -===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This defines ObjCSuperDeallocChecker, a builtin check that warns when 100b57cec5SDimitry Andric // self is used after a call to [super dealloc] in MRR mode. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 210b57cec5SDimitry Andric 220b57cec5SDimitry Andric using namespace clang; 230b57cec5SDimitry Andric using namespace ento; 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric namespace { 260b57cec5SDimitry Andric class ObjCSuperDeallocChecker 270b57cec5SDimitry Andric : public Checker<check::PostObjCMessage, check::PreObjCMessage, 280b57cec5SDimitry Andric check::PreCall, check::Location> { 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric mutable IdentifierInfo *IIdealloc, *IINSObject; 310b57cec5SDimitry Andric mutable Selector SELdealloc; 320b57cec5SDimitry Andric 330b57cec5SDimitry Andric std::unique_ptr<BugType> DoubleSuperDeallocBugType; 340b57cec5SDimitry Andric 350b57cec5SDimitry Andric void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; 360b57cec5SDimitry Andric 370b57cec5SDimitry Andric bool isSuperDeallocMessage(const ObjCMethodCall &M) const; 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric public: 400b57cec5SDimitry Andric ObjCSuperDeallocChecker(); 410b57cec5SDimitry Andric void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 420b57cec5SDimitry Andric void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 430b57cec5SDimitry Andric 440b57cec5SDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric void checkLocation(SVal l, bool isLoad, const Stmt *S, 470b57cec5SDimitry Andric CheckerContext &C) const; 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric private: 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric void diagnoseCallArguments(const CallEvent &CE, CheckerContext &C) const; 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric void reportUseAfterDealloc(SymbolRef Sym, StringRef Desc, const Stmt *S, 540b57cec5SDimitry Andric CheckerContext &C) const; 550b57cec5SDimitry Andric }; 560b57cec5SDimitry Andric 570b57cec5SDimitry Andric } // End anonymous namespace. 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric // Remember whether [super dealloc] has previously been called on the 600b57cec5SDimitry Andric // SymbolRef for the receiver. 610b57cec5SDimitry Andric REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef) 620b57cec5SDimitry Andric 630b57cec5SDimitry Andric namespace { 640b57cec5SDimitry Andric class SuperDeallocBRVisitor final : public BugReporterVisitor { 650b57cec5SDimitry Andric SymbolRef ReceiverSymbol; 660b57cec5SDimitry Andric bool Satisfied; 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric public: 690b57cec5SDimitry Andric SuperDeallocBRVisitor(SymbolRef ReceiverSymbol) 70a7dea167SDimitry Andric : ReceiverSymbol(ReceiverSymbol), Satisfied(false) {} 710b57cec5SDimitry Andric 72a7dea167SDimitry Andric PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, 730b57cec5SDimitry Andric BugReporterContext &BRC, 74a7dea167SDimitry Andric PathSensitiveBugReport &BR) override; 750b57cec5SDimitry Andric 760b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const override { 770b57cec5SDimitry Andric ID.Add(ReceiverSymbol); 780b57cec5SDimitry Andric } 790b57cec5SDimitry Andric }; 800b57cec5SDimitry Andric } // End anonymous namespace. 810b57cec5SDimitry Andric 820b57cec5SDimitry Andric void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M, 830b57cec5SDimitry Andric CheckerContext &C) const { 840b57cec5SDimitry Andric 850b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 860b57cec5SDimitry Andric SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol(); 870b57cec5SDimitry Andric if (!ReceiverSymbol) { 880b57cec5SDimitry Andric diagnoseCallArguments(M, C); 890b57cec5SDimitry Andric return; 900b57cec5SDimitry Andric } 910b57cec5SDimitry Andric 920b57cec5SDimitry Andric bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol); 930b57cec5SDimitry Andric if (!AlreadyCalled) 940b57cec5SDimitry Andric return; 950b57cec5SDimitry Andric 960b57cec5SDimitry Andric StringRef Desc; 970b57cec5SDimitry Andric 980b57cec5SDimitry Andric if (isSuperDeallocMessage(M)) { 990b57cec5SDimitry Andric Desc = "[super dealloc] should not be called multiple times"; 1000b57cec5SDimitry Andric } else { 1010b57cec5SDimitry Andric Desc = StringRef(); 1020b57cec5SDimitry Andric } 1030b57cec5SDimitry Andric 1040b57cec5SDimitry Andric reportUseAfterDealloc(ReceiverSymbol, Desc, M.getOriginExpr(), C); 1050b57cec5SDimitry Andric } 1060b57cec5SDimitry Andric 1070b57cec5SDimitry Andric void ObjCSuperDeallocChecker::checkPreCall(const CallEvent &Call, 1080b57cec5SDimitry Andric CheckerContext &C) const { 1090b57cec5SDimitry Andric diagnoseCallArguments(Call, C); 1100b57cec5SDimitry Andric } 1110b57cec5SDimitry Andric 1120b57cec5SDimitry Andric void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M, 1130b57cec5SDimitry Andric CheckerContext &C) const { 1140b57cec5SDimitry Andric // Check for [super dealloc] method call. 1150b57cec5SDimitry Andric if (!isSuperDeallocMessage(M)) 1160b57cec5SDimitry Andric return; 1170b57cec5SDimitry Andric 1180b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 119*5ffd83dbSDimitry Andric const LocationContext *LC = C.getLocationContext(); 120*5ffd83dbSDimitry Andric SymbolRef SelfSymbol = State->getSelfSVal(LC).getAsSymbol(); 121*5ffd83dbSDimitry Andric assert(SelfSymbol && "No receiver symbol at call to [super dealloc]?"); 1220b57cec5SDimitry Andric 1230b57cec5SDimitry Andric // We add this transition in checkPostObjCMessage to avoid warning when 1240b57cec5SDimitry Andric // we inline a call to [super dealloc] where the inlined call itself 1250b57cec5SDimitry Andric // calls [super dealloc]. 126*5ffd83dbSDimitry Andric State = State->add<CalledSuperDealloc>(SelfSymbol); 1270b57cec5SDimitry Andric C.addTransition(State); 1280b57cec5SDimitry Andric } 1290b57cec5SDimitry Andric 1300b57cec5SDimitry Andric void ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S, 1310b57cec5SDimitry Andric CheckerContext &C) const { 1320b57cec5SDimitry Andric SymbolRef BaseSym = L.getLocSymbolInBase(); 1330b57cec5SDimitry Andric if (!BaseSym) 1340b57cec5SDimitry Andric return; 1350b57cec5SDimitry Andric 1360b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 1370b57cec5SDimitry Andric 1380b57cec5SDimitry Andric if (!State->contains<CalledSuperDealloc>(BaseSym)) 1390b57cec5SDimitry Andric return; 1400b57cec5SDimitry Andric 1410b57cec5SDimitry Andric const MemRegion *R = L.getAsRegion(); 1420b57cec5SDimitry Andric if (!R) 1430b57cec5SDimitry Andric return; 1440b57cec5SDimitry Andric 1450b57cec5SDimitry Andric // Climb the super regions to find the base symbol while recording 1460b57cec5SDimitry Andric // the second-to-last region for error reporting. 1470b57cec5SDimitry Andric const MemRegion *PriorSubRegion = nullptr; 1480b57cec5SDimitry Andric while (const SubRegion *SR = dyn_cast<SubRegion>(R)) { 1490b57cec5SDimitry Andric if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SR)) { 1500b57cec5SDimitry Andric BaseSym = SymR->getSymbol(); 1510b57cec5SDimitry Andric break; 1520b57cec5SDimitry Andric } else { 1530b57cec5SDimitry Andric R = SR->getSuperRegion(); 1540b57cec5SDimitry Andric PriorSubRegion = SR; 1550b57cec5SDimitry Andric } 1560b57cec5SDimitry Andric } 1570b57cec5SDimitry Andric 1580b57cec5SDimitry Andric StringRef Desc = StringRef(); 1590b57cec5SDimitry Andric auto *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(PriorSubRegion); 1600b57cec5SDimitry Andric 1610b57cec5SDimitry Andric std::string Buf; 1620b57cec5SDimitry Andric llvm::raw_string_ostream OS(Buf); 1630b57cec5SDimitry Andric if (IvarRegion) { 1640b57cec5SDimitry Andric OS << "Use of instance variable '" << *IvarRegion->getDecl() << 1650b57cec5SDimitry Andric "' after 'self' has been deallocated"; 1660b57cec5SDimitry Andric Desc = OS.str(); 1670b57cec5SDimitry Andric } 1680b57cec5SDimitry Andric 1690b57cec5SDimitry Andric reportUseAfterDealloc(BaseSym, Desc, S, C); 1700b57cec5SDimitry Andric } 1710b57cec5SDimitry Andric 1720b57cec5SDimitry Andric /// Report a use-after-dealloc on Sym. If not empty, 1730b57cec5SDimitry Andric /// Desc will be used to describe the error; otherwise, 1740b57cec5SDimitry Andric /// a default warning will be used. 1750b57cec5SDimitry Andric void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym, 1760b57cec5SDimitry Andric StringRef Desc, 1770b57cec5SDimitry Andric const Stmt *S, 1780b57cec5SDimitry Andric CheckerContext &C) const { 1790b57cec5SDimitry Andric // We have a use of self after free. 1800b57cec5SDimitry Andric // This likely causes a crash, so stop exploring the 1810b57cec5SDimitry Andric // path by generating a sink. 1820b57cec5SDimitry Andric ExplodedNode *ErrNode = C.generateErrorNode(); 1830b57cec5SDimitry Andric // If we've already reached this node on another path, return. 1840b57cec5SDimitry Andric if (!ErrNode) 1850b57cec5SDimitry Andric return; 1860b57cec5SDimitry Andric 1870b57cec5SDimitry Andric if (Desc.empty()) 1880b57cec5SDimitry Andric Desc = "Use of 'self' after it has been deallocated"; 1890b57cec5SDimitry Andric 1900b57cec5SDimitry Andric // Generate the report. 191a7dea167SDimitry Andric auto BR = std::make_unique<PathSensitiveBugReport>(*DoubleSuperDeallocBugType, 192a7dea167SDimitry Andric Desc, ErrNode); 1930b57cec5SDimitry Andric BR->addRange(S->getSourceRange()); 194a7dea167SDimitry Andric BR->addVisitor(std::make_unique<SuperDeallocBRVisitor>(Sym)); 1950b57cec5SDimitry Andric C.emitReport(std::move(BR)); 1960b57cec5SDimitry Andric } 1970b57cec5SDimitry Andric 1980b57cec5SDimitry Andric /// Diagnose if any of the arguments to CE have already been 1990b57cec5SDimitry Andric /// dealloc'd. 2000b57cec5SDimitry Andric void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE, 2010b57cec5SDimitry Andric CheckerContext &C) const { 2020b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 2030b57cec5SDimitry Andric unsigned ArgCount = CE.getNumArgs(); 2040b57cec5SDimitry Andric for (unsigned I = 0; I < ArgCount; I++) { 2050b57cec5SDimitry Andric SymbolRef Sym = CE.getArgSVal(I).getAsSymbol(); 2060b57cec5SDimitry Andric if (!Sym) 2070b57cec5SDimitry Andric continue; 2080b57cec5SDimitry Andric 2090b57cec5SDimitry Andric if (State->contains<CalledSuperDealloc>(Sym)) { 2100b57cec5SDimitry Andric reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C); 2110b57cec5SDimitry Andric return; 2120b57cec5SDimitry Andric } 2130b57cec5SDimitry Andric } 2140b57cec5SDimitry Andric } 2150b57cec5SDimitry Andric 2160b57cec5SDimitry Andric ObjCSuperDeallocChecker::ObjCSuperDeallocChecker() 2170b57cec5SDimitry Andric : IIdealloc(nullptr), IINSObject(nullptr) { 2180b57cec5SDimitry Andric 2190b57cec5SDimitry Andric DoubleSuperDeallocBugType.reset( 2200b57cec5SDimitry Andric new BugType(this, "[super dealloc] should not be called more than once", 2210b57cec5SDimitry Andric categories::CoreFoundationObjectiveC)); 2220b57cec5SDimitry Andric } 2230b57cec5SDimitry Andric 2240b57cec5SDimitry Andric void 2250b57cec5SDimitry Andric ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const { 2260b57cec5SDimitry Andric if (IIdealloc) 2270b57cec5SDimitry Andric return; 2280b57cec5SDimitry Andric 2290b57cec5SDimitry Andric IIdealloc = &Ctx.Idents.get("dealloc"); 2300b57cec5SDimitry Andric IINSObject = &Ctx.Idents.get("NSObject"); 2310b57cec5SDimitry Andric 2320b57cec5SDimitry Andric SELdealloc = Ctx.Selectors.getSelector(0, &IIdealloc); 2330b57cec5SDimitry Andric } 2340b57cec5SDimitry Andric 2350b57cec5SDimitry Andric bool 2360b57cec5SDimitry Andric ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const { 2370b57cec5SDimitry Andric if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance) 2380b57cec5SDimitry Andric return false; 2390b57cec5SDimitry Andric 2400b57cec5SDimitry Andric ASTContext &Ctx = M.getState()->getStateManager().getContext(); 2410b57cec5SDimitry Andric initIdentifierInfoAndSelectors(Ctx); 2420b57cec5SDimitry Andric 2430b57cec5SDimitry Andric return M.getSelector() == SELdealloc; 2440b57cec5SDimitry Andric } 2450b57cec5SDimitry Andric 246a7dea167SDimitry Andric PathDiagnosticPieceRef 2470b57cec5SDimitry Andric SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ, 248a7dea167SDimitry Andric BugReporterContext &BRC, 249a7dea167SDimitry Andric PathSensitiveBugReport &) { 2500b57cec5SDimitry Andric if (Satisfied) 2510b57cec5SDimitry Andric return nullptr; 2520b57cec5SDimitry Andric 2530b57cec5SDimitry Andric ProgramStateRef State = Succ->getState(); 2540b57cec5SDimitry Andric 2550b57cec5SDimitry Andric bool CalledNow = 2560b57cec5SDimitry Andric Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol); 2570b57cec5SDimitry Andric bool CalledBefore = 2580b57cec5SDimitry Andric Succ->getFirstPred()->getState()->contains<CalledSuperDealloc>( 2590b57cec5SDimitry Andric ReceiverSymbol); 2600b57cec5SDimitry Andric 2610b57cec5SDimitry Andric // Is Succ the node on which the analyzer noted that [super dealloc] was 2620b57cec5SDimitry Andric // called on ReceiverSymbol? 2630b57cec5SDimitry Andric if (CalledNow && !CalledBefore) { 2640b57cec5SDimitry Andric Satisfied = true; 2650b57cec5SDimitry Andric 2660b57cec5SDimitry Andric ProgramPoint P = Succ->getLocation(); 2670b57cec5SDimitry Andric PathDiagnosticLocation L = 2680b57cec5SDimitry Andric PathDiagnosticLocation::create(P, BRC.getSourceManager()); 2690b57cec5SDimitry Andric 2700b57cec5SDimitry Andric if (!L.isValid() || !L.asLocation().isValid()) 2710b57cec5SDimitry Andric return nullptr; 2720b57cec5SDimitry Andric 2730b57cec5SDimitry Andric return std::make_shared<PathDiagnosticEventPiece>( 2740b57cec5SDimitry Andric L, "[super dealloc] called here"); 2750b57cec5SDimitry Andric } 2760b57cec5SDimitry Andric 2770b57cec5SDimitry Andric return nullptr; 2780b57cec5SDimitry Andric } 2790b57cec5SDimitry Andric 2800b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 2810b57cec5SDimitry Andric // Checker Registration. 2820b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 2830b57cec5SDimitry Andric 2840b57cec5SDimitry Andric void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) { 2850b57cec5SDimitry Andric Mgr.registerChecker<ObjCSuperDeallocChecker>(); 2860b57cec5SDimitry Andric } 2870b57cec5SDimitry Andric 288*5ffd83dbSDimitry Andric bool ento::shouldRegisterObjCSuperDeallocChecker(const CheckerManager &mgr) { 2890b57cec5SDimitry Andric return true; 2900b57cec5SDimitry Andric } 291