/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //== ObjCSelfInitChecker.cpp - Checker for 'self' initialization -*- C++ -*--=// |
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 ObjCSelfInitChecker, a builtin check that checks for uses of |
10 | | // 'self' before proper initialization. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | // This checks initialization methods to verify that they assign 'self' to the |
15 | | // result of an initialization call (e.g. [super init], or [self initWith..]) |
16 | | // before using 'self' or any instance variable. |
17 | | // |
18 | | // To perform the required checking, values are tagged with flags that indicate |
19 | | // 1) if the object is the one pointed to by 'self', and 2) if the object |
20 | | // is the result of an initializer (e.g. [super init]). |
21 | | // |
22 | | // Uses of an object that is true for 1) but not 2) trigger a diagnostic. |
23 | | // The uses that are currently checked are: |
24 | | // - Using instance variables. |
25 | | // - Returning the object. |
26 | | // |
27 | | // Note that we don't check for an invalid 'self' that is the receiver of an |
28 | | // obj-c message expression to cut down false positives where logging functions |
29 | | // get information from self (like its class) or doing "invalidation" on self |
30 | | // when the initialization fails. |
31 | | // |
32 | | // Because the object that 'self' points to gets invalidated when a call |
33 | | // receives a reference to 'self', the checker keeps track and passes the flags |
34 | | // for 1) and 2) to the new object that 'self' points to after the call. |
35 | | // |
36 | | //===----------------------------------------------------------------------===// |
37 | | |
38 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
39 | | #include "clang/AST/ParentMap.h" |
40 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
41 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
42 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
43 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
44 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
45 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" |
46 | | #include "llvm/Support/raw_ostream.h" |
47 | | |
48 | | using namespace clang; |
49 | | using namespace ento; |
50 | | |
51 | | static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND); |
52 | | static bool isInitializationMethod(const ObjCMethodDecl *MD); |
53 | | static bool isInitMessage(const ObjCMethodCall &Msg); |
54 | | static bool isSelfVar(SVal location, CheckerContext &C); |
55 | | |
56 | | namespace { |
57 | | class ObjCSelfInitChecker : public Checker< check::PostObjCMessage, |
58 | | check::PostStmt<ObjCIvarRefExpr>, |
59 | | check::PreStmt<ReturnStmt>, |
60 | | check::PreCall, |
61 | | check::PostCall, |
62 | | check::Location, |
63 | | check::Bind > { |
64 | | mutable std::unique_ptr<BugType> BT; |
65 | | |
66 | | void checkForInvalidSelf(const Expr *E, CheckerContext &C, |
67 | | const char *errorStr) const; |
68 | | |
69 | | public: |
70 | 50 | ObjCSelfInitChecker() {} |
71 | | void checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const; |
72 | | void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const; |
73 | | void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; |
74 | | void checkLocation(SVal location, bool isLoad, const Stmt *S, |
75 | | CheckerContext &C) const; |
76 | | void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; |
77 | | |
78 | | void checkPreCall(const CallEvent &CE, CheckerContext &C) const; |
79 | | void checkPostCall(const CallEvent &CE, CheckerContext &C) const; |
80 | | |
81 | | void printState(raw_ostream &Out, ProgramStateRef State, |
82 | | const char *NL, const char *Sep) const override; |
83 | | }; |
84 | | } // end anonymous namespace |
85 | | |
86 | | namespace { |
87 | | enum SelfFlagEnum { |
88 | | /// No flag set. |
89 | | SelfFlag_None = 0x0, |
90 | | /// Value came from 'self'. |
91 | | SelfFlag_Self = 0x1, |
92 | | /// Value came from the result of an initializer (e.g. [super init]). |
93 | | SelfFlag_InitRes = 0x2 |
94 | | }; |
95 | | } |
96 | | |
97 | | REGISTER_MAP_WITH_PROGRAMSTATE(SelfFlag, SymbolRef, SelfFlagEnum) |
98 | | REGISTER_TRAIT_WITH_PROGRAMSTATE(CalledInit, bool) |
99 | | |
100 | | /// A call receiving a reference to 'self' invalidates the object that |
101 | | /// 'self' contains. This keeps the "self flags" assigned to the 'self' |
102 | | /// object before the call so we can assign them to the new object that 'self' |
103 | | /// points to after the call. |
104 | | REGISTER_TRAIT_WITH_PROGRAMSTATE(PreCallSelfFlags, SelfFlagEnum) |
105 | | |
106 | 751 | static SelfFlagEnum getSelfFlags(SVal val, ProgramStateRef state) { |
107 | 751 | if (SymbolRef sym = val.getAsSymbol()) |
108 | 685 | if (const SelfFlagEnum *attachedFlags = state->get<SelfFlag>(sym)) |
109 | 519 | return *attachedFlags; |
110 | 232 | return SelfFlag_None; |
111 | 751 | } |
112 | | |
113 | 407 | static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) { |
114 | 407 | return getSelfFlags(val, C.getState()); |
115 | 407 | } |
116 | | |
117 | | static void addSelfFlag(ProgramStateRef state, SVal val, |
118 | 360 | SelfFlagEnum flag, CheckerContext &C) { |
119 | | // We tag the symbol that the SVal wraps. |
120 | 360 | if (SymbolRef sym = val.getAsSymbol()) { |
121 | 344 | state = state->set<SelfFlag>(sym, |
122 | 344 | SelfFlagEnum(getSelfFlags(val, state) | flag)); |
123 | 344 | C.addTransition(state); |
124 | 344 | } |
125 | 360 | } |
126 | | |
127 | 383 | static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C) { |
128 | 383 | return getSelfFlags(val, C) & flag; |
129 | 383 | } |
130 | | |
131 | | /// Returns true of the value of the expression is the object that 'self' |
132 | | /// points to and is an object that did not come from the result of calling |
133 | | /// an initializer. |
134 | 144 | static bool isInvalidSelf(const Expr *E, CheckerContext &C) { |
135 | 144 | SVal exprVal = C.getSVal(E); |
136 | 144 | if (!hasSelfFlag(exprVal, SelfFlag_Self, C)) |
137 | 43 | return false; // value did not come from 'self'. |
138 | 101 | if (hasSelfFlag(exprVal, SelfFlag_InitRes, C)) |
139 | 83 | return false; // 'self' is properly initialized. |
140 | | |
141 | 18 | return true; |
142 | 101 | } |
143 | | |
144 | | void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C, |
145 | 170 | const char *errorStr) const { |
146 | 170 | if (!E) |
147 | 0 | return; |
148 | | |
149 | 170 | if (!C.getState()->get<CalledInit>()) |
150 | 26 | return; |
151 | | |
152 | 144 | if (!isInvalidSelf(E, C)) |
153 | 126 | return; |
154 | | |
155 | | // Generate an error node. |
156 | 18 | ExplodedNode *N = C.generateErrorNode(); |
157 | 18 | if (!N) |
158 | 0 | return; |
159 | | |
160 | 18 | if (!BT) |
161 | 3 | BT.reset(new BugType(this, "Missing \"self = [(super or self) init...]\"", |
162 | 3 | categories::CoreFoundationObjectiveC)); |
163 | 18 | C.emitReport(std::make_unique<PathSensitiveBugReport>(*BT, errorStr, N)); |
164 | 18 | } |
165 | | |
166 | | void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, |
167 | 197 | CheckerContext &C) const { |
168 | | // When encountering a message that does initialization (init rule), |
169 | | // tag the return value so that we know later on that if self has this value |
170 | | // then it is properly initialized. |
171 | | |
172 | | // FIXME: A callback should disable checkers at the start of functions. |
173 | 197 | if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( |
174 | 197 | C.getCurrentAnalysisDeclContext()->getDecl()))) |
175 | 83 | return; |
176 | | |
177 | 114 | if (isInitMessage(Msg)) { |
178 | | // Tag the return value as the result of an initializer. |
179 | 85 | ProgramStateRef state = C.getState(); |
180 | | |
181 | | // FIXME this really should be context sensitive, where we record |
182 | | // the current stack frame (for IPA). Also, we need to clean this |
183 | | // value out when we return from this method. |
184 | 85 | state = state->set<CalledInit>(true); |
185 | | |
186 | 85 | SVal V = C.getSVal(Msg.getOriginExpr()); |
187 | 85 | addSelfFlag(state, V, SelfFlag_InitRes, C); |
188 | 85 | return; |
189 | 85 | } |
190 | | |
191 | | // We don't check for an invalid 'self' in an obj-c message expression to cut |
192 | | // down false positives where logging functions get information from self |
193 | | // (like its class) or doing "invalidation" on self when the initialization |
194 | | // fails. |
195 | 114 | } |
196 | | |
197 | | void ObjCSelfInitChecker::checkPostStmt(const ObjCIvarRefExpr *E, |
198 | 64 | CheckerContext &C) const { |
199 | | // FIXME: A callback should disable checkers at the start of functions. |
200 | 64 | if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( |
201 | 64 | C.getCurrentAnalysisDeclContext()->getDecl()))) |
202 | 20 | return; |
203 | | |
204 | 44 | checkForInvalidSelf( |
205 | 44 | E->getBase(), C, |
206 | 44 | "Instance variable used while 'self' is not set to the result of " |
207 | 44 | "'[(super or self) init...]'"); |
208 | 44 | } |
209 | | |
210 | | void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, |
211 | 239 | CheckerContext &C) const { |
212 | | // FIXME: A callback should disable checkers at the start of functions. |
213 | 239 | if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( |
214 | 239 | C.getCurrentAnalysisDeclContext()->getDecl()))) |
215 | 113 | return; |
216 | | |
217 | 126 | checkForInvalidSelf(S->getRetValue(), C, |
218 | 126 | "Returning 'self' while it is not set to the result of " |
219 | 126 | "'[(super or self) init...]'"); |
220 | 126 | } |
221 | | |
222 | | // When a call receives a reference to 'self', [Pre/Post]Call pass |
223 | | // the SelfFlags from the object 'self' points to before the call to the new |
224 | | // object after the call. This is to avoid invalidation of 'self' by logging |
225 | | // functions. |
226 | | // Another common pattern in classes with multiple initializers is to put the |
227 | | // subclass's common initialization bits into a static function that receives |
228 | | // the value of 'self', e.g: |
229 | | // @code |
230 | | // if (!(self = [super init])) |
231 | | // return nil; |
232 | | // if (!(self = _commonInit(self))) |
233 | | // return nil; |
234 | | // @endcode |
235 | | // Until we can use inter-procedural analysis, in such a call, transfer the |
236 | | // SelfFlags to the result of the call. |
237 | | |
238 | | void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE, |
239 | 607 | CheckerContext &C) const { |
240 | | // FIXME: A callback should disable checkers at the start of functions. |
241 | 607 | if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( |
242 | 607 | C.getCurrentAnalysisDeclContext()->getDecl()))) |
243 | 490 | return; |
244 | | |
245 | 117 | ProgramStateRef state = C.getState(); |
246 | 117 | unsigned NumArgs = CE.getNumArgs(); |
247 | | // If we passed 'self' as and argument to the call, record it in the state |
248 | | // to be propagated after the call. |
249 | | // Note, we could have just given up, but try to be more optimistic here and |
250 | | // assume that the functions are going to continue initialization or will not |
251 | | // modify self. |
252 | 148 | for (unsigned i = 0; i < NumArgs; ++i31 ) { |
253 | 55 | SVal argV = CE.getArgSVal(i); |
254 | 55 | if (isSelfVar(argV, C)) { |
255 | 8 | SelfFlagEnum selfFlags = |
256 | 8 | getSelfFlags(state->getSVal(argV.castAs<Loc>()), C); |
257 | 8 | C.addTransition(state->set<PreCallSelfFlags>(selfFlags)); |
258 | 8 | return; |
259 | 47 | } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { |
260 | 16 | SelfFlagEnum selfFlags = getSelfFlags(argV, C); |
261 | 16 | C.addTransition(state->set<PreCallSelfFlags>(selfFlags)); |
262 | 16 | return; |
263 | 16 | } |
264 | 55 | } |
265 | 117 | } |
266 | | |
267 | | void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE, |
268 | 637 | CheckerContext &C) const { |
269 | | // FIXME: A callback should disable checkers at the start of functions. |
270 | 637 | if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( |
271 | 637 | C.getCurrentAnalysisDeclContext()->getDecl()))) |
272 | 509 | return; |
273 | | |
274 | 128 | ProgramStateRef state = C.getState(); |
275 | 128 | SelfFlagEnum prevFlags = state->get<PreCallSelfFlags>(); |
276 | 128 | if (!prevFlags) |
277 | 102 | return; |
278 | 26 | state = state->remove<PreCallSelfFlags>(); |
279 | | |
280 | 26 | unsigned NumArgs = CE.getNumArgs(); |
281 | 30 | for (unsigned i = 0; i < NumArgs; ++i4 ) { |
282 | 28 | SVal argV = CE.getArgSVal(i); |
283 | 28 | if (isSelfVar(argV, C)) { |
284 | | // If the address of 'self' is being passed to the call, assume that the |
285 | | // 'self' after the call will have the same flags. |
286 | | // EX: log(&self) |
287 | 8 | addSelfFlag(state, state->getSVal(argV.castAs<Loc>()), prevFlags, C); |
288 | 8 | return; |
289 | 20 | } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { |
290 | | // If 'self' is passed to the call by value, assume that the function |
291 | | // returns 'self'. So assign the flags, which were set on 'self' to the |
292 | | // return value. |
293 | | // EX: self = performMoreInitialization(self) |
294 | 16 | addSelfFlag(state, CE.getReturnValue(), prevFlags, C); |
295 | 16 | return; |
296 | 16 | } |
297 | 28 | } |
298 | | |
299 | 2 | C.addTransition(state); |
300 | 2 | } |
301 | | |
302 | | void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad, |
303 | | const Stmt *S, |
304 | 1.00k | CheckerContext &C) const { |
305 | 1.00k | if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( |
306 | 1.00k | C.getCurrentAnalysisDeclContext()->getDecl()))) |
307 | 675 | return; |
308 | | |
309 | | // Tag the result of a load from 'self' so that we can easily know that the |
310 | | // value is the object that 'self' points to. |
311 | 326 | ProgramStateRef state = C.getState(); |
312 | 326 | if (isSelfVar(location, C)) |
313 | 251 | addSelfFlag(state, state->getSVal(location.castAs<Loc>()), SelfFlag_Self, |
314 | 251 | C); |
315 | 326 | } |
316 | | |
317 | | |
318 | | void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S, |
319 | 340 | CheckerContext &C) const { |
320 | | // Allow assignment of anything to self. Self is a local variable in the |
321 | | // initializer, so it is legal to assign anything to it, like results of |
322 | | // static functions/method calls. After self is assigned something we cannot |
323 | | // reason about, stop enforcing the rules. |
324 | | // (Only continue checking if the assigned value should be treated as self.) |
325 | 340 | if ((isSelfVar(loc, C)) && |
326 | 340 | !hasSelfFlag(val, SelfFlag_InitRes, C)61 && |
327 | 340 | !hasSelfFlag(val, SelfFlag_Self, C)10 && |
328 | 340 | !isSelfVar(val, C)4 ) { |
329 | | |
330 | | // Stop tracking the checker-specific state in the state. |
331 | 4 | ProgramStateRef State = C.getState(); |
332 | 4 | State = State->remove<CalledInit>(); |
333 | 4 | if (SymbolRef sym = loc.getAsSymbol()) |
334 | 0 | State = State->remove<SelfFlag>(sym); |
335 | 4 | C.addTransition(State); |
336 | 4 | } |
337 | 340 | } |
338 | | |
339 | | void ObjCSelfInitChecker::printState(raw_ostream &Out, ProgramStateRef State, |
340 | 0 | const char *NL, const char *Sep) const { |
341 | 0 | SelfFlagTy FlagMap = State->get<SelfFlag>(); |
342 | 0 | bool DidCallInit = State->get<CalledInit>(); |
343 | 0 | SelfFlagEnum PreCallFlags = State->get<PreCallSelfFlags>(); |
344 | |
|
345 | 0 | if (FlagMap.isEmpty() && !DidCallInit && !PreCallFlags) |
346 | 0 | return; |
347 | | |
348 | 0 | Out << Sep << NL << *this << " :" << NL; |
349 | |
|
350 | 0 | if (DidCallInit) |
351 | 0 | Out << " An init method has been called." << NL; |
352 | |
|
353 | 0 | if (PreCallFlags != SelfFlag_None) { |
354 | 0 | if (PreCallFlags & SelfFlag_Self) { |
355 | 0 | Out << " An argument of the current call came from the 'self' variable." |
356 | 0 | << NL; |
357 | 0 | } |
358 | 0 | if (PreCallFlags & SelfFlag_InitRes) { |
359 | 0 | Out << " An argument of the current call came from an init method." |
360 | 0 | << NL; |
361 | 0 | } |
362 | 0 | } |
363 | |
|
364 | 0 | Out << NL; |
365 | 0 | for (auto [Sym, Flag] : FlagMap) { |
366 | 0 | Out << Sym << " : "; |
367 | |
|
368 | 0 | if (Flag == SelfFlag_None) |
369 | 0 | Out << "none"; |
370 | |
|
371 | 0 | if (Flag & SelfFlag_Self) |
372 | 0 | Out << "self variable"; |
373 | |
|
374 | 0 | if (Flag & SelfFlag_InitRes) { |
375 | 0 | if (Flag != SelfFlag_InitRes) |
376 | 0 | Out << " | "; |
377 | 0 | Out << "result of init method"; |
378 | 0 | } |
379 | |
|
380 | 0 | Out << NL; |
381 | 0 | } |
382 | 0 | } |
383 | | |
384 | | |
385 | | // FIXME: A callback should disable checkers at the start of functions. |
386 | 2.74k | static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) { |
387 | 2.74k | if (!ND) |
388 | 2 | return false; |
389 | | |
390 | 2.74k | const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ND); |
391 | 2.74k | if (!MD) |
392 | 1.65k | return false; |
393 | 1.08k | if (!isInitializationMethod(MD)) |
394 | 229 | return false; |
395 | | |
396 | | // self = [super init] applies only to NSObject subclasses. |
397 | | // For instance, NSProxy doesn't implement -init. |
398 | 859 | ASTContext &Ctx = MD->getASTContext(); |
399 | 859 | IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); |
400 | 859 | ObjCInterfaceDecl *ID = MD->getClassInterface()->getSuperClass(); |
401 | 953 | for ( ; ID ; ID = ID->getSuperClass()94 ) { |
402 | 949 | IdentifierInfo *II = ID->getIdentifier(); |
403 | | |
404 | 949 | if (II == NSObjectII) |
405 | 855 | break; |
406 | 949 | } |
407 | 859 | return ID != nullptr; |
408 | 1.08k | } |
409 | | |
410 | | /// Returns true if the location is 'self'. |
411 | 753 | static bool isSelfVar(SVal location, CheckerContext &C) { |
412 | 753 | AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext(); |
413 | 753 | if (!analCtx->getSelfDecl()) |
414 | 223 | return false; |
415 | 530 | if (!isa<loc::MemRegionVal>(location)) |
416 | 21 | return false; |
417 | | |
418 | 509 | loc::MemRegionVal MRV = location.castAs<loc::MemRegionVal>(); |
419 | 509 | if (const DeclRegion *DR = dyn_cast<DeclRegion>(MRV.stripCasts())) |
420 | 458 | return (DR->getDecl() == analCtx->getSelfDecl()); |
421 | | |
422 | 51 | return false; |
423 | 509 | } |
424 | | |
425 | 1.08k | static bool isInitializationMethod(const ObjCMethodDecl *MD) { |
426 | 1.08k | return MD->getMethodFamily() == OMF_init; |
427 | 1.08k | } |
428 | | |
429 | 114 | static bool isInitMessage(const ObjCMethodCall &Call) { |
430 | 114 | return Call.getMethodFamily() == OMF_init; |
431 | 114 | } |
432 | | |
433 | | //===----------------------------------------------------------------------===// |
434 | | // Registration. |
435 | | //===----------------------------------------------------------------------===// |
436 | | |
437 | 50 | void ento::registerObjCSelfInitChecker(CheckerManager &mgr) { |
438 | 50 | mgr.registerChecker<ObjCSelfInitChecker>(); |
439 | 50 | } |
440 | | |
441 | 100 | bool ento::shouldRegisterObjCSelfInitChecker(const CheckerManager &mgr) { |
442 | 100 | return true; |
443 | 100 | } |