void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
                                        CheckerContext &C) const {
  const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
  if (!ID)
    return;
  
  if (findKnownClass(ID) == FC_NSString) {
    Selector S = msg.getSelector();
    
    if (S.isUnarySelector())
      return;
    
    // FIXME: This is going to be really slow doing these checks with
    //  lexical comparisons.
    
    std::string NameStr = S.getAsString();
    StringRef Name(NameStr);
    assert(!Name.empty());
    
    // FIXME: Checking for initWithFormat: will not work in most cases
    //  yet because [NSString alloc] returns id, not NSString*.  We will
    //  need support for tracking expected-type information in the analyzer
    //  to find these errors.
    if (Name == "caseInsensitiveCompare:" ||
        Name == "compare:" ||
        Name == "compare:options:" ||
        Name == "compare:options:range:" ||
        Name == "compare:options:range:locale:" ||
        Name == "componentsSeparatedByCharactersInSet:" ||
        Name == "initWithFormat:") {
      if (isNil(msg.getArgSVal(0)))
        WarnNilArg(C, msg, 0);
    }
  }
}
void NilArgChecker::warnIfNilArg(CheckerContext &C,
                                 const ObjCMethodCall &msg,
                                 unsigned int Arg,
                                 FoundationClass Class,
                                 bool CanBeSubscript) const {
  // Check if the argument is nil.
  ProgramStateRef State = C.getState();
  if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
      return;

  // NOTE: We cannot throw non-fatal errors from warnIfNilExpr,
  // because it's called multiple times from some callers, so it'd cause
  // an unwanted state split if two or more non-fatal errors are thrown
  // within the same checker callback. For now we don't want to, but
  // it'll need to be fixed if we ever want to.
  if (ExplodedNode *N = C.generateErrorNode()) {
    SmallString<128> sbuf;
    llvm::raw_svector_ostream os(sbuf);

    if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {

      if (Class == FC_NSArray) {
        os << "Array element cannot be nil";
      } else if (Class == FC_NSDictionary) {
        if (Arg == 0) {
          os << "Value stored into '";
          os << GetReceiverInterfaceName(msg) << "' cannot be nil";
        } else {
          assert(Arg == 1);
          os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
        }
      } else
        llvm_unreachable("Missing foundation class for the subscript expr");

    } else {
      if (Class == FC_NSDictionary) {
        if (Arg == 0)
          os << "Value argument ";
        else {
          assert(Arg == 1);
          os << "Key argument ";
        }
        os << "to '";
        msg.getSelector().print(os);
        os << "' cannot be nil";
      } else {
        os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '";
        msg.getSelector().print(os);
        os << "' cannot be nil";
      }
    }

    generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
                      msg.getArgExpr(Arg), C);
  }
}
void NilArgChecker::WarnIfNilArg(CheckerContext &C,
                                 const ObjCMethodCall &msg,
                                 unsigned int Arg,
                                 FoundationClass Class,
                                 bool CanBeSubscript) const {
  // Check if the argument is nil.
  ProgramStateRef State = C.getState();
  if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
      return;
      
  if (!BT)
    BT.reset(new APIMisuse("nil argument"));

  if (ExplodedNode *N = C.generateSink()) {
    SmallString<128> sbuf;
    llvm::raw_svector_ostream os(sbuf);

    if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {

      if (Class == FC_NSArray) {
        os << "Array element cannot be nil";
      } else if (Class == FC_NSDictionary) {
        if (Arg == 0) {
          os << "Value stored into '";
          os << GetReceiverInterfaceName(msg) << "' cannot be nil";
        } else {
          assert(Arg == 1);
          os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
        }
      } else
        llvm_unreachable("Missing foundation class for the subscript expr");

    } else {
      if (Class == FC_NSDictionary) {
        if (Arg == 0)
          os << "Value argument ";
        else {
          assert(Arg == 1);
          os << "Key argument ";
        }
        os << "to '" << msg.getSelector().getAsString() << "' cannot be nil";
      } else {
        os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
        << msg.getSelector().getAsString() << "' cannot be nil";
      }
    }

    BugReport *R = new BugReport(*BT, os.str(), N);
    R->addRange(msg.getArgSourceRange(Arg));
    bugreporter::trackNullOrUndefValue(N, msg.getArgExpr(Arg), *R);
    C.emitReport(R);
  }
}
void NilArgChecker::warnIfNilArg(CheckerContext &C,
                                 const ObjCMethodCall &msg,
                                 unsigned int Arg,
                                 FoundationClass Class,
                                 bool CanBeSubscript) const {
  // Check if the argument is nil.
  ProgramStateRef State = C.getState();
  if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
      return;

  if (ExplodedNode *N = C.generateErrorNode()) {
    SmallString<128> sbuf;
    llvm::raw_svector_ostream os(sbuf);

    if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {

      if (Class == FC_NSArray) {
        os << "Array element cannot be nil";
      } else if (Class == FC_NSDictionary) {
        if (Arg == 0) {
          os << "Value stored into '";
          os << GetReceiverInterfaceName(msg) << "' cannot be nil";
        } else {
          assert(Arg == 1);
          os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
        }
      } else
        llvm_unreachable("Missing foundation class for the subscript expr");

    } else {
      if (Class == FC_NSDictionary) {
        if (Arg == 0)
          os << "Value argument ";
        else {
          assert(Arg == 1);
          os << "Key argument ";
        }
        os << "to '";
        msg.getSelector().print(os);
        os << "' cannot be nil";
      } else {
        os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '";
        msg.getSelector().print(os);
        os << "' cannot be nil";
      }
    }

    generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
                      msg.getArgExpr(Arg), C);
  }
}
Beispiel #5
0
/// Returns the released value if M is a call a setter that releases
/// and nils out its underlying instance variable.
SymbolRef
ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M,
                                                 CheckerContext &C) const {
  SVal ReceiverVal = M.getReceiverSVal();
  if (!ReceiverVal.isValid())
    return nullptr;

  if (M.getNumArgs() == 0)
    return nullptr;

  if (!M.getArgExpr(0)->getType()->isObjCRetainableType())
    return nullptr;

  // Is the first argument nil?
  SVal Arg = M.getArgSVal(0);
  ProgramStateRef notNilState, nilState;
  std::tie(notNilState, nilState) =
      M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>());
  if (!(nilState && !notNilState))
    return nullptr;

  const ObjCPropertyDecl *Prop = M.getAccessedProperty();
  if (!Prop)
    return nullptr;

  ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl();
  if (!PropIvarDecl)
    return nullptr;

  ProgramStateRef State = C.getState();

  SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
  Optional<Loc> LValLoc = LVal.getAs<Loc>();
  if (!LValLoc)
    return nullptr;

  SVal CurrentValInIvar = State->getSVal(LValLoc.getValue());
  return CurrentValInIvar.getAsSymbol();
}
void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
                                                    CheckerContext &C) const {
  if (!BT) {
    BT.reset(new APIMisuse(this,
                           "Arguments passed to variadic method aren't all "
                           "Objective-C pointer types"));

    ASTContext &Ctx = C.getASTContext();
    arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
    dictionaryWithObjectsAndKeysS =
      GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
    setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
    orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);

    initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
    initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
  }

  if (!isVariadicMessage(msg))
      return;

  // We are not interested in the selector arguments since they have
  // well-defined types, so the compiler will issue a warning for them.
  unsigned variadicArgsBegin = msg.getSelector().getNumArgs();

  // We're not interested in the last argument since it has to be nil or the
  // compiler would have issued a warning for it elsewhere.
  unsigned variadicArgsEnd = msg.getNumArgs() - 1;

  if (variadicArgsEnd <= variadicArgsBegin)
    return;

  // Verify that all arguments have Objective-C types.
  Optional<ExplodedNode*> errorNode;

  for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
    QualType ArgTy = msg.getArgExpr(I)->getType();
    if (ArgTy->isObjCObjectPointerType())
      continue;

    // Block pointers are treaded as Objective-C pointers.
    if (ArgTy->isBlockPointerType())
      continue;

    // Ignore pointer constants.
    if (msg.getArgSVal(I).getAs<loc::ConcreteInt>())
      continue;

    // Ignore pointer types annotated with 'NSObject' attribute.
    if (C.getASTContext().isObjCNSObjectType(ArgTy))
      continue;

    // Ignore CF references, which can be toll-free bridged.
    if (coreFoundation::isCFObjectRef(ArgTy))
      continue;

    // Generate only one error node to use for all bug reports.
    if (!errorNode.hasValue())
      errorNode = C.generateNonFatalErrorNode();

    if (!errorNode.getValue())
      continue;

    SmallString<128> sbuf;
    llvm::raw_svector_ostream os(sbuf);

    StringRef TypeName = GetReceiverInterfaceName(msg);
    if (!TypeName.empty())
      os << "Argument to '" << TypeName << "' method '";
    else
      os << "Argument to method '";

    msg.getSelector().print(os);
    os << "' should be an Objective-C pointer type, not '";
    ArgTy.print(os, C.getLangOpts());
    os << "'";

    auto R = llvm::make_unique<BugReport>(*BT, os.str(), errorNode.getValue());
    R->addRange(msg.getArgSourceRange(I));
    C.emitReport(std::move(R));
  }
}
/// When the receiver has a tracked type, use that type to validate the
/// argumments of the message expression and the return value.
void DynamicTypePropagation::checkPreObjCMessage(const ObjCMethodCall &M,
                                                 CheckerContext &C) const {
  ProgramStateRef State = C.getState();
  SymbolRef Sym = M.getReceiverSVal().getAsSymbol();
  if (!Sym)
    return;

  const ObjCObjectPointerType *const *TrackedType =
      State->get<MostSpecializedTypeArgsMap>(Sym);
  if (!TrackedType)
    return;

  // Get the type arguments from tracked type and substitute type arguments
  // before do the semantic check.

  ASTContext &ASTCtxt = C.getASTContext();
  const ObjCMessageExpr *MessageExpr = M.getOriginExpr();
  const ObjCMethodDecl *Method =
      findMethodDecl(MessageExpr, *TrackedType, ASTCtxt);

  // It is possible to call non-existent methods in Obj-C.
  if (!Method)
    return;

  Optional<ArrayRef<QualType>> TypeArgs =
      (*TrackedType)->getObjCSubstitutions(Method->getDeclContext());
  // This case might happen when there is an unspecialized override of a
  // specialized method.
  if (!TypeArgs)
    return;

  for (unsigned i = 0; i < Method->param_size(); i++) {
    const Expr *Arg = MessageExpr->getArg(i);
    const ParmVarDecl *Param = Method->parameters()[i];

    QualType OrigParamType = Param->getType();
    if (!isObjCTypeParamDependent(OrigParamType))
      continue;

    QualType ParamType = OrigParamType.substObjCTypeArgs(
        ASTCtxt, *TypeArgs, ObjCSubstitutionContext::Parameter);
    // Check if it can be assigned
    const auto *ParamObjectPtrType = ParamType->getAs<ObjCObjectPointerType>();
    const auto *ArgObjectPtrType =
        stripCastsAndSugar(Arg)->getType()->getAs<ObjCObjectPointerType>();
    if (!ParamObjectPtrType || !ArgObjectPtrType)
      continue;

    // Check if we have more concrete tracked type that is not a super type of
    // the static argument type.
    SVal ArgSVal = M.getArgSVal(i);
    SymbolRef ArgSym = ArgSVal.getAsSymbol();
    if (ArgSym) {
      const ObjCObjectPointerType *const *TrackedArgType =
          State->get<MostSpecializedTypeArgsMap>(ArgSym);
      if (TrackedArgType &&
          ASTCtxt.canAssignObjCInterfaces(ArgObjectPtrType, *TrackedArgType)) {
        ArgObjectPtrType = *TrackedArgType;
      }
    }

    // Warn when argument is incompatible with the parameter.
    if (!ASTCtxt.canAssignObjCInterfaces(ParamObjectPtrType,
                                         ArgObjectPtrType)) {
      static CheckerProgramPointTag Tag(this, "ArgTypeMismatch");
      ExplodedNode *N = C.addTransition(State, &Tag);
      reportGenericsBug(ArgObjectPtrType, ParamObjectPtrType, N, Sym, C, Arg);
      return;
    }
  }
}
  void DanglingDelegateChecker::checkPostObjCMessage(const ObjCMethodCall &message, CheckerContext &context) const {
    // if the call was inlined, there is nothing else to do
    if (context.wasInlined) {
      return;
    }

    const ObjCMessageExpr *expr = message.getOriginExpr();
    if (!expr) {
      assert(false);
      return;
    }

    // want an instance message to a non-null receiver
    const Expr *receiver = expr->getInstanceReceiver();
    if (!receiver) {
      return;
    }
    if (isKnownToBeNil(message.getReceiverSVal(), context)) {
      // we are sure that the receiver is nil => abort mission
      return;
    }

    // retrieves the static facts on ivars
    const ObjCImplFacts *facts = getCurrentFacts(getCurrentTopClassInterface(context));
    if (!facts) {
      return;
    }

    // First we try to detect the setting of an interesting property of self
    if (isObjCSelfExpr(receiver)) {
      const ObjCPropertyDecl *propDecl = matchObjCMessageWithPropertySetter(*expr);
      if (propDecl) {
        // To mitigate false positives, we verify only setters that have an unknown body.
        // (Setters with a known body are unfortunately not always inlined.)
        RuntimeDefinition runtimeDefinition = message.getRuntimeDefinition();
        if (!runtimeDefinition.getDecl() || runtimeDefinition.getDecl()->isImplicit()) {
          verifyIvarDynamicStateAgainstStaticFacts(*expr, propDecl->getPropertyIvarDecl(), context);
        }

        // Next we deal with a possible assignment self.x = nil to prevent further warning
        const ObjCIvarDecl *ivarDecl = propDecl->getPropertyIvarDecl();
        if (ivarDecl && facts->_ivarFactsMap.find(ivarDecl) != facts->_ivarFactsMap.end()) {
          SVal value = message.getArgSVal(0);
          if (isKnownToBeNil(value, context)) {
            // mark the corresponding ivar as cleared
            ProgramStateRef state = context.getState();
            IvarDynamicState clearedStateForIvar(facts->_ivarFactsMap.at(ivarDecl));
            state = state->set<IvarMap>(ivarDecl, clearedStateForIvar);
            context.addTransition(state);
          }
        }

        return;
      }
    }

    // What follows detects when we correctly clear the references inside an ivar
    // This is dual to FactFinder::VisitObjCMessageExpr

    StringRef selectorStr = expr->getSelector().getAsString();
    // do we have a first argument equal to self?
    bool paramIsSelf = isObjCSelfExpr(getArgOfObjCMessageExpr(*expr, 0));

    // is the receiver an interesting ivar?
    const ObjCIvarDecl *ivarDecl = matchIvarLValueExpression(*receiver);
    if (ivarDecl && facts->_ivarFactsMap.find(ivarDecl) != facts->_ivarFactsMap.end()) {

      // is this a release?
      if (selectorStr == "release" || selectorStr == "autorelease") {
        assert(!paramIsSelf);
        verifyIvarDynamicStateAgainstStaticFacts(*expr, ivarDecl, context);
        return;
      }

      // Prepare a new state to modify, associated with the receiver
      ProgramStateRef state = context.getState();
      // Copy the previous state if present
      IvarDynamicState ivarState(state->get<IvarMap>(ivarDecl));

      // is this a setter of an assign property?
      const ObjCPropertyDecl *propDecl = matchObjCMessageWithPropertySetter(*expr);
      if (propDecl) {
        if (propDecl->getSetterKind() != ObjCPropertyDecl::Assign) {
          return;
        }
        std::string propName = propDecl->getNameAsString();
        if (!paramIsSelf) {
          // the property is now considered cleared
          ivarState._assignPropertyWasCleared.insert(propName);
        } else {
          // "unclear" the property since we just stored self again in it
          ivarState._assignPropertyWasCleared.erase(propName);
        }

      } else if (paramIsSelf && selectorStr.startswith("removeTarget:")) {
        ivarState._targetWasCleared = true;
      } else if (paramIsSelf && selectorStr.startswith("removeObserver:")) {
        ivarState._observerWasCleared = true;
      } else {
        // return to avoid transitioning to a new identical state
        return;
      }

      // write the new state
      state = state->set<IvarMap>(ivarDecl, ivarState);
      context.addTransition(state);
      return;
    }

    // TODO: is the receiver an interesting "observable singleton object"?
//    string receiverObjectName = matchObservableSingletonObject(receiver);
//    if (!receiverObjectName.empty()) {
//
//      if (paramIsSelf && selectorStr.startswith("addObserver:")) {
//        // TODO
//      }
//
//    }
  }