284 lines · cpp
1//===- ObjCSuperDeallocChecker.cpp - Check correct use of [super dealloc] -===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This defines ObjCSuperDeallocChecker, a builtin check that warns when10// self is used after a call to [super dealloc] in MRR mode.11//12//===----------------------------------------------------------------------===//13 14#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"15#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"16#include "clang/StaticAnalyzer/Core/Checker.h"17#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"18#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"19#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"20#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"21 22using namespace clang;23using namespace ento;24 25namespace {26class ObjCSuperDeallocChecker27 : public Checker<check::PostObjCMessage, check::PreObjCMessage,28 check::PreCall, check::Location> {29 mutable const IdentifierInfo *IIdealloc = nullptr;30 mutable const IdentifierInfo *IINSObject = nullptr;31 mutable Selector SELdealloc;32 33 const BugType DoubleSuperDeallocBugType{34 this, "[super dealloc] should not be called more than once",35 categories::CoreFoundationObjectiveC};36 37 void initIdentifierInfoAndSelectors(const ASTContext &Ctx) const;38 39 bool isSuperDeallocMessage(const ObjCMethodCall &M) const;40 41public: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 50private: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 the61// SymbolRef for the receiver.62REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef)63 64namespace {65class SuperDeallocBRVisitor final : public BugReporterVisitor {66 SymbolRef ReceiverSymbol;67 bool Satisfied;68 69public:70 SuperDeallocBRVisitor(SymbolRef ReceiverSymbol)71 : ReceiverSymbol(ReceiverSymbol), Satisfied(false) {}72 73 PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,74 BugReporterContext &BRC,75 PathSensitiveBugReport &BR) override;76 77 void Profile(llvm::FoldingSetNodeID &ID) const override {78 ID.Add(ReceiverSymbol);79 }80};81} // End anonymous namespace.82 83void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M,84 CheckerContext &C) const {85 86 ProgramStateRef State = C.getState();87 SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol();88 if (!ReceiverSymbol) {89 diagnoseCallArguments(M, C);90 return;91 }92 93 bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol);94 if (!AlreadyCalled)95 return;96 97 StringRef Desc;98 99 if (isSuperDeallocMessage(M)) {100 Desc = "[super dealloc] should not be called multiple times";101 } else {102 Desc = StringRef();103 }104 105 reportUseAfterDealloc(ReceiverSymbol, Desc, M.getOriginExpr(), C);106}107 108void ObjCSuperDeallocChecker::checkPreCall(const CallEvent &Call,109 CheckerContext &C) const {110 diagnoseCallArguments(Call, C);111}112 113void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M,114 CheckerContext &C) const {115 // Check for [super dealloc] method call.116 if (!isSuperDeallocMessage(M))117 return;118 119 ProgramStateRef State = C.getState();120 const LocationContext *LC = C.getLocationContext();121 SymbolRef SelfSymbol = State->getSelfSVal(LC).getAsSymbol();122 assert(SelfSymbol && "No receiver symbol at call to [super dealloc]?");123 124 // We add this transition in checkPostObjCMessage to avoid warning when125 // we inline a call to [super dealloc] where the inlined call itself126 // calls [super dealloc].127 State = State->add<CalledSuperDealloc>(SelfSymbol);128 C.addTransition(State);129}130 131void 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 recording147 // 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 = Buf;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.176void 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 the182 // 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 auto BR = std::make_unique<PathSensitiveBugReport>(DoubleSuperDeallocBugType,193 Desc, ErrNode);194 BR->addRange(S->getSourceRange());195 BR->addVisitor(std::make_unique<SuperDeallocBRVisitor>(Sym));196 C.emitReport(std::move(BR));197}198 199/// Diagnose if any of the arguments to CE have already been200/// dealloc'd.201void 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 217void ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(218 const ASTContext &Ctx) const {219 if (IIdealloc)220 return;221 222 IIdealloc = &Ctx.Idents.get("dealloc");223 IINSObject = &Ctx.Idents.get("NSObject");224 225 SELdealloc = Ctx.Selectors.getSelector(0, &IIdealloc);226}227 228bool229ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const {230 if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)231 return false;232 233 const ASTContext &Ctx = M.getASTContext();234 initIdentifierInfoAndSelectors(Ctx);235 236 return M.getSelector() == SELdealloc;237}238 239PathDiagnosticPieceRef240SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ,241 BugReporterContext &BRC,242 PathSensitiveBugReport &) {243 if (Satisfied)244 return nullptr;245 246 ProgramStateRef State = Succ->getState();247 248 bool CalledNow =249 Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol);250 bool CalledBefore =251 Succ->getFirstPred()->getState()->contains<CalledSuperDealloc>(252 ReceiverSymbol);253 254 // Is Succ the node on which the analyzer noted that [super dealloc] was255 // called on ReceiverSymbol?256 if (CalledNow && !CalledBefore) {257 Satisfied = true;258 259 ProgramPoint P = Succ->getLocation();260 PathDiagnosticLocation L =261 PathDiagnosticLocation::create(P, BRC.getSourceManager());262 263 if (!L.isValid() || !L.asLocation().isValid())264 return nullptr;265 266 return std::make_shared<PathDiagnosticEventPiece>(267 L, "[super dealloc] called here");268 }269 270 return nullptr;271}272 273//===----------------------------------------------------------------------===//274// Checker Registration.275//===----------------------------------------------------------------------===//276 277void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) {278 Mgr.registerChecker<ObjCSuperDeallocChecker>();279}280 281bool ento::shouldRegisterObjCSuperDeallocChecker(const CheckerManager &mgr) {282 return true;283}284