Store and check apex-versions in boot images.
Since apex-versions contains the version of the ART APEX, this change
enables the runtime to reject boot images after a placebo update.
Bug: 211973309
Test: atest odsign_e2e_tests
Test: Build a system image, flash it to device and run
`atest art_standalone_dexpreopt_tests`.
Change-Id: I1e454428b9a2f8b9cc5dcb3f3753228cb235619d
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index b8271d8..64d94a6 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1555,6 +1555,8 @@
key_value_store_->Put(OatHeader::kCompilationReasonKey, compilation_reason_);
}
+ Runtime* runtime = Runtime::Current();
+
if (IsBootImage()) {
// If we're compiling the boot image, store the boot classpath into the Key-Value store.
// We use this when loading the boot image.
@@ -1562,7 +1564,6 @@
} else if (IsBootImageExtension()) {
// Validate the boot class path and record the dependency on the loaded boot images.
TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
- Runtime* runtime = Runtime::Current();
std::string full_bcp = android::base::Join(runtime->GetBootClassPathLocations(), ':');
std::string extension_part = ":" + android::base::Join(dex_locations_, ':');
if (!android::base::EndsWith(full_bcp, extension_part)) {
@@ -1581,18 +1582,12 @@
} else {
if (CompilerFilter::DependsOnImageChecksum(original_compiler_filter)) {
TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
- Runtime* runtime = Runtime::Current();
key_value_store_->Put(OatHeader::kBootClassPathKey,
android::base::Join(runtime->GetBootClassPathLocations(), ':'));
ArrayRef<ImageSpace* const> image_spaces(runtime->GetHeap()->GetBootImageSpaces());
key_value_store_->Put(
OatHeader::kBootClassPathChecksumsKey,
gc::space::ImageSpace::GetBootClassPathChecksums(image_spaces, bcp_dex_files));
-
- std::string versions = apex_versions_argument_.empty()
- ? runtime->GetApexVersions()
- : apex_versions_argument_;
- key_value_store_->Put(OatHeader::kApexVersionsKey, versions);
}
// Open dex files for class path.
@@ -1634,6 +1629,14 @@
key_value_store_->Put(OatHeader::kClassPathKey, class_path_key);
}
+ if (IsBootImage() ||
+ IsBootImageExtension() ||
+ CompilerFilter::DependsOnImageChecksum(original_compiler_filter)) {
+ std::string versions =
+ apex_versions_argument_.empty() ? runtime->GetApexVersions() : apex_versions_argument_;
+ key_value_store_->Put(OatHeader::kApexVersionsKey, versions);
+ }
+
// Now that we have finalized key_value_store_, start writing the .rodata section.
// Among other things, this creates type lookup tables that speed up the compilation.
{
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 13a424b..5c2d84c 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -227,6 +227,7 @@
SafeMap<std::string, std::string> key_value_store;
key_value_store.Put(OatHeader::kBootClassPathKey,
android::base::Join(out_helper.dex_file_locations, ':'));
+ key_value_store.Put(OatHeader::kApexVersionsKey, Runtime::Current()->GetApexVersions());
std::vector<std::unique_ptr<ElfWriter>> elf_writers;
std::vector<std::unique_ptr<OatWriter>> oat_writers;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index c7247cc..9860f60 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -3455,6 +3455,34 @@
<< ",name=\"" << GetName() << "\"]";
}
+bool ImageSpace::ValidateApexVersions(const OatFile& oat_file, std::string* error_msg) {
+ // For a boot image, the key value store only exists in the first OAT file. Skip other OAT files.
+ if (oat_file.GetOatHeader().GetKeyValueStoreSize() == 0) {
+ return true;
+ }
+
+ const char* oat_apex_versions =
+ oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kApexVersionsKey);
+ if (oat_apex_versions == nullptr) {
+ *error_msg = StringPrintf("ValidateApexVersions failed to get APEX versions from oat file '%s'",
+ oat_file.GetLocation().c_str());
+ return false;
+ }
+ // For a boot image, it can be generated from a subset of the bootclasspath.
+ // For an app image, some dex files get compiled with a subset of the bootclasspath.
+ // For such cases, the OAT APEX versions will be a prefix of the runtime APEX versions.
+ if (!android::base::StartsWith(Runtime::Current()->GetApexVersions(), oat_apex_versions)) {
+ *error_msg = StringPrintf(
+ "ValidateApexVersions found APEX versions mismatch between oat file '%s' and the runtime "
+ "(Oat file: '%s', Runtime: '%s')",
+ oat_file.GetLocation().c_str(),
+ oat_apex_versions,
+ Runtime::Current()->GetApexVersions().c_str());
+ return false;
+ }
+ return true;
+}
+
bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) {
return ValidateOatFile(oat_file, error_msg, ArrayRef<const std::string>(), ArrayRef<const int>());
}
@@ -3463,6 +3491,10 @@
std::string* error_msg,
ArrayRef<const std::string> dex_filenames,
ArrayRef<const int> dex_fds) {
+ if (!ValidateApexVersions(oat_file, error_msg)) {
+ return false;
+ }
+
const ArtDexFileLoader dex_file_loader;
size_t dex_file_index = 0;
for (const OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index d366b5b..8a93f2b 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -267,6 +267,9 @@
const std::string& image_location,
bool boot_image_extension = false);
+ // Returns true if the APEX versions in the OAT file match the current APEX versions.
+ static bool ValidateApexVersions(const OatFile& oat_file, std::string* error_msg);
+
// Returns true if the dex checksums in the given oat file match the
// checksums of the original dex files on disk. This is intended to be used
// to validate the boot image oat file, which may contain dex entries from
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index c303a7b..46a4d0e 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -413,19 +413,6 @@
return true;
}
-static bool ValidateApexVersions(const OatFile& oat_file) {
- const char* oat_apex_versions =
- oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kApexVersionsKey);
- if (oat_apex_versions == nullptr) {
- return false;
- }
- // Some dex files get compiled with a subset of the boot classpath (for
- // example currently system server is compiled with DEX2OAT_BOOTCLASSPATH).
- // For such cases, the oat apex versions will be a prefix of the runtime apex
- // versions.
- return android::base::StartsWith(Runtime::Current()->GetApexVersions(), oat_apex_versions);
-}
-
OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
// Verify the ART_USE_READ_BARRIER state.
// TODO: Don't fully reject files due to read barrier state. If they contain
@@ -456,8 +443,8 @@
VLOG(oat) << "Oat image checksum does not match image checksum.";
return kOatBootImageOutOfDate;
}
- if (!ValidateApexVersions(file)) {
- VLOG(oat) << "Apex versions do not match.";
+ if (!gc::space::ImageSpace::ValidateApexVersions(file, &error_msg)) {
+ VLOG(oat) << error_msg;
return kOatBootImageOutOfDate;
}
} else {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 54e9d38..a22d24a 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1548,6 +1548,9 @@
// Generational CC collection is currently only compatible with Baker read barriers.
bool use_generational_cc = kUseBakerReadBarrier && xgc_option.generational_cc;
+ // Cache the apex versions.
+ InitializeApexVersions();
+
heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
runtime_options.GetOrDefault(Opt::HeapMinFree),
@@ -1823,9 +1826,6 @@
boot_class_path_checksums_ = gc::space::ImageSpace::GetBootClassPathChecksums(image_spaces,
bcp_dex_files);
- // Cache the apex versions.
- InitializeApexVersions();
-
CHECK(class_linker_ != nullptr);
verifier::ClassVerifier::Init(class_linker_);