Пример #1
0
TEST_F(PostVerify, ConstantPropagation) {
  TRACE(CONSTP, 1, "------------- post ---------------\n");
  auto cls = find_class_named(classes, "Lredex/ConstantPropagationTest;");
  ASSERT_NE(cls, nullptr);
  for (auto& meth : cls->get_vmethods()) {
    if(meth->get_name()->str().find("if") == std::string::npos) {
      continue;
    }
    IRCode* code = new IRCode(meth);
    EXPECT_NE(code, nullptr);
    code->build_cfg(/* editable */ true);
    if (meth->get_name()->str().find("plus_one") != std::string::npos) {
      TRACE(CONSTP, 1, "%s\n", SHOW(meth));
      TRACE(CONSTP, 1, "%s\n", SHOW(code));
    }

    EXPECT_EQ(0, count_ifs(code->cfg()));
    if (meth->get_name()->str().find("plus_one") != std::string::npos) {
      EXPECT_EQ(0, count_ops(code->cfg(), OPCODE_ADD_INT_LIT8));
    }

    if (meth->get_name()->str().find("overflow") != std::string::npos) {
      // make sure we don't fold overflow at compile time
      EXPECT_EQ(1, count_ops(code->cfg(), OPCODE_ADD_INT_LIT8));
    }
    code->clear_cfg();
  }
}
Пример #2
0
size_t delete_methods(
    std::vector<DexClass*>& scope, std::unordered_set<DexMethod*>& removable,
    std::function<DexMethod*(DexMethod*, MethodSearch search)> resolver) {

  // if a removable candidate is invoked do not delete
  walk_opcodes(scope, [](DexMethod* meth) { return true; },
      [&](DexMethod* meth, DexInstruction* insn) {
        if (is_invoke(insn->opcode())) {
          const auto mop = static_cast<DexOpcodeMethod*>(insn);
          auto callee = resolver(mop->get_method(), opcode_to_search(insn));
          if (callee != nullptr) {
            removable.erase(callee);
          }
        }
      });

  size_t deleted = 0;
  for (auto callee : removable) {
    if (!callee->is_concrete()) continue;
    if (do_not_strip(callee)) continue;
    auto cls = type_class(callee->get_class());
    always_assert_log(cls != nullptr,
        "%s is concrete but does not have a DexClass\n",
        SHOW(callee));
    if (callee->is_virtual()) {
      cls->get_vmethods().remove(callee);
    } else {
      cls->get_dmethods().remove(callee);
    }
    deleted++;
    TRACE(DELMET, 4, "removing %s\n", SHOW(callee));
  }
  return deleted;
}
Пример #3
0
TEST_F(PostVerify, IPConstantPropagation) {
  auto test_cls =
      find_class_named(classes, "Lredex/IPConstantPropagationTest;");
  size_t count = 0;
  for (auto& meth : test_cls->get_vmethods()) {
    IRCode* code = new IRCode(meth);
    ASSERT_NE(code, nullptr);
    code->build_cfg(/* editable */ true);
    if (meth->get_name()->str() == "two_ctors") {
      EXPECT_EQ(0, count_igets(code->cfg(), "a"));
      EXPECT_EQ(2, count_igets(code->cfg(), "b"));
      ++count;
    } else if (meth->get_name()->str() == "modified_elsewhere") {
      EXPECT_EQ(0, count_igets(code->cfg(), "a"));
      EXPECT_EQ(1, count_igets(code->cfg(), "b"));
      ++count;
    }
  }
  // Make sure the two functions are there.
  ASSERT_EQ(count, 2);
}
Пример #4
0
/**
 * Move all methods of the interface to the concrete (if not there already)
 * and rewrite all refs that were calling to the interface
 * (invoke-interface* -> invoke-virtual*).
 */
