ART: Be more robust with multidex uncompressed-dex odex files
Reject odex/vdex files that mix embedded and uncompressed dex files.
This simplifies the code and allows early detection of mismatches
and corruption.
Bug: 73086368
Test: m test-art-host
Test: m test-art-host-gtest-oat_file_test
Test: manual (with bug's environment)
Change-Id: Icccb3ca29b6c5f5adefe3735c91b8b6eac113af6
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 894b33b..65b691f 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -83,6 +83,11 @@
ART_TEST_HOST_GTEST_EmptyUncompressed_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))EmptyUncompressed$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
ART_TEST_TARGET_GTEST_EmptyUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))EmptyUncompressed$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX))
+# Create rules for MultiDexUncompressed, a copy of MultiDex with the classes.dex uncompressed
+# for the OatFile tests.
+ART_TEST_HOST_GTEST_MultiDexUncompressed_DEX := $(basename $(ART_TEST_HOST_GTEST_MultiDex_DEX))Uncompressed$(suffix $(ART_TEST_HOST_GTEST_MultiDex_DEX))
+ART_TEST_TARGET_GTEST_MultiDexUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTEST_MultiDex_DEX))Uncompressed$(suffix $(ART_TEST_TARGET_GTEST_MultiDex_DEX))
+
$(ART_TEST_HOST_GTEST_MainStripped_DEX): $(ART_TEST_HOST_GTEST_Main_DEX)
cp $< $@
$(call dexpreopt-remove-classes.dex,$@)
@@ -111,6 +116,16 @@
zip -j -qD -X -0 $@ $(dir $@)classes.dex
rm $(dir $@)classes.dex
+$(ART_TEST_HOST_GTEST_MultiDexUncompressed_DEX): $(ART_TEST_HOST_GTEST_MultiDex_DEX) $(ZIPALIGN)
+ cp $< $@
+ $(call uncompress-dexs, $@)
+ $(call align-package, $@)
+
+$(ART_TEST_TARGET_GTEST_MultiDexUncompressed_DEX): $(ART_TEST_TARGET_GTEST_MultiDex_DEX) $(ZIPALIGN)
+ cp $< $@
+ $(call uncompress-dexs, $@)
+ $(call align-package, $@)
+
ART_TEST_GTEST_VerifierDeps_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDeps/*.smali))
ART_TEST_GTEST_VerifierDepsMulti_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDepsMulti/*.smali))
ART_TEST_HOST_GTEST_VerifierDeps_DEX := $(dir $(ART_TEST_HOST_GTEST_Main_DEX))$(subst Main,VerifierDeps,$(basename $(notdir $(ART_TEST_HOST_GTEST_Main_DEX))))$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
@@ -153,7 +168,7 @@
ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_dexoptanalyzer_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
-ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
+ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex MainUncompressed MultiDexUncompressed
ART_GTEST_oat_test_DEX_DEPS := Main
ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
ART_GTEST_patchoat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index dc4bae3..600449c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -633,6 +633,15 @@
const uint8_t* dex_file_pointer = nullptr;
if (UNLIKELY(dex_file_offset == 0U)) {
if (uncompressed_dex_files_ == nullptr) {
+ // Do not support mixed-mode oat files.
+ if (i > 0) {
+ *error_msg = StringPrintf("In oat file '%s', unsupported uncompressed-dex-file for dex "
+ "file %zu (%s)",
+ GetLocation().c_str(),
+ i,
+ dex_file_location.c_str());
+ return false;
+ }
uncompressed_dex_files_.reset(new std::vector<std::unique_ptr<const DexFile>>());
// No dex files, load it from location.
const ArtDexFileLoader dex_file_loader;
@@ -652,9 +661,31 @@
return false;
}
}
+ // The oat file may be out of date wrt/ the dex-file location. We need to be defensive
+ // here and ensure that at least the number of dex files still matches.
+ // Note: actual checksum comparisons are the duty of the OatFileAssistant and will be
+ // done after loading the OatFile.
+ if (uncompressed_dex_files_->size() != dex_file_count) {
+ *error_msg = StringPrintf("In oat file '%s', expected %u uncompressed dex files, but "
+ "found %zu in '%s'",
+ GetLocation().c_str(),
+ dex_file_count,
+ uncompressed_dex_files_->size(),
+ dex_file_location.c_str());
+ return false;
+ }
}
dex_file_pointer = uncompressed_dex_files_.get()->at(i)->Begin();
} else {
+ // Do not support mixed-mode oat files.
+ if (uncompressed_dex_files_ != nullptr) {
+ *error_msg = StringPrintf("In oat file '%s', unsupported embedded dex-file for dex file "
+ "%zu (%s)",
+ GetLocation().c_str(),
+ i,
+ dex_file_location.c_str());
+ return false;
+ }
if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) {
*error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file "
"offset %u of %zu but the size of dex file header is %zu",
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
index 8d86401..89812f3 100644
--- a/runtime/oat_file_test.cc
+++ b/runtime/oat_file_test.cc
@@ -88,4 +88,46 @@
EXPECT_EQ(odex_file->GetVdexFile()->Begin(), odex_file->VdexBegin());
}
+TEST_F(OatFileTest, ChangingMultiDexUncompressed) {
+ std::string dex_location = GetScratchDir() + "/MultiDexUncompressed.jar";
+
+ Copy(GetTestDexFileName("MultiDexUncompressed"), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kQuicken);
+
+ std::string oat_location;
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
+ dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
+
+ // Ensure we can load that file. Just a precondition.
+ {
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+ oat_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
+ ASSERT_TRUE(odex_file != nullptr);
+ ASSERT_EQ(2u, odex_file->GetOatDexFiles().size());
+ }
+
+ // Now replace the source.
+ Copy(GetTestDexFileName("MainUncompressed"), dex_location);
+
+ // And try to load again.
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+ oat_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
+ EXPECT_TRUE(odex_file == nullptr);
+ EXPECT_NE(std::string::npos, error_msg.find("expected 2 uncompressed dex files, but found 1"))
+ << error_msg;
+}
+
} // namespace art