TEST(DedupBlocksTest, useSwitch) { g_redex = new RedexContext(); const char* dexfile = std::getenv("dexfile"); EXPECT_NE(nullptr, dexfile); std::vector<DexStore> stores; DexMetadata dm; dm.set_id("classes"); DexStore root_store(dm); root_store.add_classes(load_classes_from_dex(dexfile)); DexClasses& classes = root_store.get_dexen().back(); stores.emplace_back(std::move(root_store)); TRACE(RME, 1, "Code before:\n"); for (const auto& cls : classes) { TRACE(RME, 1, "Class %s\n", SHOW(cls)); for (const auto& m : cls->get_vmethods()) { TRACE(RME, 1, "\nmethod %s:\n", SHOW(m)); IRCode* code = m->get_code(); code->build_cfg(true); EXPECT_EQ(2, count_sgets(code->cfg())); code->clear_cfg(); } } auto copy_prop = new CopyPropagationPass(); copy_prop->m_config.static_finals = true; copy_prop->m_config.wide_registers = true; std::vector<Pass*> passes = { copy_prop }; PassManager manager(passes); manager.set_testing_mode(); Json::Value conf_obj = Json::nullValue; Scope external_classes; ConfigFiles dummy_cfg(conf_obj); manager.run_passes(stores, external_classes, dummy_cfg); TRACE(RME, 1, "Code after:\n"); for (const auto& cls : classes) { for (const auto& m : cls->get_vmethods()) { TRACE(RME, 1, "\nmethod %s:\n", SHOW(m)); IRCode* code = m->get_code(); code->build_cfg(true); if (strcmp(m->get_name()->c_str(), "remove") == 0) { EXPECT_EQ(1, count_sgets(code->cfg())); } else { EXPECT_EQ(2, count_sgets(code->cfg())); } code->clear_cfg(); } } }
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(); } }
void BasicBlockProfilePass::run_pass(DexStoresVector& stores, ConfigFiles& /* conf */, PassManager& pm) { std::unordered_set<cfg::Block*> bb_profiled; auto scope = build_class_scope(stores); walk::methods(scope, [&](DexMethod* method) { IRCode* code = method->get_code(); if (code == nullptr) { return; } code->build_cfg(/* editable */ false); const auto& blocks = code->cfg().blocks(); TRACE(BBPROFILE, 5, "M,%s,%zu,%zu,%d\n", method->get_deobfuscated_name().c_str(), blocks.size(), code->count_opcodes(), method->is_virtual()); for (cfg::Block* block : blocks) { // Only if the current block is a multi-sink block, its predecessors are // profiled. This is for tracing the path back. if (block->preds().size() > 1) { for (const auto& pred : block->preds()) { bb_profiled.insert(pred->src()); } } TRACE(BBPROFILE, 5, "B,%zu,%zu,%zu," "%zu,%d\n", block->id(), num_opcodes_bb(block), block->succs().size(), block->preds().size(), bb_profiled.count(block) != 0); } }); }
void IRTypeChecker::run() { IRCode* code = m_dex_method->get_code(); if (m_complete) { // The type checker can only be run once on any given method. return; } if (code == nullptr) { // If the method has no associated code, the type checking trivially // succeeds. m_complete = true; return; } auto result = check_structure(m_dex_method, m_check_no_overwrite_this); if (result != Result::Ok()) { m_complete = true; m_good = false; m_what = result.error_message(); return; } // We then infer types for all the registers used in the method. code->build_cfg(/* editable */ false); const cfg::ControlFlowGraph& cfg = code->cfg(); m_type_inference = std::make_unique<TypeInference>(cfg, m_enable_polymorphic_constants); m_type_inference->run(m_dex_method); // Finally, we use the inferred types to type-check each instruction in the // method. We stop at the first type error encountered. auto& type_envs = m_type_inference->get_type_environments(); for (const MethodItemEntry& mie : InstructionIterable(code)) { IRInstruction* insn = mie.insn; try { auto it = type_envs.find(insn); always_assert(it != type_envs.end()); check_instruction(insn, &it->second); } catch (const TypeCheckingException& e) { m_good = false; std::ostringstream out; out << "Type error in method " << m_dex_method->get_deobfuscated_name() << " at instruction '" << SHOW(insn) << "' @ " << std::hex << static_cast<const void*>(&mie) << " for " << e.what(); m_what = out.str(); m_complete = true; return; } } m_complete = true; if (traceEnabled(TYPE, 5)) { std::ostringstream out; m_type_inference->print(out); TRACE(TYPE, 5, "%s\n", out.str().c_str()); } }
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); }
TEST(ResultPropagationTest, useSwitch) { g_redex = new RedexContext(); const char* dexfile = std::getenv("dexfile"); EXPECT_NE(nullptr, dexfile); std::vector<DexStore> stores; DexMetadata dm; dm.set_id("classes"); DexStore root_store(dm); root_store.add_classes(load_classes_from_dex(dexfile)); DexClasses& classes = root_store.get_dexen().back(); stores.emplace_back(std::move(root_store)); std::vector<Pass*> passes = { new ResultPropagationPass(), }; PassManager manager(passes); manager.set_testing_mode(); Json::Value conf_obj = Json::nullValue; ConfigFiles dummy_cfg(conf_obj); manager.run_passes(stores, dummy_cfg); int num_test_classes = 0; const char* test_class_prefix = "Lcom/facebook/redextest/ResultPropagation$"; const char* test_method_prefix = "returns_"; for (const auto& cls : classes) { if (strncmp(cls->get_name()->c_str(), test_class_prefix, strlen(test_class_prefix)) == 0) { TRACE(RP, 1, "test class %s\n", cls->get_name()->c_str()); num_test_classes++; int num_tests_in_class = 0; for (const auto& m : cls->get_vmethods()) { const auto method_name = m->get_name()->c_str(); TRACE(RP, 1, " test method %s\n", method_name); EXPECT_EQ(strncmp(method_name, test_method_prefix, strlen(test_method_prefix)), 0); const auto suffix = method_name + strlen(test_method_prefix); boost::optional<ParamIndex> expected; if (strcmp(suffix, "none") != 0) { expected = atoi(suffix); } IRCode* code = m->get_code(); code->build_cfg(/* editable */ true); auto actual = find_return_param_index(code->cfg()); code->clear_cfg(); if (expected) { EXPECT_TRUE(actual); EXPECT_EQ(*actual, *expected); } else { EXPECT_FALSE(actual); } num_tests_in_class++; } EXPECT_EQ(num_tests_in_class, 1); } } EXPECT_EQ(num_test_classes, 6); }