brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.1 KiB · e40b4f8 Raw
441 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This defines ObjCSelfInitChecker, a builtin check that checks for uses of10// 'self' before proper initialization.11//12//===----------------------------------------------------------------------===//13 14// This checks initialization methods to verify that they assign 'self' to the15// 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 indicate19// 1) if the object is the one pointed to by 'self', and 2) if the object20// 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 an28// obj-c message expression to cut down false positives where logging functions29// get information from self (like its class) or doing "invalidation" on self30// when the initialization fails.31//32// Because the object that 'self' points to gets invalidated when a call33// receives a reference to 'self', the checker keeps track and passes the flags34// 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 48using namespace clang;49using namespace ento;50 51static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND);52static bool isInitializationMethod(const ObjCMethodDecl *MD);53static bool isInitMessage(const ObjCMethodCall &Msg);54static bool isSelfVar(SVal location, CheckerContext &C);55 56namespace {57class 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  const BugType BT{this, "Missing \"self = [(super or self) init...]\"",65                   categories::CoreFoundationObjectiveC};66 67  void checkForInvalidSelf(const Expr *E, CheckerContext &C,68                           const char *errorStr) const;69 70public: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, bool AtDeclInit,77                 CheckerContext &C) const;78 79  void checkPreCall(const CallEvent &CE, CheckerContext &C) const;80  void checkPostCall(const CallEvent &CE, CheckerContext &C) const;81 82  void printState(raw_ostream &Out, ProgramStateRef State,83                  const char *NL, const char *Sep) const override;84};85} // end anonymous namespace86 87namespace {88enum SelfFlagEnum {89  /// No flag set.90  SelfFlag_None = 0x0,91  /// Value came from 'self'.92  SelfFlag_Self    = 0x1,93  /// Value came from the result of an initializer (e.g. [super init]).94  SelfFlag_InitRes = 0x295};96}97 98REGISTER_MAP_WITH_PROGRAMSTATE(SelfFlag, SymbolRef, SelfFlagEnum)99REGISTER_TRAIT_WITH_PROGRAMSTATE(CalledInit, bool)100 101/// A call receiving a reference to 'self' invalidates the object that102/// 'self' contains. This keeps the "self flags" assigned to the 'self'103/// object before the call so we can assign them to the new object that 'self'104/// points to after the call.105REGISTER_TRAIT_WITH_PROGRAMSTATE(PreCallSelfFlags, SelfFlagEnum)106 107static SelfFlagEnum getSelfFlags(SVal val, ProgramStateRef state) {108  if (SymbolRef sym = val.getAsSymbol())109    if (const SelfFlagEnum *attachedFlags = state->get<SelfFlag>(sym))110      return *attachedFlags;111  return SelfFlag_None;112}113 114static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) {115  return getSelfFlags(val, C.getState());116}117 118static void addSelfFlag(ProgramStateRef state, SVal val,119                        SelfFlagEnum flag, CheckerContext &C) {120  // We tag the symbol that the SVal wraps.121  if (SymbolRef sym = val.getAsSymbol()) {122    state = state->set<SelfFlag>(sym,123                                 SelfFlagEnum(getSelfFlags(val, state) | flag));124    C.addTransition(state);125  }126}127 128static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C) {129  return getSelfFlags(val, C) & flag;130}131 132/// Returns true of the value of the expression is the object that 'self'133/// points to and is an object that did not come from the result of calling134/// an initializer.135static bool isInvalidSelf(const Expr *E, CheckerContext &C) {136  SVal exprVal = C.getSVal(E);137  if (!hasSelfFlag(exprVal, SelfFlag_Self, C))138    return false; // value did not come from 'self'.139  if (hasSelfFlag(exprVal, SelfFlag_InitRes, C))140    return false; // 'self' is properly initialized.141 142  return true;143}144 145void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C,146                                              const char *errorStr) const {147  if (!E)148    return;149 150  if (!C.getState()->get<CalledInit>())151    return;152 153  if (!isInvalidSelf(E, C))154    return;155 156  // Generate an error node.157  ExplodedNode *N = C.generateErrorNode();158  if (!N)159    return;160 161  C.emitReport(std::make_unique<PathSensitiveBugReport>(BT, errorStr, N));162}163 164void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,165                                               CheckerContext &C) const {166  // When encountering a message that does initialization (init rule),167  // tag the return value so that we know later on that if self has this value168  // then it is properly initialized.169 170  // FIXME: A callback should disable checkers at the start of functions.171  if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(172                                C.getCurrentAnalysisDeclContext()->getDecl())))173    return;174 175  if (isInitMessage(Msg)) {176    // Tag the return value as the result of an initializer.177    ProgramStateRef state = C.getState();178 179    // FIXME this really should be context sensitive, where we record180    // the current stack frame (for IPA).  Also, we need to clean this181    // value out when we return from this method.182    state = state->set<CalledInit>(true);183 184    SVal V = C.getSVal(Msg.getOriginExpr());185    addSelfFlag(state, V, SelfFlag_InitRes, C);186    return;187  }188 189  // We don't check for an invalid 'self' in an obj-c message expression to cut190  // down false positives where logging functions get information from self191  // (like its class) or doing "invalidation" on self when the initialization192  // fails.193}194 195void ObjCSelfInitChecker::checkPostStmt(const ObjCIvarRefExpr *E,196                                        CheckerContext &C) const {197  // FIXME: A callback should disable checkers at the start of functions.198  if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(199                                 C.getCurrentAnalysisDeclContext()->getDecl())))200    return;201 202  checkForInvalidSelf(203      E->getBase(), C,204      "Instance variable used while 'self' is not set to the result of "205      "'[(super or self) init...]'");206}207 208void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S,209                                       CheckerContext &C) const {210  // FIXME: A callback should disable checkers at the start of functions.211  if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(212                                 C.getCurrentAnalysisDeclContext()->getDecl())))213    return;214 215  checkForInvalidSelf(S->getRetValue(), C,216                      "Returning 'self' while it is not set to the result of "217                      "'[(super or self) init...]'");218}219 220// When a call receives a reference to 'self', [Pre/Post]Call pass221// the SelfFlags from the object 'self' points to before the call to the new222// object after the call. This is to avoid invalidation of 'self' by logging223// functions.224// Another common pattern in classes with multiple initializers is to put the225// subclass's common initialization bits into a static function that receives226// the value of 'self', e.g:227// @code228//   if (!(self = [super init]))229//     return nil;230//   if (!(self = _commonInit(self)))231//     return nil;232// @endcode233// Until we can use inter-procedural analysis, in such a call, transfer the234// SelfFlags to the result of the call.235 236void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE,237                                       CheckerContext &C) const {238  // FIXME: A callback should disable checkers at the start of functions.239  if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(240                                 C.getCurrentAnalysisDeclContext()->getDecl())))241    return;242 243  ProgramStateRef state = C.getState();244  unsigned NumArgs = CE.getNumArgs();245  // If we passed 'self' as and argument to the call, record it in the state246  // to be propagated after the call.247  // Note, we could have just given up, but try to be more optimistic here and248  // assume that the functions are going to continue initialization or will not249  // modify self.250  for (unsigned i = 0; i < NumArgs; ++i) {251    SVal argV = CE.getArgSVal(i);252    if (isSelfVar(argV, C)) {253      SelfFlagEnum selfFlags =254          getSelfFlags(state->getSVal(argV.castAs<Loc>()), C);255      C.addTransition(state->set<PreCallSelfFlags>(selfFlags));256      return;257    } else if (hasSelfFlag(argV, SelfFlag_Self, C)) {258      SelfFlagEnum selfFlags = getSelfFlags(argV, C);259      C.addTransition(state->set<PreCallSelfFlags>(selfFlags));260      return;261    }262  }263}264 265void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE,266                                        CheckerContext &C) const {267  // FIXME: A callback should disable checkers at the start of functions.268  if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(269                                 C.getCurrentAnalysisDeclContext()->getDecl())))270    return;271 272  ProgramStateRef state = C.getState();273  SelfFlagEnum prevFlags = state->get<PreCallSelfFlags>();274  if (!prevFlags)275    return;276  state = state->remove<PreCallSelfFlags>();277 278  unsigned NumArgs = CE.getNumArgs();279  for (unsigned i = 0; i < NumArgs; ++i) {280    SVal argV = CE.getArgSVal(i);281    if (isSelfVar(argV, C)) {282      // If the address of 'self' is being passed to the call, assume that the283      // 'self' after the call will have the same flags.284      // EX: log(&self)285      addSelfFlag(state, state->getSVal(argV.castAs<Loc>()), prevFlags, C);286      return;287    } else if (hasSelfFlag(argV, SelfFlag_Self, C)) {288      // If 'self' is passed to the call by value, assume that the function289      // returns 'self'. So assign the flags, which were set on 'self' to the290      // return value.291      // EX: self = performMoreInitialization(self)292      addSelfFlag(state, CE.getReturnValue(), prevFlags, C);293      return;294    }295  }296 297  C.addTransition(state);298}299 300void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad,301                                        const Stmt *S,302                                        CheckerContext &C) const {303  if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(304        C.getCurrentAnalysisDeclContext()->getDecl())))305    return;306 307  // Tag the result of a load from 'self' so that we can easily know that the308  // value is the object that 'self' points to.309  ProgramStateRef state = C.getState();310  if (isSelfVar(location, C))311    addSelfFlag(state, state->getSVal(location.castAs<Loc>()), SelfFlag_Self,312                C);313}314 315void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S,316                                    bool AtDeclInit, CheckerContext &C) const {317  // Allow assignment of anything to self. Self is a local variable in the318  // initializer, so it is legal to assign anything to it, like results of319  // static functions/method calls. After self is assigned something we cannot320  // reason about, stop enforcing the rules.321  // (Only continue checking if the assigned value should be treated as self.)322  if ((isSelfVar(loc, C)) &&323      !hasSelfFlag(val, SelfFlag_InitRes, C) &&324      !hasSelfFlag(val, SelfFlag_Self, C) &&325      !isSelfVar(val, C)) {326 327    // Stop tracking the checker-specific state in the state.328    ProgramStateRef State = C.getState();329    State = State->remove<CalledInit>();330    if (SymbolRef sym = loc.getAsSymbol())331      State = State->remove<SelfFlag>(sym);332    C.addTransition(State);333  }334}335 336void ObjCSelfInitChecker::printState(raw_ostream &Out, ProgramStateRef State,337                                     const char *NL, const char *Sep) const {338  SelfFlagTy FlagMap = State->get<SelfFlag>();339  bool DidCallInit = State->get<CalledInit>();340  SelfFlagEnum PreCallFlags = State->get<PreCallSelfFlags>();341 342  if (FlagMap.isEmpty() && !DidCallInit && !PreCallFlags)343    return;344 345  Out << Sep << NL << "ObjCSelfInitChecker:" << NL;346 347  if (DidCallInit)348    Out << "  An init method has been called." << NL;349 350  if (PreCallFlags != SelfFlag_None) {351    if (PreCallFlags & SelfFlag_Self) {352      Out << "  An argument of the current call came from the 'self' variable."353          << NL;354    }355    if (PreCallFlags & SelfFlag_InitRes) {356      Out << "  An argument of the current call came from an init method."357          << NL;358    }359  }360 361  Out << NL;362  for (auto [Sym, Flag] : FlagMap) {363    Out << Sym << " : ";364 365    if (Flag == SelfFlag_None)366      Out << "none";367 368    if (Flag & SelfFlag_Self)369      Out << "self variable";370 371    if (Flag & SelfFlag_InitRes) {372      if (Flag != SelfFlag_InitRes)373        Out << " | ";374      Out << "result of init method";375    }376 377    Out << NL;378  }379}380 381 382// FIXME: A callback should disable checkers at the start of functions.383static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) {384  if (!ND)385    return false;386 387  const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ND);388  if (!MD)389    return false;390  if (!isInitializationMethod(MD))391    return false;392 393  // self = [super init] applies only to NSObject subclasses.394  // For instance, NSProxy doesn't implement -init.395  ASTContext &Ctx = MD->getASTContext();396  IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject");397  ObjCInterfaceDecl *ID = MD->getClassInterface()->getSuperClass();398  for ( ; ID ; ID = ID->getSuperClass()) {399    IdentifierInfo *II = ID->getIdentifier();400 401    if (II == NSObjectII)402      break;403  }404  return ID != nullptr;405}406 407/// Returns true if the location is 'self'.408static bool isSelfVar(SVal location, CheckerContext &C) {409  AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext();410  if (!analCtx->getSelfDecl())411    return false;412  if (!isa<loc::MemRegionVal>(location))413    return false;414 415  loc::MemRegionVal MRV = location.castAs<loc::MemRegionVal>();416  if (const DeclRegion *DR = dyn_cast<DeclRegion>(MRV.stripCasts()))417    return (DR->getDecl() == analCtx->getSelfDecl());418 419  return false;420}421 422static bool isInitializationMethod(const ObjCMethodDecl *MD) {423  return MD->getMethodFamily() == OMF_init;424}425 426static bool isInitMessage(const ObjCMethodCall &Call) {427  return Call.getMethodFamily() == OMF_init;428}429 430//===----------------------------------------------------------------------===//431// Registration.432//===----------------------------------------------------------------------===//433 434void ento::registerObjCSelfInitChecker(CheckerManager &mgr) {435  mgr.registerChecker<ObjCSelfInitChecker>();436}437 438bool ento::shouldRegisterObjCSelfInitChecker(const CheckerManager &mgr) {439  return true;440}441