clang  15.0.0git
ObjCSelfInitChecker.cpp
Go to the documentation of this file.
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 
39 #include "clang/AST/ParentMap.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  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 static SelfFlagEnum getSelfFlags(SVal val, ProgramStateRef state) {
107  if (SymbolRef sym = val.getAsSymbol())
108  if (const SelfFlagEnum *attachedFlags = state->get<SelfFlag>(sym))
109  return *attachedFlags;
110  return SelfFlag_None;
111 }
112 
113 static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) {
114  return getSelfFlags(val, C.getState());
115 }
116 
117 static void addSelfFlag(ProgramStateRef state, SVal val,
118  SelfFlagEnum flag, CheckerContext &C) {
119  // We tag the symbol that the SVal wraps.
120  if (SymbolRef sym = val.getAsSymbol()) {
121  state = state->set<SelfFlag>(sym,
122  SelfFlagEnum(getSelfFlags(val, state) | flag));
123  C.addTransition(state);
124  }
125 }
126 
127 static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C) {
128  return getSelfFlags(val, C) & flag;
129 }
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 static bool isInvalidSelf(const Expr *E, CheckerContext &C) {
135  SVal exprVal = C.getSVal(E);
136  if (!hasSelfFlag(exprVal, SelfFlag_Self, C))
137  return false; // value did not come from 'self'.
138  if (hasSelfFlag(exprVal, SelfFlag_InitRes, C))
139  return false; // 'self' is properly initialized.
140 
141  return true;
142 }
143 
144 void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C,
145  const char *errorStr) const {
146  if (!E)
147  return;
148 
149  if (!C.getState()->get<CalledInit>())
150  return;
151 
152  if (!isInvalidSelf(E, C))
153  return;
154 
155  // Generate an error node.
156  ExplodedNode *N = C.generateErrorNode();
157  if (!N)
158  return;
159 
160  if (!BT)
161  BT.reset(new BugType(this, "Missing \"self = [(super or self) init...]\"",
163  C.emitReport(std::make_unique<PathSensitiveBugReport>(*BT, errorStr, N));
164 }
165 
166 void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
167  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  if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
174  C.getCurrentAnalysisDeclContext()->getDecl())))
175  return;
176 
177  if (isInitMessage(Msg)) {
178  // Tag the return value as the result of an initializer.
179  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  state = state->set<CalledInit>(true);
185 
186  SVal V = C.getSVal(Msg.getOriginExpr());
187  addSelfFlag(state, V, SelfFlag_InitRes, C);
188  return;
189  }
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 }
196 
197 void ObjCSelfInitChecker::checkPostStmt(const ObjCIvarRefExpr *E,
198  CheckerContext &C) const {
199  // FIXME: A callback should disable checkers at the start of functions.
200  if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
201  C.getCurrentAnalysisDeclContext()->getDecl())))
202  return;
203 
204  checkForInvalidSelf(
205  E->getBase(), C,
206  "Instance variable used while 'self' is not set to the result of "
207  "'[(super or self) init...]'");
208 }
209 
210 void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S,
211  CheckerContext &C) const {
212  // FIXME: A callback should disable checkers at the start of functions.
213  if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
214  C.getCurrentAnalysisDeclContext()->getDecl())))
215  return;
216 
217  checkForInvalidSelf(S->getRetValue(), C,
218  "Returning 'self' while it is not set to the result of "
219  "'[(super or self) init...]'");
220 }
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  CheckerContext &C) const {
240  // FIXME: A callback should disable checkers at the start of functions.
241  if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
242  C.getCurrentAnalysisDeclContext()->getDecl())))
243  return;
244 
245  ProgramStateRef state = C.getState();
246  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  for (unsigned i = 0; i < NumArgs; ++i) {
253  SVal argV = CE.getArgSVal(i);
254  if (isSelfVar(argV, C)) {
255  SelfFlagEnum selfFlags =
256  getSelfFlags(state->getSVal(argV.castAs<Loc>()), C);
257  C.addTransition(state->set<PreCallSelfFlags>(selfFlags));
258  return;
259  } else if (hasSelfFlag(argV, SelfFlag_Self, C)) {
260  SelfFlagEnum selfFlags = getSelfFlags(argV, C);
261  C.addTransition(state->set<PreCallSelfFlags>(selfFlags));
262  return;
263  }
264  }
265 }
266 
267 void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE,
268  CheckerContext &C) const {
269  // FIXME: A callback should disable checkers at the start of functions.
270  if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
271  C.getCurrentAnalysisDeclContext()->getDecl())))
272  return;
273 
274  ProgramStateRef state = C.getState();
275  SelfFlagEnum prevFlags = state->get<PreCallSelfFlags>();
276  if (!prevFlags)
277  return;
278  state = state->remove<PreCallSelfFlags>();
279 
280  unsigned NumArgs = CE.getNumArgs();
281  for (unsigned i = 0; i < NumArgs; ++i) {
282  SVal argV = CE.getArgSVal(i);
283  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  addSelfFlag(state, state->getSVal(argV.castAs<Loc>()), prevFlags, C);
288  return;
289  } 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  addSelfFlag(state, CE.getReturnValue(), prevFlags, C);
295  return;
296  }
297  }
298 
299  C.addTransition(state);
300 }
301 
302 void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad,
303  const Stmt *S,
304  CheckerContext &C) const {
305  if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
306  C.getCurrentAnalysisDeclContext()->getDecl())))
307  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  ProgramStateRef state = C.getState();
312  if (isSelfVar(location, C))
313  addSelfFlag(state, state->getSVal(location.castAs<Loc>()), SelfFlag_Self,
314  C);
315 }
316 
317 
318 void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S,
319  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  if ((isSelfVar(loc, C)) &&
326  !hasSelfFlag(val, SelfFlag_InitRes, C) &&
327  !hasSelfFlag(val, SelfFlag_Self, C) &&
328  !isSelfVar(val, C)) {
329 
330  // Stop tracking the checker-specific state in the state.
331  ProgramStateRef State = C.getState();
332  State = State->remove<CalledInit>();
333  if (SymbolRef sym = loc.getAsSymbol())
334  State = State->remove<SelfFlag>(sym);
335  C.addTransition(State);
336  }
337 }
338 
339 void ObjCSelfInitChecker::printState(raw_ostream &Out, ProgramStateRef State,
340  const char *NL, const char *Sep) const {
341  SelfFlagTy FlagMap = State->get<SelfFlag>();
342  bool DidCallInit = State->get<CalledInit>();
343  SelfFlagEnum PreCallFlags = State->get<PreCallSelfFlags>();
344 
345  if (FlagMap.isEmpty() && !DidCallInit && !PreCallFlags)
346  return;
347 
348  Out << Sep << NL << *this << " :" << NL;
349 
350  if (DidCallInit)
351  Out << " An init method has been called." << NL;
352 
353  if (PreCallFlags != SelfFlag_None) {
354  if (PreCallFlags & SelfFlag_Self) {
355  Out << " An argument of the current call came from the 'self' variable."
356  << NL;
357  }
358  if (PreCallFlags & SelfFlag_InitRes) {
359  Out << " An argument of the current call came from an init method."
360  << NL;
361  }
362  }
363 
364  Out << NL;
365  for (SelfFlagTy::iterator I = FlagMap.begin(), E = FlagMap.end();
366  I != E; ++I) {
367  Out << I->first << " : ";
368 
369  if (I->second == SelfFlag_None)
370  Out << "none";
371 
372  if (I->second & SelfFlag_Self)
373  Out << "self variable";
374 
375  if (I->second & SelfFlag_InitRes) {
376  if (I->second != SelfFlag_InitRes)
377  Out << " | ";
378  Out << "result of init method";
379  }
380 
381  Out << NL;
382  }
383 }
384 
385 
386 // FIXME: A callback should disable checkers at the start of functions.
387 static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) {
388  if (!ND)
389  return false;
390 
391  const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ND);
392  if (!MD)
393  return false;
394  if (!isInitializationMethod(MD))
395  return false;
396 
397  // self = [super init] applies only to NSObject subclasses.
398  // For instance, NSProxy doesn't implement -init.
399  ASTContext &Ctx = MD->getASTContext();
400  IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject");
402  for ( ; ID ; ID = ID->getSuperClass()) {
403  IdentifierInfo *II = ID->getIdentifier();
404 
405  if (II == NSObjectII)
406  break;
407  }
408  return ID != nullptr;
409 }
410 
411 /// Returns true if the location is 'self'.
412 static bool isSelfVar(SVal location, CheckerContext &C) {
413  AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext();
414  if (!analCtx->getSelfDecl())
415  return false;
416  if (!isa<loc::MemRegionVal>(location))
417  return false;
418 
419  loc::MemRegionVal MRV = location.castAs<loc::MemRegionVal>();
420  if (const DeclRegion *DR = dyn_cast<DeclRegion>(MRV.stripCasts()))
421  return (DR->getDecl() == analCtx->getSelfDecl());
422 
423  return false;
424 }
425 
426 static bool isInitializationMethod(const ObjCMethodDecl *MD) {
427  return MD->getMethodFamily() == OMF_init;
428 }
429 
430 static bool isInitMessage(const ObjCMethodCall &Call) {
431  return Call.getMethodFamily() == OMF_init;
432 }
433 
434 //===----------------------------------------------------------------------===//
435 // Registration.
436 //===----------------------------------------------------------------------===//
437 
438 void ento::registerObjCSelfInitChecker(CheckerManager &mgr) {
439  mgr.registerChecker<ObjCSelfInitChecker>();
440 }
441 
442 bool ento::shouldRegisterObjCSelfInitChecker(const CheckerManager &mgr) {
443  return true;
444 }
clang::ObjCInterfaceDecl
Represents an ObjC class declaration.
Definition: DeclObjC.h:1150
getSelfFlags
static SelfFlagEnum getSelfFlags(SVal val, ProgramStateRef state)
A call receiving a reference to 'self' invalidates the object that 'self' contains.
Definition: ObjCSelfInitChecker.cpp:106
clang::Decl::getASTContext
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:414
clang::IdentifierTable::get
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
Definition: IdentifierTable.h:596
clang::NamedDecl
This represents a decl that may have a name.
Definition: Decl.h:247
isInitializationMethod
static bool isInitializationMethod(const ObjCMethodDecl *MD)
Definition: ObjCSelfInitChecker.cpp:426
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:55
clang::AnalysisDeclContext
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Definition: AnalysisDeclContext.h:72
clang::AnalysisDeclContext::getSelfDecl
const ImplicitParamDecl * getSelfDecl() const
Definition: AnalysisDeclContext.cpp:148
clang::OMF_init
@ OMF_init
Definition: IdentifierTable.h:700
clang::ObjCMethodDecl::getClassInterface
ObjCInterfaceDecl * getClassInterface()
Definition: DeclObjC.cpp:1176
clang::ento::SymbolRef
const SymExpr * SymbolRef
Definition: SymExpr.h:111
REGISTER_TRAIT_WITH_PROGRAMSTATE
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy.
Definition: ProgramStateTrait.h:34
CallEvent.h
REGISTER_MAP_WITH_PROGRAMSTATE
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Definition: ProgramStateTrait.h:87
V
#define V(N, I)
Definition: ASTContext.h:3176
BuiltinCheckerRegistration.h
hasSelfFlag
static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C)
Definition: ObjCSelfInitChecker.cpp:127
CheckerManager.h
clang::ObjCMethodDecl::getMethodFamily
ObjCMethodFamily getMethodFamily() const
Determines the family of this method.
Definition: DeclObjC.cpp:1018
clang::ASTContext
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:208
isInvalidSelf
static bool isInvalidSelf(const Expr *E, CheckerContext &C)
Returns true of the value of the expression is the object that 'self' points to and is an object that...
Definition: ObjCSelfInitChecker.cpp:134
state
and static some checkers Checker The latter are built on top of the former via the Checker and CheckerVisitor and attempts to isolate them from much of the gore of the internal analysis the analyzer is basically a source code simulator that traces out possible paths of execution The state of the and the combination of state and program point is a node in an exploded which has the entry program point and initial state
Definition: README.txt:30
isSelfVar
static bool isSelfVar(SVal location, CheckerContext &C)
Returns true if the location is 'self'.
Definition: ObjCSelfInitChecker.cpp:412
clang::ento::categories::CoreFoundationObjectiveC
const char *const CoreFoundationObjectiveC
Definition: CommonBugCategories.cpp:16
BugType.h
clang::ASTContext::Idents
IdentifierTable & Idents
Definition: ASTContext.h:655
State
LineState State
Definition: UnwrappedLineFormatter.cpp:1126
clang::ObjCInterfaceDecl::getSuperClass
ObjCInterfaceDecl * getSuperClass() const
Definition: DeclObjC.cpp:351
clang::IdentifierInfo
One of these records is kept for each identifier that is lexed.
Definition: IdentifierTable.h:84
CheckerContext.h
Checker.h
clang::ObjCMethodDecl
ObjCMethodDecl - Represents an instance or class method declaration.
Definition: DeclObjC.h:139
ParentMap.h
clang::Builtin::ID
ID
Definition: Builtins.h:52
clang
Definition: CalledOnceCheck.h:17
clang::Stmt
Stmt - This represents one statement.
Definition: Stmt.h:69
addSelfFlag
static void addSelfFlag(ProgramStateRef state, SVal val, SelfFlagEnum flag, CheckerContext &C)
Definition: ObjCSelfInitChecker.cpp:117
isInitMessage
static bool isInitMessage(const ObjCMethodCall &Msg)
Definition: ObjCSelfInitChecker.cpp:430
clang::ObjCIvarRefExpr::getBase
const Expr * getBase() const
Definition: ExprObjC.h:580
ProgramStateTrait.h
clang::Expr
This represents one expression.
Definition: Expr.h:109
clang::ObjCIvarRefExpr
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:548
shouldRunOnFunctionOrMethod
static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND)
Definition: ObjCSelfInitChecker.cpp:387
clang::ReturnStmt
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Definition: Stmt.h:2764