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