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);
}
}
}