xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
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