//== ObjCSelfInitChecker.cpp - Checker for 'self' initialization -*- C++ -*--=// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This defines ObjCSelfInitChecker, a builtin check that checks for uses of // 'self' before proper initialization. // //===----------------------------------------------------------------------===// // This checks initialization methods to verify that they assign 'self' to the // result of an initialization call (e.g. [super init], or [self initWith..]) // before using 'self' or any instance variable. // // To perform the required checking, values are tagged with flags that indicate // 1) if the object is the one pointed to by 'self', and 2) if the object // is the result of an initializer (e.g. [super init]). // // Uses of an object that is true for 1) but not 2) trigger a diagnostic. // The uses that are currently checked are: // - Using instance variables. // - Returning the object. // // Note that we don't check for an invalid 'self' that is the receiver of an // obj-c message expression to cut down false positives where logging functions // get information from self (like its class) or doing "invalidation" on self // when the initialization fails. // // Because the object that 'self' points to gets invalidated when a call // receives a reference to 'self', the checker keeps track and passes the flags // for 1) and 2) to the new object that 'self' points to after the call. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/ParentMap.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/Support/raw_ostream.h" usingnamespaceclang; usingnamespaceento; static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND); static bool isInitializationMethod(const ObjCMethodDecl *MD); static bool isInitMessage(const ObjCMethodCall &Msg); static bool isSelfVar(SVal location, CheckerContext &C); namespace { class ObjCSelfInitChecker : public Checker< check::PostObjCMessage, check::PostStmt<ObjCIvarRefExpr>, check::PreStmt<ReturnStmt>, check::PreCall, check::PostCall, check::Location, check::Bind > { … }; } // end anonymous namespace namespace { enum SelfFlagEnum { … }; } REGISTER_MAP_WITH_PROGRAMSTATE(…) REGISTER_TRAIT_WITH_PROGRAMSTATE(…) … /// A call receiving a reference to 'self' invalidates the object that /// 'self' contains. This keeps the "self flags" assigned to the 'self' /// object before the call so we can assign them to the new object that 'self' /// points to after the call. REGISTER_TRAIT_WITH_PROGRAMSTATE(…) static SelfFlagEnum getSelfFlags(SVal val, ProgramStateRef state) { … } static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) { … } static void addSelfFlag(ProgramStateRef state, SVal val, SelfFlagEnum flag, CheckerContext &C) { … } static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C) { … } /// Returns true of the value of the expression is the object that 'self' /// points to and is an object that did not come from the result of calling /// an initializer. static bool isInvalidSelf(const Expr *E, CheckerContext &C) { … } void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C, const char *errorStr) const { … } void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const { … } void ObjCSelfInitChecker::checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const { … } void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { … } // When a call receives a reference to 'self', [Pre/Post]Call pass // the SelfFlags from the object 'self' points to before the call to the new // object after the call. This is to avoid invalidation of 'self' by logging // functions. // Another common pattern in classes with multiple initializers is to put the // subclass's common initialization bits into a static function that receives // the value of 'self', e.g: // @code // if (!(self = [super init])) // return nil; // if (!(self = _commonInit(self))) // return nil; // @endcode // Until we can use inter-procedural analysis, in such a call, transfer the // SelfFlags to the result of the call. void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE, CheckerContext &C) const { … } void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE, CheckerContext &C) const { … } void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad, const Stmt *S, CheckerContext &C) const { … } void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const { … } void ObjCSelfInitChecker::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const { … } // FIXME: A callback should disable checkers at the start of functions. static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) { … } /// Returns true if the location is 'self'. static bool isSelfVar(SVal location, CheckerContext &C) { … } static bool isInitializationMethod(const ObjCMethodDecl *MD) { … } static bool isInitMessage(const ObjCMethodCall &Call) { … } //===----------------------------------------------------------------------===// // Registration. //===----------------------------------------------------------------------===// void ento::registerObjCSelfInitChecker(CheckerManager &mgr) { … } bool ento::shouldRegisterObjCSelfInitChecker(const CheckerManager &mgr) { … }