xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp (revision ec727ea710c91afd8ce4f788c5aaa8482b7b69b2)
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