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