ObjCSelfInitChecker.cpp revision 695fb502825a53ccd178ec1c85c77929d88acb71
1//== ObjCSelfInitChecker.cpp - Checker for 'self' initialization -*- C++ -*--=// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This defines ObjCSelfInitChecker, a builtin check that checks for uses of 11// 'self' before proper initialization. 12// 13//===----------------------------------------------------------------------===// 14 15// This checks initialization methods to verify that they assign 'self' to the 16// result of an initialization call (e.g. [super init], or [self initWith..]) 17// before using 'self' or any instance variable. 18// 19// To perform the required checking, values are tagged wih flags that indicate 20// 1) if the object is the one pointed to by 'self', and 2) if the object 21// is the result of an initializer (e.g. [super init]). 22// 23// Uses of an object that is true for 1) but not 2) trigger a diagnostic. 24// The uses that are currently checked are: 25// - Using instance variables. 26// - Returning the object. 27// 28// Note that we don't check for an invalid 'self' that is the receiver of an 29// obj-c message expression to cut down false positives where logging functions 30// get information from self (like its class) or doing "invalidation" on self 31// when the initialization fails. 32// 33// Because the object that 'self' points to gets invalidated when a call 34// receives a reference to 'self', the checker keeps track and passes the flags 35// for 1) and 2) to the new object that 'self' points to after the call. 36// 37// FIXME (rdar://7937506): In the case of: 38// [super init]; 39// return self; 40// Have an extra PathDiagnosticPiece in the path that says "called [super init], 41// but didn't assign the result to self." 42 43//===----------------------------------------------------------------------===// 44 45// FIXME: Somehow stick the link to Apple's documentation about initializing 46// objects in the diagnostics. 47// http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocAllocInit.html 48 49#include "ClangSACheckers.h" 50#include "clang/StaticAnalyzer/Core/CheckerManager.h" 51#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" 52#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" 53#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 54#include "clang/Analysis/DomainSpecific/CocoaConventions.h" 55#include "clang/AST/ParentMap.h" 56 57using namespace clang; 58using namespace ento; 59 60static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND); 61static bool isInitializationMethod(const ObjCMethodDecl *MD); 62static bool isInitMessage(const ObjCMessage &msg); 63static bool isSelfVar(SVal location, CheckerContext &C); 64 65namespace { 66enum SelfFlagEnum { 67 /// \brief No flag set. 68 SelfFlag_None = 0x0, 69 /// \brief Value came from 'self'. 70 SelfFlag_Self = 0x1, 71 /// \brief Value came from the result of an initializer (e.g. [super init]). 72 SelfFlag_InitRes = 0x2 73}; 74} 75 76namespace { 77class ObjCSelfInitChecker : public CheckerVisitor<ObjCSelfInitChecker> { 78 /// \brief A call receiving a reference to 'self' invalidates the object that 79 /// 'self' contains. This field keeps the "self flags" assigned to the 'self' 80 /// object before the call and assign them to the new object that 'self' 81 /// points to after the call. 82 SelfFlagEnum preCallSelfFlags; 83 84public: 85 static void *getTag() { static int tag = 0; return &tag; } 86 void postVisitObjCMessage(CheckerContext &C, ObjCMessage msg); 87 void PostVisitObjCIvarRefExpr(CheckerContext &C, const ObjCIvarRefExpr *E); 88 void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); 89 void PreVisitGenericCall(CheckerContext &C, const CallExpr *CE); 90 void PostVisitGenericCall(CheckerContext &C, const CallExpr *CE); 91 virtual void visitLocation(CheckerContext &C, const Stmt *S, SVal location, 92 bool isLoad); 93}; 94} // end anonymous namespace 95 96static void RegisterObjCSelfInitChecker(ExprEngine &Eng) { 97 if (Eng.getContext().getLangOptions().ObjC1) 98 Eng.registerCheck(new ObjCSelfInitChecker()); 99} 100 101void ento::registerObjCSelfInitChecker(CheckerManager &mgr) { 102 mgr.addCheckerRegisterFunction(RegisterObjCSelfInitChecker); 103} 104 105namespace { 106 107class InitSelfBug : public BugType { 108 const std::string desc; 109public: 110 InitSelfBug() : BugType("missing \"self = [(super or self) init...]\"", 111 "missing \"self = [(super or self) init...]\"") {} 112}; 113 114} // end anonymous namespace 115 116typedef llvm::ImmutableMap<SymbolRef, unsigned> SelfFlag; 117namespace { struct CalledInit {}; } 118 119namespace clang { 120namespace ento { 121 template<> 122 struct GRStateTrait<SelfFlag> : public GRStatePartialTrait<SelfFlag> { 123 static void* GDMIndex() { 124 static int index = 0; 125 return &index; 126 } 127 }; 128 template <> 129 struct GRStateTrait<CalledInit> : public GRStatePartialTrait<bool> { 130 static void *GDMIndex() { static int index = 0; return &index; } 131 }; 132} 133} 134 135static SelfFlagEnum getSelfFlags(SVal val, const GRState *state) { 136 if (SymbolRef sym = val.getAsSymbol()) 137 if (const unsigned *attachedFlags = state->get<SelfFlag>(sym)) 138 return (SelfFlagEnum)*attachedFlags; 139 return SelfFlag_None; 140} 141 142static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) { 143 return getSelfFlags(val, C.getState()); 144} 145 146static void addSelfFlag(const GRState *state, SVal val, 147 SelfFlagEnum flag, CheckerContext &C) { 148 // We tag the symbol that the SVal wraps. 149 if (SymbolRef sym = val.getAsSymbol()) 150 C.addTransition(state->set<SelfFlag>(sym, getSelfFlags(val, C) | flag)); 151} 152 153static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C) { 154 return getSelfFlags(val, C) & flag; 155} 156 157/// \brief Returns true of the value of the expression is the object that 'self' 158/// points to and is an object that did not come from the result of calling 159/// an initializer. 160static bool isInvalidSelf(const Expr *E, CheckerContext &C) { 161 SVal exprVal = C.getState()->getSVal(E); 162 if (!hasSelfFlag(exprVal, SelfFlag_Self, C)) 163 return false; // value did not come from 'self'. 164 if (hasSelfFlag(exprVal, SelfFlag_InitRes, C)) 165 return false; // 'self' is properly initialized. 166 167 return true; 168} 169 170static void checkForInvalidSelf(const Expr *E, CheckerContext &C, 171 const char *errorStr) { 172 if (!E) 173 return; 174 175 if (!C.getState()->get<CalledInit>()) 176 return; 177 178 if (!isInvalidSelf(E, C)) 179 return; 180 181 // Generate an error node. 182 ExplodedNode *N = C.generateSink(); 183 if (!N) 184 return; 185 186 EnhancedBugReport *report = 187 new EnhancedBugReport(*new InitSelfBug(), errorStr, N); 188 C.EmitReport(report); 189} 190 191void ObjCSelfInitChecker::postVisitObjCMessage(CheckerContext &C, 192 ObjCMessage msg) { 193 // When encountering a message that does initialization (init rule), 194 // tag the return value so that we know later on that if self has this value 195 // then it is properly initialized. 196 197 // FIXME: A callback should disable checkers at the start of functions. 198 if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( 199 C.getCurrentAnalysisContext()->getDecl()))) 200 return; 201 202 if (isInitMessage(msg)) { 203 // Tag the return value as the result of an initializer. 204 const GRState *state = C.getState(); 205 206 // FIXME this really should be context sensitive, where we record 207 // the current stack frame (for IPA). Also, we need to clean this 208 // value out when we return from this method. 209 state = state->set<CalledInit>(true); 210 211 SVal V = state->getSVal(msg.getOriginExpr()); 212 addSelfFlag(state, V, SelfFlag_InitRes, C); 213 return; 214 } 215 216 // We don't check for an invalid 'self' in an obj-c message expression to cut 217 // down false positives where logging functions get information from self 218 // (like its class) or doing "invalidation" on self when the initialization 219 // fails. 220} 221 222void ObjCSelfInitChecker::PostVisitObjCIvarRefExpr(CheckerContext &C, 223 const ObjCIvarRefExpr *E) { 224 // FIXME: A callback should disable checkers at the start of functions. 225 if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( 226 C.getCurrentAnalysisContext()->getDecl()))) 227 return; 228 229 checkForInvalidSelf(E->getBase(), C, 230 "Instance variable used while 'self' is not set to the result of " 231 "'[(super or self) init...]'"); 232} 233 234void ObjCSelfInitChecker::PreVisitReturnStmt(CheckerContext &C, 235 const ReturnStmt *S) { 236 // FIXME: A callback should disable checkers at the start of functions. 237 if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( 238 C.getCurrentAnalysisContext()->getDecl()))) 239 return; 240 241 checkForInvalidSelf(S->getRetValue(), C, 242 "Returning 'self' while it is not set to the result of " 243 "'[(super or self) init...]'"); 244} 245 246// When a call receives a reference to 'self', [Pre/Post]VisitGenericCall pass 247// the SelfFlags from the object 'self' point to before the call, to the new 248// object after the call. This is to avoid invalidation of 'self' by logging 249// functions. 250// Another common pattern in classes with multiple initializers is to put the 251// subclass's common initialization bits into a static function that receives 252// the value of 'self', e.g: 253// @code 254// if (!(self = [super init])) 255// return nil; 256// if (!(self = _commonInit(self))) 257// return nil; 258// @endcode 259// Until we can use inter-procedural analysis, in such a call, transfer the 260// SelfFlags to the result of the call. 261 262void ObjCSelfInitChecker::PreVisitGenericCall(CheckerContext &C, 263 const CallExpr *CE) { 264 const GRState *state = C.getState(); 265 for (CallExpr::const_arg_iterator 266 I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) { 267 SVal argV = state->getSVal(*I); 268 if (isSelfVar(argV, C)) { 269 preCallSelfFlags = getSelfFlags(state->getSVal(cast<Loc>(argV)), C); 270 return; 271 } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { 272 preCallSelfFlags = getSelfFlags(argV, C); 273 return; 274 } 275 } 276} 277 278void ObjCSelfInitChecker::PostVisitGenericCall(CheckerContext &C, 279 const CallExpr *CE) { 280 const GRState *state = C.getState(); 281 for (CallExpr::const_arg_iterator 282 I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) { 283 SVal argV = state->getSVal(*I); 284 if (isSelfVar(argV, C)) { 285 addSelfFlag(state, state->getSVal(cast<Loc>(argV)), preCallSelfFlags, C); 286 return; 287 } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { 288 addSelfFlag(state, state->getSVal(CE), preCallSelfFlags, C); 289 return; 290 } 291 } 292} 293 294void ObjCSelfInitChecker::visitLocation(CheckerContext &C, const Stmt *S, 295 SVal location, bool isLoad) { 296 // Tag the result of a load from 'self' so that we can easily know that the 297 // value is the object that 'self' points to. 298 const GRState *state = C.getState(); 299 if (isSelfVar(location, C)) 300 addSelfFlag(state, state->getSVal(cast<Loc>(location)), SelfFlag_Self, C); 301} 302 303// FIXME: A callback should disable checkers at the start of functions. 304static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) { 305 if (!ND) 306 return false; 307 308 const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ND); 309 if (!MD) 310 return false; 311 if (!isInitializationMethod(MD)) 312 return false; 313 314 // self = [super init] applies only to NSObject subclasses. 315 // For instance, NSProxy doesn't implement -init. 316 ASTContext& Ctx = MD->getASTContext(); 317 IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); 318 ObjCInterfaceDecl* ID = MD->getClassInterface()->getSuperClass(); 319 for ( ; ID ; ID = ID->getSuperClass()) { 320 IdentifierInfo *II = ID->getIdentifier(); 321 322 if (II == NSObjectII) 323 break; 324 } 325 if (!ID) 326 return false; 327 328 return true; 329} 330 331/// \brief Returns true if the location is 'self'. 332static bool isSelfVar(SVal location, CheckerContext &C) { 333 AnalysisContext *analCtx = C.getCurrentAnalysisContext(); 334 if (!analCtx->getSelfDecl()) 335 return false; 336 if (!isa<loc::MemRegionVal>(location)) 337 return false; 338 339 loc::MemRegionVal MRV = cast<loc::MemRegionVal>(location); 340 if (const DeclRegion *DR = dyn_cast<DeclRegion>(MRV.getRegion())) 341 return (DR->getDecl() == analCtx->getSelfDecl()); 342 343 return false; 344} 345 346static bool isInitializationMethod(const ObjCMethodDecl *MD) { 347 // Init methods with prefix like '-(id)_init' are private and the requirements 348 // are less strict so we don't check those. 349 return MD->isInstanceMethod() && 350 cocoa::deriveNamingConvention(MD->getSelector(), 351 /*ignorePrefix=*/false) == cocoa::InitRule; 352} 353 354static bool isInitMessage(const ObjCMessage &msg) { 355 return cocoa::deriveNamingConvention(msg.getSelector()) == cocoa::InitRule; 356} 357