diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2021-03-19 22:00:41 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2021-03-19 22:00:41 +0000 |
commit | bd5b3f836f2c83ff22d30ba045d1d43d9b624fe3 (patch) | |
tree | 069b7eb083647533427115dc10399c490fe81ed9 | |
parent | a153ff714cb088e92a8eaf20c042ba6258aee524 (diff) | |
parent | ad72901ccde05c18c5fd42e9d021252bead98587 (diff) |
Snap for 7221907 from ad72901ccde05c18c5fd42e9d021252bead98587 to rvc-qpr3-release
Change-Id: I9192c937c8c6d96add99008bf4333f636f6de39f
-rw-r--r-- | dex2oat/dex2oat_test.cc | 106 | ||||
-rw-r--r-- | runtime/elf_file_impl.h | 4 | ||||
-rw-r--r-- | runtime/oat_file.cc | 12 |
3 files changed, 121 insertions, 1 deletions
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index ab33c19e27..4de8265deb 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -44,6 +44,8 @@ #include "dex/dex_file_loader.h" #include "dex2oat_environment_test.h" #include "dex2oat_return_codes.h" +#include "elf_file.h" +#include "elf_file_impl.h" #include "gc_root-inl.h" #include "intern_table-inl.h" #include "oat.h" @@ -2489,4 +2491,108 @@ TEST_F(Dex2oatISAFeaturesRuntimeDetectionTest, TestCurrentRuntimeFeaturesAsDex2O RunTest(); } +// Regression test for bug 179221298. +TEST_F(Dex2oatTest, LoadOutOfDateOatFile) { + std::unique_ptr<const DexFile> dex(OpenTestDexFile("ManyMethods")); + std::string out_dir = GetScratchDir(); + const std::string base_oat_name = out_dir + "/base.oat"; + ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(), + base_oat_name, + CompilerFilter::Filter::kSpeed, + { "--deduplicate-code=false" }, + /*expect_success=*/ true, + /*use_fd=*/ false, + /*use_zip_fd=*/ false)); + + // Check that we can open the oat file as executable. + { + std::string error_msg; + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, + base_oat_name.c_str(), + base_oat_name.c_str(), + /*executable=*/ true, + /*low_4gb=*/ false, + dex->GetLocation(), + &error_msg)); + ASSERT_TRUE(odex_file != nullptr) << error_msg; + } + + // Rewrite the oat file with wrong version and bogus contents. + { + std::unique_ptr<File> file(OS::OpenFileReadWrite(base_oat_name.c_str())); + ASSERT_TRUE(file != nullptr); + // Retrieve the offset and size of the embedded oat file. + size_t oatdata_offset; + size_t oatdata_size; + { + std::string error_msg; + std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.get(), + /*writable=*/ false, + /*program_header_only=*/ true, + /*low_4gb=*/ false, + &error_msg)); + ASSERT_TRUE(elf_file != nullptr) << error_msg; + ASSERT_TRUE(elf_file->Load(file.get(), + /*executable=*/ false, + /*low_4gb=*/ false, + /*reservation=*/ nullptr, + &error_msg)) << error_msg; + const uint8_t* base_address = elf_file->Is64Bit() + ? elf_file->GetImpl64()->GetBaseAddress() + : elf_file->GetImpl32()->GetBaseAddress(); + const uint8_t* oatdata = elf_file->FindDynamicSymbolAddress("oatdata"); + ASSERT_TRUE(oatdata != nullptr); + ASSERT_TRUE(oatdata > base_address); + // Note: We're assuming here that the virtual address offset is the same + // as file offset. This is currently true for all oat files we generate. + oatdata_offset = static_cast<size_t>(oatdata - base_address); + const uint8_t* oatlastword = elf_file->FindDynamicSymbolAddress("oatlastword"); + ASSERT_TRUE(oatlastword != nullptr); + ASSERT_TRUE(oatlastword > oatdata); + oatdata_size = oatlastword - oatdata; + } + + // Check that we have the right `oatdata_offset`. + int64_t length = file->GetLength(); + ASSERT_GE(length, static_cast<ssize_t>(oatdata_offset + sizeof(OatHeader))); + alignas(OatHeader) uint8_t header_data[sizeof(OatHeader)]; + ASSERT_TRUE(file->PreadFully(header_data, sizeof(header_data), oatdata_offset)); + const OatHeader& header = reinterpret_cast<const OatHeader&>(header_data); + ASSERT_TRUE(header.IsValid()) << header.GetValidationErrorMessage(); + + // Overwrite all oat data from version onwards with bytes with value 4. + // (0x04040404 is not a valid version, we're using three decimal digits and '\0'.) + // + // We previously tried to find the value for key "debuggable" (bug 179221298) + // in the key-value store before checking the oat header. This test tries to + // ensure that such early processing of the key-value store shall crash. + // Reading 0x04040404 as the size of the key-value store yields a bit over + // 64MiB which should hopefully include some unmapped memory beyond the end + // of the loaded oat file. Overwriting the whole embedded oat file ensures + // that we do not match the key within the oat file but we could still + // accidentally match it in the additional sections of the elf file, so this + // approach could fail to catch similar issues. At the time of writing, this + // test crashed when run without the fix on 64-bit host (but not 32-bit). + static constexpr size_t kVersionOffset = sizeof(OatHeader::kOatMagic); + static_assert(kVersionOffset < sizeof(OatHeader)); + std::vector<uint8_t> data(oatdata_size - kVersionOffset, 4u); + ASSERT_TRUE(file->PwriteFully(data.data(), data.size(), oatdata_offset + kVersionOffset)); + UNUSED(oatdata_size); + CHECK_EQ(file->FlushClose(), 0) << "Could not flush and close oat file"; + } + + // Check that we reject the oat file without crashing. + { + std::string error_msg; + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, + base_oat_name.c_str(), + base_oat_name.c_str(), + /*executable=*/ true, + /*low_4gb=*/ false, + dex->GetLocation(), + &error_msg)); + ASSERT_FALSE(odex_file != nullptr); + } +} + } // namespace art diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h index 9900c76e40..a5937ff590 100644 --- a/runtime/elf_file_impl.h +++ b/runtime/elf_file_impl.h @@ -59,6 +59,10 @@ class ElfFileImpl { return file_path_; } + uint8_t* GetBaseAddress() const { + return base_address_; + } + uint8_t* Begin() const { return map_.Begin(); } diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index d72fc7ee37..48e34af141 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -275,7 +275,17 @@ bool OatFileBase::ShouldUnquickenVDex() const { // We sometimes load oat files without a runtime (eg oatdump) and don't want to do anything in // that case. If we are debuggable there are no -quick opcodes to unquicken. If the runtime is not // debuggable we don't care whether there are -quick opcodes or not so no need to do anything. - return Runtime::Current() != nullptr && !IsDebuggable() && Runtime::Current()->IsJavaDebuggable(); + Runtime* runtime = Runtime::Current(); + return (runtime != nullptr && runtime->IsJavaDebuggable()) && + // Note: This is called before `OatFileBase::Setup()` where we validate the + // oat file contents. Check that we have at least a valid header, including + // oat file version, to avoid parsing the key-value store for a different + // version (out-of-date oat file) which can lead to crashes. b/179221298. + // TODO: While this is a poor workaround and the correct solution would be + // to postpone the unquickening check until after `OatFileBase::Setup()`, + // we prefer to avoid larger rewrites because quickening is deprecated and + // should be removed completely anyway. b/170086509 + (GetOatHeader().IsValid() && !IsDebuggable()); } bool OatFileBase::LoadVdex(const std::string& vdex_filename, |