void OptimizationImpl::rewrite_interface_methods(DexType* intf,
                                                 SingleImplData& data) {
  auto intf_cls = type_class(intf);
  for (auto method : intf_cls->get_vmethods()) {
    auto meth = method;
    auto meth_it = new_methods.find(meth);
    if (meth_it != new_methods.end()) {
      meth = meth_it->second;
    }
    drop_single_impl_collision(intf, data, meth);
    auto impl = type_class(data.cls);
    // Given an interface method and a class determine whether the method
    // is already defined in the class and use it if so.
    // An interface method can be defined in some base class for "convenience"
    // even though the base class does not implement the interface so we walk
    // the chain looking for the method.
    // NOTICE: if we have interfaces that have methods defined up the chain
    // in some java, android, google or other library we are screwed.
    // We'll not find the method and introduce a possible abstract one that
    // will break things.
    // Hopefully we'll find that out during verification and correct things.

    // get the new method if one was created (interface method with a single
    // impl in signature)
    TRACE(INTF, 3, "(MITF) interface method %s\n", SHOW(meth));
    auto new_meth = resolve_virtual(impl, meth->get_name(), meth->get_proto());
    if (!new_meth) {
      new_meth = DexMethod::make_method(impl->get_type(), meth->get_name(),
          meth->get_proto());
      TRACE(INTF, 5, "(MITF) created impl method %s\n", SHOW(new_meth));
      setup_method(meth, new_meth);
      assert(new_meth->is_virtual());
      impl->add_method(new_meth);
      TRACE(INTF, 3, "(MITF) moved interface method %s\n", SHOW(new_meth));
    } else {
      TRACE(INTF, 3, "(MITF) found method impl %s\n", SHOW(new_meth));
    }
    assert(new_methods.find(meth) == new_methods.end());
    new_methods[method] = new_meth;
  }

  // rewrite invoke-interface to invoke-virtual
  for (const auto& mref_it : data.intf_methodrefs) {
    auto m = mref_it.first;
    assert(new_methods.find(m) != new_methods.end());
    auto new_m = new_methods[m];
    assert(new_m && new_m != m);
    TRACE(INTF, 3, "(MITFOP) %s\n", SHOW(new_m));
    for (auto mop : mref_it.second) {
      TRACE(INTF, 3, "(MITFOP) %s\n", SHOW(mop));
      mop->rewrite_method(new_m);
      auto op = mop->opcode();
      if (op == OPCODE_INVOKE_INTERFACE) {
        mop->set_opcode(OPCODE_INVOKE_VIRTUAL);
      } else if (op == OPCODE_INVOKE_INTERFACE_RANGE) {
        mop->set_opcode(OPCODE_INVOKE_VIRTUAL_RANGE);
      }
      SingleImplPass::s_invoke_intf_count++;
      TRACE(INTF, 3, "(MITFOP)\t=>%s\n", SHOW(mop));
    }
  }
}
Пример #5
0
  void search_hierarchy_for_matches(DexMethod* bridge, DexMethod* bridgee) {
    /*
     * Direct reference.  The only one if it's non-virtual.
     */
    auto clstype = bridgee->get_class();
    auto name = bridgee->get_name();
    auto proto = bridgee->get_proto();
    TRACE(BRIDGE, 5, "   %s %s %s\n", SHOW(clstype), SHOW(name), SHOW(proto));
    m_potential_bridgee_refs.emplace(MethodRef(clstype, name, proto), bridge);
    if (!bridgee->is_virtual()) return;

    /*
     * Search super classes
     *
     *   A bridge method in a derived class may be referred to using the name
     *   of a super class if a method with a matching signature is defined in
     *   that super class.
     *
     *   To build the set of potential matches, we accumulate potential refs in
     *   maybe_refs, and when we find a matching signature in a super class, we
     *   add everything in maybe_refs to the set.
     */
    std::vector<std::pair<MethodRef, DexMethod*>> maybe_refs;
    for (auto super = type_class(type_class(clstype)->get_super_class());
         super != nullptr;
         super = type_class(super->get_super_class())) {
      maybe_refs.emplace_back(
          MethodRef(super->get_type(), name, proto), bridge);
      for (auto vmethod : super->get_vmethods()) {
        if (signature_matches(bridgee, vmethod)) {
          for (auto DEBUG_ONLY refp : maybe_refs) {
            TRACE(BRIDGE,
                  5,
                  "    %s %s %s\n",
                  SHOW(std::get<0>(refp.first)),
                  SHOW(std::get<1>(refp.first)),
                  SHOW(std::get<2>(refp.first)));
          }
          m_potential_bridgee_refs.insert(maybe_refs.begin(), maybe_refs.end());
          maybe_refs.clear();
        }
      }
    }

    /*
     * Search sub classes
     *
     *   Easy.  Any subclass can refer to the bridgee.
     */
    TypeVector subclasses;
    get_all_children(clstype, subclasses);
    for (auto subclass : subclasses) {
      m_potential_bridgee_refs.emplace(MethodRef(subclass, name, proto),
                                       bridge);
      TRACE(BRIDGE,
            5,
            "    %s %s %s\n",
            SHOW(subclass),
            SHOW(name),
            SHOW(proto));
    }
  }
