Beispiel #1
0
/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is
/// a release or a nilling-out property setter.
void ObjCDeallocChecker::checkPreObjCMessage(
    const ObjCMethodCall &M, CheckerContext &C) const {
  // Only run if -dealloc is on the stack.
  SVal DeallocedInstance;
  if (!instanceDeallocIsOnStack(C, DeallocedInstance))
    return;

  SymbolRef ReleasedValue = nullptr;

  if (M.getSelector() == ReleaseSel) {
    ReleasedValue = M.getReceiverSVal().getAsSymbol();
  } else if (M.getSelector() == DeallocSel && !M.isReceiverSelfOrSuper()) {
    if (diagnoseMistakenDealloc(M.getReceiverSVal().getAsSymbol(), M, C))
      return;
  }

  if (ReleasedValue) {
    // An instance variable symbol was released with -release:
    //    [_property release];
    if (diagnoseExtraRelease(ReleasedValue,M, C))
      return;
  } else {
    // An instance variable symbol was released nilling out its property:
    //    self.property = nil;
    ReleasedValue = getValueReleasedByNillingOut(M, C);
  }

  if (!ReleasedValue)
    return;

  transitionToReleaseValue(C, ReleasedValue);
}
void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M,
        CheckerContext &C) const {
    if (!M.isInstanceMessage())
        return;

    const ObjCInterfaceDecl *ClassID = M.getReceiverInterface();
    if (!ClassID)
        return;

    FoundationClass Class = findKnownClass(ClassID);
    if (Class != FC_NSDictionary &&
            Class != FC_NSArray &&
            Class != FC_NSSet)
        return;

    SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol();
    if (!ContainerS)
        return;

    // If we are processing a call to "count", get the symbolic value returned by
    // a call to "count" and add it to the map.
    if (!isCollectionCountMethod(M, C))
        return;

    const Expr *MsgExpr = M.getOriginExpr();
    SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol();
    if (CountS) {
        ProgramStateRef State = C.getState();
        C.getSymbolManager().addSymbolDependency(ContainerS, CountS);
        State = State->set<ContainerCountMap>(ContainerS, CountS);
        C.addTransition(State);
    }
    return;
}
static Nullability getReceiverNullability(const ObjCMethodCall &M,
                                          ProgramStateRef State) {
  if (M.isReceiverSelfOrSuper()) {
    // For super and super class receivers we assume that the receiver is
    // nonnull.
    return Nullability::Nonnull;
  }
  // Otherwise look up nullability in the state.
  SVal Receiver = M.getReceiverSVal();
  if (auto DefOrUnknown = Receiver.getAs<DefinedOrUnknownSVal>()) {
    // If the receiver is constrained to be nonnull, assume that it is nonnull
    // regardless of its type.
    NullConstraint Nullness = getNullConstraint(*DefOrUnknown, State);
    if (Nullness == NullConstraint::IsNotNull)
      return Nullability::Nonnull;
  }
  auto ValueRegionSVal = Receiver.getAs<loc::MemRegionVal>();
  if (ValueRegionSVal) {
    const MemRegion *SelfRegion = ValueRegionSVal->getRegion();
    assert(SelfRegion);

    const NullabilityState *TrackedSelfNullability =
        State->get<NullabilityMap>(SelfRegion);
    if (TrackedSelfNullability)
      return TrackedSelfNullability->getValue();
  }
  return Nullability::Unspecified;
}
void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M,
                                                  CheckerContext &C) const {

  ProgramStateRef State = C.getState();
  SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol();
  if (!ReceiverSymbol) {
    diagnoseCallArguments(M, C);
    return;
  }

  bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol);
  if (!AlreadyCalled)
    return;

  StringRef Desc;

  if (isSuperDeallocMessage(M)) {
    Desc = "[super dealloc] should not be called multiple times";
  } else {
    Desc = StringRef();
  }

  reportUseAfterDealloc(ReceiverSymbol, Desc, M.getOriginExpr(), C);

  return;
}
Beispiel #5
0
void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
                                                CheckerContext &C) const {
  SVal recVal = msg.getReceiverSVal();
  if (recVal.isUndef()) {
    if (ExplodedNode *N = C.generateSink()) {
      BugType *BT = 0;
      switch (msg.getMessageKind()) {
      case OCM_Message:
        if (!BT_msg_undef)
          BT_msg_undef.reset(new BuiltinBug("Receiver in message expression "
                                            "is an uninitialized value"));
        BT = BT_msg_undef.get();
        break;
      case OCM_PropertyAccess:
        if (!BT_objc_prop_undef)
          BT_objc_prop_undef.reset(new BuiltinBug("Property access on an "
                                                  "uninitialized object "
                                                  "pointer"));
        BT = BT_objc_prop_undef.get();
        break;
      case OCM_Subscript:
        if (!BT_objc_subscript_undef)
          BT_objc_subscript_undef.reset(new BuiltinBug("Subscript access on an "
                                                       "uninitialized object "
                                                       "pointer"));
        BT = BT_objc_subscript_undef.get();
        break;
      }
      assert(BT && "Unknown message kind.");

      BugReport *R = new BugReport(*BT, BT->getName(), N);
      const ObjCMessageExpr *ME = msg.getOriginExpr();
      R->addRange(ME->getReceiverRange());

      // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
      if (const Expr *ReceiverE = ME->getInstanceReceiver())
        R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
                                                                   ReceiverE,
                                                                   R));
      C.EmitReport(R);
    }
    return;
  } else {
    // Bifurcate the state into nil and non-nil ones.
    DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);

    ProgramStateRef state = C.getState();
    ProgramStateRef notNilState, nilState;
    llvm::tie(notNilState, nilState) = state->assume(receiverVal);

    // Handle receiver must be nil.
    if (nilState && !notNilState) {
      HandleNilReceiver(C, state, msg);
      return;
    }
  }
}
void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
                                                CheckerContext &C) const {
  SVal recVal = msg.getReceiverSVal();
  if (recVal.isUndef()) {
    if (ExplodedNode *N = C.generateErrorNode()) {
      BugType *BT = nullptr;
      switch (msg.getMessageKind()) {
      case OCM_Message:
        if (!BT_msg_undef)
          BT_msg_undef.reset(new BuiltinBug(this,
                                            "Receiver in message expression "
                                            "is an uninitialized value"));
        BT = BT_msg_undef.get();
        break;
      case OCM_PropertyAccess:
        if (!BT_objc_prop_undef)
          BT_objc_prop_undef.reset(new BuiltinBug(
              this, "Property access on an uninitialized object pointer"));
        BT = BT_objc_prop_undef.get();
        break;
      case OCM_Subscript:
        if (!BT_objc_subscript_undef)
          BT_objc_subscript_undef.reset(new BuiltinBug(
              this, "Subscript access on an uninitialized object pointer"));
        BT = BT_objc_subscript_undef.get();
        break;
      }
      assert(BT && "Unknown message kind.");

      auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
      const ObjCMessageExpr *ME = msg.getOriginExpr();
      R->addRange(ME->getReceiverRange());

      // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
      if (const Expr *ReceiverE = ME->getInstanceReceiver())
        bugreporter::trackExpressionValue(N, ReceiverE, *R);
      C.emitReport(std::move(R));
    }
    return;
  }
}
Beispiel #7
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();
}
/// This callback is used to infer the types for Class variables. This info is
/// used later to validate messages that sent to classes. Class variables are
/// initialized with by invoking the 'class' method on a class.
/// This method is also used to infer the type information for the return
/// types.
// TODO: right now it only tracks generic types. Extend this to track every
// type in the DynamicTypeMap and diagnose type errors!
void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M,
                                                  CheckerContext &C) const {
  const ObjCMessageExpr *MessageExpr = M.getOriginExpr();

  SymbolRef RetSym = M.getReturnValue().getAsSymbol();
  if (!RetSym)
    return;

  Selector Sel = MessageExpr->getSelector();
  ProgramStateRef State = C.getState();
  // Inference for class variables.
  // We are only interested in cases where the class method is invoked on a
  // class. This method is provided by the runtime and available on all classes.
  if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Class &&
      Sel.getAsString() == "class") {
    QualType ReceiverType = MessageExpr->getClassReceiver();
    const auto *ReceiverClassType = ReceiverType->getAs<ObjCObjectType>();
    QualType ReceiverClassPointerType =
        C.getASTContext().getObjCObjectPointerType(
            QualType(ReceiverClassType, 0));

    if (!ReceiverClassType->isSpecialized())
      return;
    const auto *InferredType =
        ReceiverClassPointerType->getAs<ObjCObjectPointerType>();
    assert(InferredType);

    State = State->set<MostSpecializedTypeArgsMap>(RetSym, InferredType);
    C.addTransition(State);
    return;
  }

  // Tracking for return types.
  SymbolRef RecSym = M.getReceiverSVal().getAsSymbol();
  if (!RecSym)
    return;

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

  ASTContext &ASTCtxt = C.getASTContext();
  const ObjCMethodDecl *Method =
      findMethodDecl(MessageExpr, *TrackedType, ASTCtxt);
  if (!Method)
    return;

  Optional<ArrayRef<QualType>> TypeArgs =
      (*TrackedType)->getObjCSubstitutions(Method->getDeclContext());
  if (!TypeArgs)
    return;

  QualType ResultType =
      getReturnTypeForMethod(Method, *TypeArgs, *TrackedType, ASTCtxt);
  // The static type is the same as the deduced type.
  if (ResultType.isNull())
    return;

  const MemRegion *RetRegion = M.getReturnValue().getAsRegion();
  ExplodedNode *Pred = C.getPredecessor();
  // When there is an entry available for the return symbol in DynamicTypeMap,
  // the call was inlined, and the information in the DynamicTypeMap is should
  // be precise.
  if (RetRegion && !State->get<DynamicTypeMap>(RetRegion)) {
    // TODO: we have duplicated information in DynamicTypeMap and
    // MostSpecializedTypeArgsMap. We should only store anything in the later if
    // the stored data differs from the one stored in the former.
    State = setDynamicTypeInfo(State, RetRegion, ResultType,
                               /*CanBeSubclass=*/true);
    Pred = C.addTransition(State);
  }

  const auto *ResultPtrType = ResultType->getAs<ObjCObjectPointerType>();

  if (!ResultPtrType || ResultPtrType->isUnspecialized())
    return;

  // When the result is a specialized type and it is not tracked yet, track it
  // for the result symbol.
  if (!State->get<MostSpecializedTypeArgsMap>(RetSym)) {
    State = State->set<MostSpecializedTypeArgsMap>(RetSym, ResultPtrType);
    C.addTransition(State, Pred);
  }
}
/// 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;
    }
  }
}
//FIXME: Consider other methods than setObject like dictionaryWithObjectsAndKeys
/// Process call to NSMutableArray:setObject:forKey: 
void iOSAppSecInsecureKeyChainStorageChecker::checkPreObjCMessage 
  (const ObjCMethodCall &M, CheckerContext &C) const
{
  MSEC_DEBUG_FUNC("redwud:","ENTER") ;

  do
  {
    const ObjCInterfaceDecl *pRxInterface = M.getReceiverInterface() ;
    
    if ( !pRxInterface )
    {
      break ;
    }

    ASTContext &Ctx = C.getASTContext() ;
    Selector selCurr = M.getSelector() ; 

    initIdentifierInfo( Ctx ) ;

    //TODO: Check this with property, this might not work on it    
    //NSMutableDictionary 
    if ( pRxInterface ->getIdentifier() != m_piiNSMutableDictionary )
    {
      break ;
    } 

    //setObject 
    IdentifierInfo *piiSetObject = selCurr.getIdentifierInfoForSlot(0) ;  

    if ( piiSetObject != m_piiSetObject )
    {
       break ;
    }
    
    //forKey
    IdentifierInfo *piiForKey = selCurr.getIdentifierInfoForSlot(1) ;

    if ( piiForKey != m_piiForKey )
    {
       break ;
    }
    
    // MSEC_DEBUG("redwud: ", "'" << selCurr.getAsString() << "' num args: " << selCurr.getNumArgs() ) ;
    if ( selCurr.getNumArgs() != 2 )
    {
      // Unlikely to be of concerned 
      break ;
    }

    ProgramStateRef pProgState = C.getState() ;
    const LocationContext *pLCtx = C.getLocationContext() ; 

    //Get the value for "aKey" parameter (2nd)
    // Checking this first because checking the first parameter takes a bit longer    
    const Expr *pKeyExpr = M.getArgExpr(1) ;
    SVal argValKey = pProgState ->getSVal( pKeyExpr, pLCtx ) ;

    if ( !CMSecCommon::isSValContains( argValKey, "kSecAttrAccessible" ) )
    {
      // Not of concern
      break ; 
    }

    //Get the value for "anObject" parameter (1st)
    const Expr *pObjExpr = M.getArgExpr(0) ;
    SVal argValAnObject = pProgState ->getSVal( pObjExpr, pLCtx ) ;

    //Get receiver as symbol, should be used in either condition
    SymbolRef pSymQuery = M.getReceiverSVal().getAsSymbol() ;

    if ( !pSymQuery )
    {
      // redwud: Can't save empty receiver symbol,
      // so there is no point of moving on, 
      // there must be something wrong with this
      break ;
    }

    //Idea: if [query] is currently being tracked change it to different status, e.g. secure
    //      if not tracked add new secure state

    bool bInsecureObject = isInsecureObject( argValAnObject ) ; 

    pProgState = pProgState ->set <StreamMap>( pSymQuery, bInsecureObject ? 
      KeyChainState::getNotSecure() : KeyChainState::getSecure() ) ;   

    // Add transition of state
    //redwud: it seems that the states are transitioned at some point
    C.addTransition( pProgState ) ;

    MSEC_DEBUG( "redwud: ", "Finish checking!" ) ; 
  } while (_PASSING_) ;


  MSEC_DEBUG_FUNC("redwud:","EXIT") ;
}
  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
//      }
//
//    }
  }