void analyze_instruction()

in libredex/ReflectionAnalysis.cpp [477:786]


  void analyze_instruction(
      const IRInstruction* insn,
      AbstractObjectEnvironment* current_state) const override {
    AbstractObjectDomain callee_return;
    callee_return.set_to_bottom();
    if (opcode::is_an_invoke(insn->opcode())) {
      CallingContext cc;
      auto srcs = insn->srcs();
      for (param_index_t i = 0; i < srcs.size(); i++) {
        reg_t src = insn->src(i);
        auto aobj = current_state->get_abstract_obj(src);
        cc.set(i, aobj);
      }
      if (!cc.is_bottom()) {
        current_state->set_calling_context(insn, cc);
      }

      if (m_summary_query_fn) {
        callee_return = (*m_summary_query_fn)(insn);
      }
    }

    switch (insn->opcode()) {
    case IOPCODE_LOAD_PARAM:
    case IOPCODE_LOAD_PARAM_OBJECT:
    case IOPCODE_LOAD_PARAM_WIDE: {
      // IOPCODE_LOAD_PARAM_* instructions have been processed before the
      // analysis.
      break;
    }
    case OPCODE_MOVE:
    case OPCODE_MOVE_OBJECT: {
      const auto aobj = current_state->get_abstract_obj(insn->src(0));
      current_state->set_abstract_obj(insn->dest(), aobj);
      const auto obj = aobj.get_object();
      if (obj && obj->obj_kind == AbstractObjectKind::CLASS) {
        current_state->set_class_source(
            insn->dest(), current_state->get_class_source(insn->src(0)));
      }
      break;
    }
    case IOPCODE_MOVE_RESULT_PSEUDO_OBJECT:
    case OPCODE_MOVE_RESULT_OBJECT: {
      const auto aobj = current_state->get_abstract_obj(RESULT_REGISTER);
      current_state->set_abstract_obj(insn->dest(), aobj);
      const auto obj = aobj.get_object();
      if (obj && obj->obj_kind == AbstractObjectKind::CLASS) {
        current_state->set_class_source(
            insn->dest(), current_state->get_class_source(RESULT_REGISTER));
      }
      break;
    }
    case OPCODE_CONST: {
      current_state->set_abstract_obj(
          insn->dest(),
          AbstractObjectDomain(AbstractObject(insn->get_literal())));
      break;
    }
    case OPCODE_CONST_STRING: {
      current_state->set_abstract_obj(
          RESULT_REGISTER,
          AbstractObjectDomain(AbstractObject(insn->get_string())));
      break;
    }
    case OPCODE_CONST_CLASS: {
      auto aobj = AbstractObject(AbstractObjectKind::CLASS, insn->get_type());
      current_state->set_abstract_obj(RESULT_REGISTER,
                                      AbstractObjectDomain(aobj));
      current_state->set_class_source(
          RESULT_REGISTER,
          ClassObjectSourceDomain(ClassObjectSource::REFLECTION));
      break;
    }
    case OPCODE_CHECK_CAST: {
      const auto aobj = current_state->get_abstract_obj(insn->src(0));
      current_state->set_abstract_obj(
          RESULT_REGISTER,
          AbstractObjectDomain(
              AbstractObject(AbstractObjectKind::OBJECT, insn->get_type())));
      const auto obj = aobj.get_object();
      if (obj && obj->obj_kind == AbstractObjectKind::CLASS) {
        current_state->set_class_source(
            RESULT_REGISTER, current_state->get_class_source(insn->src(0)));
      }
      // Note that this is sound. In a concrete execution, if the check-cast
      // operation fails, an exception is thrown and the control point
      // following the check-cast becomes unreachable, which corresponds to
      // _|_ in the abstract domain. Any abstract state is a sound
      // approximation of _|_.
      break;
    }
    case OPCODE_INSTANCE_OF: {
      const auto aobj = current_state->get_abstract_obj(insn->src(0));
      auto obj = aobj.get_object();
      // Append the referenced type here to the potential dex types list.
      // Doing this increases the type information we have at the reflection
      // site. It's up to the user of the analysis  how to interpret this
      // information.
      if (obj && (obj->obj_kind == AbstractObjectKind::OBJECT) &&
          obj->dex_type) {
        auto dex_type = insn->get_type();
        if (obj->dex_type != dex_type) {
          obj->potential_dex_types.insert(dex_type);
          current_state->set_abstract_obj(
              insn->src(0),
              AbstractObjectDomain(AbstractObject(obj->obj_kind, obj->dex_type,
                                                  obj->potential_dex_types)));
        }
      }

      break;
    }
    case OPCODE_AGET_OBJECT: {
      const auto array_object =
          current_state->get_abstract_obj(insn->src(0)).get_object();
      if (array_object) {
        auto type = array_object->dex_type;
        if (type && type::is_array(type)) {
          const auto etype = type::get_array_component_type(type);
          update_non_string_input(current_state, insn, etype);
          break;
        }
      }
      default_semantics(insn, current_state);
      break;
    }
    case OPCODE_APUT_OBJECT: {
      // insn format: aput <source> <array> <offset>
      const auto source_object =
          current_state->get_abstract_obj(insn->src(0)).get_object();
      const auto array_object =
          current_state->get_abstract_obj(insn->src(1)).get_object();
      const auto offset_object =
          current_state->get_abstract_obj(insn->src(2)).get_object();

      if (source_object && source_object->obj_kind == CLASS && array_object &&
          array_object->is_known_class_array() && offset_object &&
          offset_object->obj_kind == INT) {

        auto type = source_object->dex_type;
        boost::optional<int64_t> offset = offset_object->dex_int;
        boost::optional<std::vector<DexType*>> class_array =
            current_state->get_heap_class_array(array_object->heap_address)
                .get_constant();

        if (offset && class_array && *offset >= 0 &&
            class_array->size() > (size_t)*offset) {
          (*class_array)[*offset] = type;
          current_state->set_heap_class_array(
              array_object->heap_address,
              ConstantAbstractDomain<std::vector<DexType*>>(*class_array));
        }
      }
      if (source_object && source_object->is_known_class_array()) {
        current_state->set_heap_addr_to_top(source_object->heap_address);
      }
      default_semantics(insn, current_state);
      break;
    }
    case OPCODE_IPUT_OBJECT:
    case OPCODE_SPUT_OBJECT: {
      const auto source_object =
          current_state->get_abstract_obj(insn->src(0)).get_object();
      if (source_object && source_object->is_known_class_array()) {
        current_state->set_heap_addr_to_top(source_object->heap_address);
      }
      break;
    }
    case OPCODE_IGET_OBJECT:
    case OPCODE_SGET_OBJECT: {
      always_assert(insn->has_field());
      const auto field = insn->get_field();
      DexType* primitive_type = check_primitive_type_class(field);
      if (primitive_type) {
        // The field being accessed is a Class object to a primitive type
        // likely being used for reflection
        auto aobj = AbstractObject(AbstractObjectKind::CLASS, primitive_type);
        current_state->set_abstract_obj(RESULT_REGISTER,
                                        AbstractObjectDomain(aobj));
        current_state->set_class_source(
            RESULT_REGISTER,
            ClassObjectSourceDomain(ClassObjectSource::REFLECTION));
      } else {
        update_non_string_input(current_state, insn, field->get_type());
      }
      break;
    }
    case OPCODE_NEW_INSTANCE: {
      current_state->set_abstract_obj(
          RESULT_REGISTER,
          AbstractObjectDomain(
              AbstractObject(AbstractObjectKind::OBJECT, insn->get_type())));
      break;
    }
    case OPCODE_NEW_ARRAY: {
      auto array_type = insn->get_type();
      always_assert(type::is_array(array_type));
      auto component_type = type::get_array_component_type(array_type);
      if (component_type == type::java_lang_Class()) {
        const auto aobj =
            current_state->get_abstract_obj(insn->src(0)).get_object();

        if (aobj && aobj->obj_kind == INT && aobj->dex_int) {
          AbstractHeapAddress addr = allocate_heap_address();
          int64_t size = *(aobj->dex_int);
          std::vector<DexType*> array(size);
          ConstantAbstractDomain<std::vector<DexType*>> heap_array(array);
          current_state->set_heap_class_array(addr, heap_array);
          current_state->set_abstract_obj(
              RESULT_REGISTER,
              AbstractObjectDomain(
                  AbstractObject(AbstractObjectKind::OBJECT, addr)));
          break;
        }
      }
      current_state->set_abstract_obj(
          RESULT_REGISTER,
          AbstractObjectDomain(
              AbstractObject(AbstractObjectKind::OBJECT, insn->get_type())));
      break;
    }
    case OPCODE_FILLED_NEW_ARRAY: {
      auto array_type = insn->get_type();
      always_assert(type::is_array(array_type));
      auto component_type = type::get_array_component_type(array_type);
      AbstractObject aobj(AbstractObjectKind::OBJECT, insn->get_type());
      if (component_type == type::java_lang_Class()) {
        auto arg_count = insn->srcs_size();
        std::vector<DexType*> known_types;
        known_types.reserve(arg_count);

        // collect known types from the filled new array
        for (auto src_reg : insn->srcs()) {
          auto reg_obj = current_state->get_abstract_obj(src_reg).get_object();
          if (reg_obj && reg_obj->obj_kind == CLASS && reg_obj->dex_type) {
            known_types.push_back(reg_obj->dex_type);
          }
        }

        if (known_types.size() == arg_count) {
          AbstractHeapAddress addr = allocate_heap_address();
          ConstantAbstractDomain<std::vector<DexType*>> heap_array(known_types);
          current_state->set_heap_class_array(addr, heap_array);
          aobj = AbstractObject(AbstractObjectKind::OBJECT, addr);
        }
      }

      current_state->set_abstract_obj(RESULT_REGISTER,
                                      AbstractObjectDomain(aobj));
      break;
    }
    case OPCODE_INVOKE_VIRTUAL: {
      auto receiver =
          current_state->get_abstract_obj(insn->src(0)).get_object();
      if (!receiver) {
        update_return_object_and_invalidate_heap_args(current_state, insn,
                                                      callee_return);
        break;
      }
      process_virtual_call(insn, *receiver, current_state, callee_return);
      break;
    }
    case OPCODE_INVOKE_STATIC: {
      if (insn->get_method() == m_cache->for_name) {
        auto class_name =
            current_state->get_abstract_obj(insn->src(0)).get_object();
        if (class_name && class_name->obj_kind == STRING) {
          if (class_name->dex_string != nullptr) {
            auto internal_name =
                DexString::make_string(java_names::external_to_internal(
                    class_name->dex_string->str()));
            current_state->set_abstract_obj(
                RESULT_REGISTER,
                AbstractObjectDomain(
                    AbstractObject(AbstractObjectKind::CLASS,
                                   DexType::make_type(internal_name))));
          } else {
            current_state->set_abstract_obj(
                RESULT_REGISTER,
                AbstractObjectDomain(
                    AbstractObject(AbstractObjectKind::CLASS, nullptr)));
          }

          current_state->set_class_source(
              RESULT_REGISTER,
              ClassObjectSourceDomain(ClassObjectSource::REFLECTION));
          break;
        }
      }
      update_return_object_and_invalidate_heap_args(current_state, insn,
                                                    callee_return);
      break;
    }
    case OPCODE_INVOKE_INTERFACE:
    case OPCODE_INVOKE_SUPER:
    case OPCODE_INVOKE_DIRECT: {
      update_return_object_and_invalidate_heap_args(current_state, insn,
                                                    callee_return);
      break;
    }
    case OPCODE_RETURN_OBJECT: {
      this->m_return_value.join_with(
          current_state->get_abstract_obj(insn->src(0)));
      break;
    }
    default: {
      default_semantics(insn, current_state);
    }
    }
  }