Пример #6
0
void RemoveBuildersPass::run_pass(DexStoresVector& stores,
                                  ConfigFiles&,
                                  PassManager& mgr) {
  if (mgr.no_proguard_rules()) {
    TRACE(BUILDERS,
          1,
          "RemoveBuildersPass did not run because no Proguard configuration "
          "was provided.");
    return;
  }

  // Initialize couters.
  b_counter = {0, 0, 0, 0};

  auto obj_type = get_object_type();
  auto scope = build_class_scope(stores);
  for (DexClass* cls : scope) {
    if (is_annotation(cls) || is_interface(cls) ||
        cls->get_super_class() != obj_type) {
      continue;
    }

    if (has_builder_name(cls->get_type())) {
      m_builders.emplace(cls->get_type());
    }
  }

  std::unordered_set<DexType*> escaped_builders;
  walk::methods(scope, [&](DexMethod* m) {
    auto builders = created_builders(m);
    for (DexType* builder : builders) {
      if (escapes_stack(builder, m)) {
        TRACE(BUILDERS,
              3,
              "%s escapes in %s\n",
              SHOW(builder),
              m->get_deobfuscated_name().c_str());
        escaped_builders.emplace(builder);
      }
    }
  });

  std::unordered_set<DexType*> stack_only_builders;
  for (DexType* builder : m_builders) {
    if (escaped_builders.find(builder) == escaped_builders.end()) {
      stack_only_builders.emplace(builder);
    }
  }

  std::unordered_set<DexType*> builders_and_supers;
  for (DexType* builder : stack_only_builders) {
    DexType* cls = builder;
    while (cls != nullptr && cls != obj_type) {
      builders_and_supers.emplace(cls);
      cls = type_class(cls)->get_super_class();
    }
  }

  std::unordered_set<DexType*> this_escapes;
  for (DexType* cls_ty : builders_and_supers) {
    DexClass* cls = type_class(cls_ty);
    if (cls->is_external() ||
        this_arg_escapes(cls, m_enable_buildee_constr_change)) {
      this_escapes.emplace(cls_ty);
    }
  }

  // set of builders that neither escape the stack nor pass their 'this' arg
  // to another function
  std::unordered_set<DexType*> no_escapes;
  for (DexType* builder : stack_only_builders) {
    DexType* cls = builder;
    bool hierarchy_has_escape = false;
    while (cls != nullptr) {
      if (this_escapes.find(cls) != this_escapes.end()) {
        hierarchy_has_escape = true;
        break;
      }
      cls = type_class(cls)->get_super_class();
    }
    if (!hierarchy_has_escape) {
      no_escapes.emplace(builder);
    }
  }

  size_t dmethod_count = 0;
  size_t vmethod_count = 0;
  size_t build_count = 0;
  for (DexType* builder : no_escapes) {
    auto cls = type_class(builder);
    auto buildee = get_buildee(builder);
    dmethod_count += cls->get_dmethods().size();
    vmethod_count += cls->get_vmethods().size();
    for (DexMethod* m : cls->get_vmethods()) {
      if (m->get_proto()->get_rtype() == buildee) {
        build_count++;
      }
    }
  }

  std::unordered_set<DexClass*> trivial_builders =
      get_trivial_builders(m_builders, no_escapes);

  std::unordered_set<DexClass*> kept_builders =
      get_builders_with_subclasses(scope);

  PassConfig pc(mgr.get_config());
  BuilderTransform b_transform(pc, scope, stores, false);

  // Inline non init methods.
  std::unordered_set<DexClass*> removed_builders;
  walk::methods(scope, [&](DexMethod* method) {
    auto builders = created_builders(method);

    for (DexType* builder : builders) {
      if (method->get_class() == builder) {
        continue;
      }

      DexClass* builder_cls = type_class(builder);

      // Filter out builders that we cannot remove.
      if (kept_builders.find(builder_cls) != kept_builders.end()) {
        continue;
      }

      // Check it is a trivial one.
      if (trivial_builders.find(builder_cls) != trivial_builders.end()) {

        DexMethod* method_copy = DexMethod::make_method_from(
            method,
            method->get_class(),
            DexString::make_string(method->get_name()->str() +
                                   "$redex_builders"));
        bool was_not_removed =
            !b_transform.inline_methods(
                method, builder, &get_non_init_methods) ||
            !remove_builder_from(method, builder_cls, b_transform);

        if (was_not_removed) {
          kept_builders.emplace(builder_cls);
          method->set_code(method_copy->release_code());
        } else {
          b_counter.methods_cleared++;
          removed_builders.emplace(builder_cls);
        }

        DexMethod::erase_method(method_copy);
      }
    }
  });

  // No need to remove the builders here, since `RemoveUnreachable` will
  // take care of it.
  gather_removal_builder_stats(removed_builders, kept_builders);

  mgr.set_metric("total_builders", m_builders.size());
  mgr.set_metric("stack_only_builders", stack_only_builders.size());
  mgr.set_metric("no_escapes", no_escapes.size());
  mgr.incr_metric(METRIC_CLASSES_REMOVED, b_counter.classes_removed);
  mgr.incr_metric(METRIC_METHODS_REMOVED, b_counter.methods_removed);
  mgr.incr_metric(METRIC_FIELDS_REMOVED, b_counter.fields_removed);
  mgr.incr_metric(METRIC_METHODS_CLEARED, b_counter.methods_cleared);

  TRACE(BUILDERS, 1, "Total builders: %d\n", m_builders.size());
  TRACE(BUILDERS, 1, "Stack-only builders: %d\n", stack_only_builders.size());
  TRACE(BUILDERS,
        1,
        "Stack-only builders that don't let `this` escape: %d\n",
        no_escapes.size());
  TRACE(BUILDERS, 1, "Stats for unescaping builders:\n");
  TRACE(BUILDERS, 1, "\tdmethods: %d\n", dmethod_count);
  TRACE(BUILDERS, 1, "\tvmethods: %d\n", vmethod_count);
  TRACE(BUILDERS, 1, "\tbuild methods: %d\n", build_count);
  TRACE(BUILDERS, 1, "Trivial builders: %d\n", trivial_builders.size());
  TRACE(BUILDERS, 1, "Classes removed: %d\n", b_counter.classes_removed);
  TRACE(BUILDERS, 1, "Methods removed: %d\n", b_counter.methods_removed);
  TRACE(BUILDERS, 1, "Fields removed: %d\n", b_counter.fields_removed);
  TRACE(BUILDERS, 1, "Methods cleared: %d\n", b_counter.methods_cleared);
}