diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2021-05-04 17:48:16 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2021-05-04 17:48:16 +0000 |
commit | 4e6cbddac8c7a49209b6a9381b62c8c60a73307b (patch) | |
tree | a64a6da79ff33aa225c206c410342a1ab6292935 | |
parent | 26c50e14c9049f256eefe12de511db4d4b3b7611 (diff) | |
parent | 9a0b12f40faf7800a867df027bba3a02cc488a5e (diff) | |
download | art-android11-platform-release.tar.gz |
Snap for 7316203 from 9a0b12f40faf7800a867df027bba3a02cc488a5e to rvc-platform-releaseandroid-platform-11.0.0_r9android-platform-11.0.0_r8android-platform-11.0.0_r7android-platform-11.0.0_r40android-platform-11.0.0_r39android-platform-11.0.0_r38android-platform-11.0.0_r37android-platform-11.0.0_r36android-platform-11.0.0_r35android-platform-11.0.0_r34android-platform-11.0.0_r33android-platform-11.0.0_r32android-platform-11.0.0_r31android-platform-11.0.0_r30android-platform-11.0.0_r29android-platform-11.0.0_r28android-platform-11.0.0_r27android-platform-11.0.0_r26android-platform-11.0.0_r25android-platform-11.0.0_r24android-platform-11.0.0_r23android-platform-11.0.0_r22android-platform-11.0.0_r21android-platform-11.0.0_r20android-platform-11.0.0_r19android-platform-11.0.0_r18android-platform-11.0.0_r17android-platform-11.0.0_r16android-platform-11.0.0_r15android-platform-11.0.0_r14android-platform-11.0.0_r13android-platform-11.0.0_r12android-platform-11.0.0_r11android-platform-11.0.0_r10android11-platform-release
Change-Id: I57526e0bf8d55f4b521f697ef53934cea8853f14
-rw-r--r-- | build/Android.gtest.mk | 7 | ||||
-rwxr-xr-x | build/apex/art_apex_test.py | 1 | ||||
-rw-r--r-- | dex2oat/Android.bp | 1 | ||||
-rw-r--r-- | dex2oat/dex2oat.cc | 6 | ||||
-rw-r--r-- | dex2oat/dex2oat_test.cc | 168 | ||||
-rw-r--r-- | dex2oat/dex2oat_vdex_test.cc | 135 | ||||
-rw-r--r-- | runtime/dex2oat_environment_test.h | 58 | ||||
-rw-r--r-- | runtime/elf_file_impl.h | 4 | ||||
-rw-r--r-- | runtime/oat_file.cc | 12 | ||||
-rw-r--r-- | test/Android.bp | 7 | ||||
-rw-r--r-- | test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java | 83 | ||||
-rwxr-xr-x | test/etc/run-test-jar | 2 | ||||
-rw-r--r-- | tools/veridex/flow_analysis.cc | 37 | ||||
-rw-r--r-- | tools/veridex/hidden_api_finder.cc | 9 |
14 files changed, 458 insertions, 72 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index a9855cdf4c..1b2180e508 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -27,6 +27,7 @@ GTEST_DEX_DIRECTORIES := \ AllFields \ DefaultMethods \ DexToDexDecompiler \ + Dex2oatVdexTestDex \ ErroneousA \ ErroneousB \ ErroneousInit \ @@ -211,7 +212,7 @@ ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods Prof ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes ART_GTEST_dexanalyze_test_DEX_DEPS := MultiDex ART_GTEST_dexlayout_test_DEX_DEPS := ManyMethods -ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps MainUncompressedAligned EmptyUncompressed EmptyUncompressedAligned StringLiterals +ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Dex2oatVdexTestDex ManyMethods Statics VerifierDeps MainUncompressedAligned EmptyUncompressed EmptyUncompressedAligned StringLiterals ART_GTEST_dex2oat_image_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi HiddenApiStubs @@ -260,6 +261,10 @@ ART_GTEST_two_runtimes_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_DEFAULT_64) $(TAR ART_GTEST_transaction_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32) ART_GTEST_transaction_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_DEFAULT_64) $(TARGET_CORE_IMAGE_DEFAULT_32) +# The dex2oat_vdex_test test has dependencies on core.oat. +ART_GTEST_dex2oat_vdex_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32) +ART_GTEST_dex2oat_vdex_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_DEFAULT_64) $(TARGET_CORE_IMAGE_DEFAULT_32) + ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \ $(HOST_CORE_IMAGE_optimizing_64) \ $(HOST_CORE_IMAGE_optimizing_32) \ diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py index 6bccdf5926..358ef824ea 100755 --- a/build/apex/art_apex_test.py +++ b/build/apex/art_apex_test.py @@ -798,6 +798,7 @@ class TestingTargetChecker: self._checker.check_art_test_executable('compiler_driver_test') self._checker.check_art_test_executable('dex2oat_image_test') self._checker.check_art_test_executable('dex2oat_test') + self._checker.check_art_test_executable('dex2oat_vdex_test') self._checker.check_art_test_executable('dex_to_dex_decompiler_test') self._checker.check_art_test_executable('elf_writer_test') self._checker.check_art_test_executable('image_test') diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index fb76dd99d0..b2ae48db33 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -447,6 +447,7 @@ art_cc_test { ], srcs: [ "dex2oat_test.cc", + "dex2oat_vdex_test.cc", "dex2oat_image_test.cc", "dex/dex_to_dex_decompiler_test.cc", "driver/compiler_driver_test.cc", diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 56548949f3..abb0bd1367 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1600,6 +1600,10 @@ class Dex2Oat final { LOG(WARNING) << "Could not open vdex file in DexMetadata archive: " << error_msg; } else { input_vdex_file_ = std::make_unique<VdexFile>(std::move(input_file)); + if (input_vdex_file_->HasDexSection()) { + LOG(ERROR) << "The dex metadata is not allowed to contain dex files"; + return false; + } VLOG(verifier) << "Doing fast verification with vdex from DexMetadata archive"; } } @@ -3243,6 +3247,8 @@ static dex2oat::ReturnCode Dex2oat(int argc, char** argv) { // Check early that the result of compilation can be written if (!dex2oat->OpenFile()) { + // Flush close so that the File Guard checks don't fail the assertions. + dex2oat->FlushCloseOutputFiles(); return dex2oat::ReturnCode::kOther; } diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 34c8c5e271..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" @@ -54,7 +56,6 @@ namespace art { -static constexpr bool kDebugArgs = false; static const char* kDisableCompactDex = "--compact-dex-level=none"; using android::base::StringPrintf; @@ -66,7 +67,6 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { output_ = ""; error_msg_ = ""; - success_ = false; } protected: @@ -100,7 +100,7 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { args.insert(args.end(), extra_args.begin(), extra_args.end()); - int status = Dex2Oat(args, error_msg); + int status = Dex2Oat(args, &output_, error_msg); if (oat_file != nullptr) { CHECK_EQ(oat_file->FlushClose(), 0) << "Could not flush and close oat file"; } @@ -207,58 +207,8 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { EXPECT_EQ(expected, actual); } - int Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* error_msg) { - std::vector<std::string> argv; - if (!CommonRuntimeTest::StartDex2OatCommandLine(&argv, error_msg)) { - return false; - } - - Runtime* runtime = Runtime::Current(); - if (!runtime->IsVerificationEnabled()) { - argv.push_back("--compiler-filter=assume-verified"); - } - - if (runtime->MustRelocateIfPossible()) { - argv.push_back("--runtime-arg"); - argv.push_back("-Xrelocate"); - } else { - argv.push_back("--runtime-arg"); - argv.push_back("-Xnorelocate"); - } - - if (!kIsTargetBuild) { - argv.push_back("--host"); - } - - argv.insert(argv.end(), dex2oat_args.begin(), dex2oat_args.end()); - - // We must set --android-root. - const char* android_root = getenv("ANDROID_ROOT"); - CHECK(android_root != nullptr); - argv.push_back("--android-root=" + std::string(android_root)); - - if (kDebugArgs) { - std::string all_args; - for (const std::string& arg : argv) { - all_args += arg + " "; - } - LOG(ERROR) << all_args; - } - - // We need dex2oat to actually log things. - auto post_fork_fn = []() { return setenv("ANDROID_LOG_TAGS", "*:d", 1) == 0; }; - ForkAndExecResult res = ForkAndExec(argv, post_fork_fn, &output_); - if (res.stage != ForkAndExecResult::kFinished) { - *error_msg = strerror(errno); - return -1; - } - success_ = res.StandardSuccess(); - return res.status_code; - } - std::string output_ = ""; std::string error_msg_ = ""; - bool success_ = false; }; class Dex2oatSwapTest : public Dex2oatTest { @@ -282,7 +232,6 @@ class Dex2oatSwapTest : public Dex2oatTest { ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy)); CheckValidity(); - ASSERT_TRUE(success_); CheckResult(expect_use); } @@ -508,7 +457,6 @@ class Dex2oatVeryLargeTest : public Dex2oatTest { ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, filter, new_args)); CheckValidity(); - ASSERT_TRUE(success_); CheckResult(dex_location, odex_location, app_image_file, @@ -730,7 +678,6 @@ class Dex2oatLayoutTest : public Dex2oatTest { /*use_fd=*/ false, /*num_profile_classes=*/ 0); CheckValidity(); - ASSERT_TRUE(success_); // Don't check the result since CheckResult relies on the class being in the profile. image_file_empty_profile = GetImageObjectSectionSize(app_image_file); EXPECT_GT(image_file_empty_profile, 0u); @@ -743,7 +690,6 @@ class Dex2oatLayoutTest : public Dex2oatTest { /*use_fd=*/ false, /*num_profile_classes=*/ 1); CheckValidity(); - ASSERT_TRUE(success_); CheckResult(dex_location, odex_location, app_image_file); if (app_image) { @@ -789,7 +735,6 @@ class Dex2oatLayoutTest : public Dex2oatTest { } ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file"; CheckValidity(); - ASSERT_TRUE(success_); } void CheckResult(const std::string& dex_location, @@ -919,7 +864,6 @@ class Dex2oatUnquickenTest : public Dex2oatTest { for (size_t i = 0; i != checksums1.size(); ++i) { EXPECT_EQ(checksums1[i], checksums2[i]) << i; } - ASSERT_TRUE(success_); } void RunUnquickenMultiDexCDex() { @@ -961,7 +905,6 @@ class Dex2oatUnquickenTest : public Dex2oatTest { ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file"; ASSERT_EQ(vdex_file2->FlushCloseOrErase(), 0) << "Could not flush and close vdex file"; CheckResult(dex_location, odex_location2); - ASSERT_TRUE(success_); } void CheckResult(const std::string& dex_location, const std::string& odex_location) { @@ -2063,7 +2006,6 @@ TEST_F(Dex2oatTest, QuickenedInput) { /* use_fd= */ true)); } ASSERT_EQ(vdex_unquickened->Flush(), 0) << "Could not flush and close vdex file"; - ASSERT_TRUE(success_); { // Check that hte vdex has one dex and compare it to the original one. std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_location2.c_str(), @@ -2549,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/dex2oat/dex2oat_vdex_test.cc b/dex2oat/dex2oat_vdex_test.cc new file mode 100644 index 0000000000..0eddab6190 --- /dev/null +++ b/dex2oat/dex2oat_vdex_test.cc @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string> +#include <vector> + +#include "common_runtime_test.h" +#include "dex2oat_environment_test.h" + +#include "vdex_file.h" +#include "verifier/verifier_deps.h" +#include "ziparchive/zip_writer.h" + +namespace art { + +using verifier::VerifierDeps; + +class Dex2oatVdexTest : public Dex2oatEnvironmentTest { + public: + void TearDown() override { + Dex2oatEnvironmentTest::TearDown(); + + output_ = ""; + error_msg_ = ""; + opened_vdex_files_.clear(); + } + + protected: + bool RunDex2oat(const std::string& dex_location, + const std::string& odex_location, + bool copy_dex_files = false, + const std::vector<std::string>& extra_args = {}) { + std::vector<std::string> args; + args.push_back("--dex-file=" + dex_location); + args.push_back("--oat-file=" + odex_location); + args.push_back("--compiler-filter=" + + CompilerFilter::NameOfFilter(CompilerFilter::Filter::kVerify)); + args.push_back("--runtime-arg"); + args.push_back("-Xnorelocate"); + if (!copy_dex_files) { + args.push_back("--copy-dex-files=false"); + } + args.push_back("--runtime-arg"); + args.push_back("-verbose:verifier,compiler"); + // Use a single thread to facilitate debugging. We only compile tiny dex files. + args.push_back("-j1"); + + args.insert(args.end(), extra_args.begin(), extra_args.end()); + + return Dex2Oat(args, &output_, &error_msg_) == 0; + } + + void CreateDexMetadata(const std::string& vdex, const std::string& out_dm) { + // Read the vdex bytes. + std::unique_ptr<File> vdex_file(OS::OpenFileForReading(vdex.c_str())); + std::vector<uint8_t> data(vdex_file->GetLength()); + ASSERT_TRUE(vdex_file->ReadFully(data.data(), data.size())); + + // Zip the content. + FILE* file = fopen(out_dm.c_str(), "wb"); + ZipWriter writer(file); + writer.StartEntry("primary.vdex", ZipWriter::kAlign32); + writer.WriteBytes(data.data(), data.size()); + writer.FinishEntry(); + writer.Finish(); + fflush(file); + fclose(file); + } + + std::string GetFilename(const std::unique_ptr<const DexFile>& dex_file) { + const std::string& str = dex_file->GetLocation(); + size_t idx = str.rfind('/'); + if (idx == std::string::npos) { + return str; + } + return str.substr(idx + 1); + } + + std::string GetOdex(const std::unique_ptr<const DexFile>& dex_file, + const std::string& suffix = "") { + return GetScratchDir() + "/" + GetFilename(dex_file) + suffix + ".odex"; + } + + std::string GetVdex(const std::unique_ptr<const DexFile>& dex_file, + const std::string& suffix = "") { + return GetScratchDir() + "/" + GetFilename(dex_file) + suffix + ".vdex"; + } + + std::string output_; + std::string error_msg_; + std::vector<std::unique_ptr<VdexFile>> opened_vdex_files_; +}; + +// Check that if the input dm does contain dex files then the compilation fails +TEST_F(Dex2oatVdexTest, VerifyPublicSdkStubsWithDexFiles) { + std::string error_msg; + + // Dex2oatVdexTestDex is the subject app using normal APIs found in the boot classpath. + std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex")); + + // Compile the subject app using the predefined API-stubs + ASSERT_TRUE(RunDex2oat( + dex_file->GetLocation(), + GetOdex(dex_file), + /*copy_dex_files=*/ true)); + + // Create the .dm file with the output. + std::string dm_file = GetScratchDir() + "/base.dm"; + CreateDexMetadata(GetVdex(dex_file), dm_file); + std::vector<std::string> extra_args; + extra_args.push_back("--dm-file=" + dm_file); + + // Recompile again with the .dm file which contains a vdex with code. + // The compilation should fail. + ASSERT_FALSE(RunDex2oat( + dex_file->GetLocation(), + GetOdex(dex_file, "v2"), + /*copy_dex_files=*/ true, + extra_args)); +} + +} // namespace art diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h index fb8a760862..8182e16ff5 100644 --- a/runtime/dex2oat_environment_test.h +++ b/runtime/dex2oat_environment_test.h @@ -39,6 +39,8 @@ namespace art { +static constexpr bool kDebugArgs = false; + // Test class that provides some helpers to set a test up for compilation using dex2oat. class Dex2oatEnvironmentTest : public CommonRuntimeTest { public: @@ -173,6 +175,62 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { return odex_dir_; } + int Dex2Oat(const std::vector<std::string>& dex2oat_args, + std::string* output, + std::string* error_msg) { + std::vector<std::string> argv; + if (!CommonRuntimeTest::StartDex2OatCommandLine(&argv, error_msg)) { + ::testing::AssertionFailure() << "Could not start dex2oat cmd line " << *error_msg; + } + + Runtime* runtime = Runtime::Current(); + if (!runtime->IsVerificationEnabled()) { + argv.push_back("--compiler-filter=assume-verified"); + } + + if (runtime->MustRelocateIfPossible()) { + argv.push_back("--runtime-arg"); + argv.push_back("-Xrelocate"); + } else { + argv.push_back("--runtime-arg"); + argv.push_back("-Xnorelocate"); + } + + if (!kIsTargetBuild) { + argv.push_back("--host"); + } + + argv.insert(argv.end(), dex2oat_args.begin(), dex2oat_args.end()); + + // We must set --android-root. + const char* android_root = getenv("ANDROID_ROOT"); + CHECK(android_root != nullptr); + argv.push_back("--android-root=" + std::string(android_root)); + + if (kDebugArgs) { + std::string all_args; + for (const std::string& arg : argv) { + all_args += arg + " "; + } + LOG(ERROR) << all_args; + } + + // We need dex2oat to actually log things. + auto post_fork_fn = []() { return setenv("ANDROID_LOG_TAGS", "*:d", 1) == 0; }; + ForkAndExecResult res = ForkAndExec(argv, post_fork_fn, output); + if (res.stage != ForkAndExecResult::kFinished) { + *error_msg = strerror(errno); + ::testing::AssertionFailure() << "Failed to finish dex2oat invocation: " << *error_msg; + } + + if (!res.StandardSuccess()) { + // We cannot use ASSERT_TRUE since the method returns an int and not void. + ::testing::AssertionFailure() << "dex2oat fork/exec failed: " << *error_msg; + } + + return res.status_code; + } + private: std::string scratch_dir_; std::string odex_oat_dir_; 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, diff --git a/test/Android.bp b/test/Android.bp index 0f09fcc997..6236739720 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -925,6 +925,7 @@ filegroup { ":art-gtest-jars-AllFields", ":art-gtest-jars-DefaultMethods", ":art-gtest-jars-DexToDexDecompiler", + ":art-gtest-jars-Dex2oatVdexTestDex", ":art-gtest-jars-ErroneousA", ":art-gtest-jars-ErroneousB", ":art-gtest-jars-ErroneousInit", @@ -1233,6 +1234,12 @@ java_library { defaults: ["art-gtest-jars-defaults"], } +java_library { + name: "art-gtest-jars-Dex2oatVdexTestDex", + srcs: ["Dex2oatVdexTestDex/**/*.java"], + defaults: ["art-gtest-jars-defaults"], +} + // The following cases are non-trivial. // Uncompress classes.dex files in the jar file. diff --git a/test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java b/test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java new file mode 100644 index 0000000000..e4862bdd81 --- /dev/null +++ b/test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Check that the classes using publicly listed APIS are verified. + */ + +class AccessPublicCtor { + public Integer foo() { + return new Integer(1); + } +} + +class AccessPublicMethod { + public double foo(Integer i) { + return i.doubleValue(); + } +} + +class AccessPublicMethodFromParent { + public void foo(Integer i) { + i.notify(); + } +} + +class AccessPublicStaticMethod { + public Integer foo() { + return Integer.getInteger("1"); + } +} + +class AccessPublicStaticField { + public int foo() { + return Integer.BYTES; + } +} + +/** + * Check that the classes using non publicly listed APIS are not verified. + */ + +class AccessNonPublicCtor { + public Integer foo() { + return new Integer("1"); + } +} + +class AccessNonPublicMethod { + public float foo(Integer i) { + return i.floatValue(); + } +} + +class AccessNonPublicMethodFromParent { + public void foo(Integer i) { + i.notifyAll(); + } +} + +class AccessNonPublicStaticMethod { + public Integer foo() { + return Integer.getInteger("1", 0); + } +} + +class AccessNonPublicStaticField { + public Class foo() { + return Integer.TYPE; + } +} diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 101fa522cf..7d85b102b5 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -963,7 +963,7 @@ if [ "$PREBUILD" = "y" ]; then elif [ "$TEST_VDEX" = "y" ]; then vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex" elif [ "$TEST_DM" = "y" ]; then - dex2oat_cmdline="${dex2oat_cmdline} --output-vdex=$DEX_LOCATION/oat/$ISA/primary.vdex" + dex2oat_cmdline="${dex2oat_cmdline} --copy-dex-files=false --output-vdex=$DEX_LOCATION/oat/$ISA/primary.vdex" dm_cmdline="zip -qj $DEX_LOCATION/oat/$ISA/$TEST_NAME.dm $DEX_LOCATION/oat/$ISA/primary.vdex" vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --dump-timings --dm-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.dm" elif [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc index 65f236325e..2a8b8a0522 100644 --- a/tools/veridex/flow_analysis.cc +++ b/tools/veridex/flow_analysis.cc @@ -47,6 +47,9 @@ bool VeriFlowAnalysis::IsBranchTarget(uint32_t dex_pc) { } bool VeriFlowAnalysis::MergeRegisterValues(uint32_t dex_pc) { + if (dex_pc >= code_item_accessor_.InsnsSizeInCodeUnits()) { + return false; + } // TODO: Do the merging. Right now, just return that we should continue // the iteration if the instruction has not been visited. if (!instruction_infos_[dex_pc].has_been_visited) { @@ -85,9 +88,14 @@ void VeriFlowAnalysis::FindBranches() { } // Iterate over all instructions and find branching instructions. + const uint32_t max_pc = code_item_accessor_.InsnsSizeInCodeUnits(); for (const DexInstructionPcPair& pair : code_item_accessor_) { const uint32_t dex_pc = pair.DexPc(); const Instruction& instruction = pair.Inst(); + if (dex_pc >= max_pc) { + // We need to prevent abnormal access for outside of code + break; + } if (instruction.IsBranch()) { SetAsBranchTarget(dex_pc + instruction.GetTargetOffset()); @@ -107,22 +115,32 @@ void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, RegisterSource kind, VeriClass* cls, uint32_t source_id) { - current_registers_[dex_register] = RegisterValue( - kind, DexFileReference(&resolver_->GetDexFile(), source_id), cls); + // veridex doesn't do any code verification, so it can be that there are bogus dex + // instructions that update a non-existent register. + if (dex_register < current_registers_.size()) { + current_registers_[dex_register] = RegisterValue( + kind, DexFileReference(&resolver_->GetDexFile(), source_id), cls); + } } void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const RegisterValue& value) { - current_registers_[dex_register] = value; + if (dex_register < current_registers_.size()) { + current_registers_[dex_register] = value; + } } void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const VeriClass* cls) { - current_registers_[dex_register] = - RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls); + if (dex_register < current_registers_.size()) { + current_registers_[dex_register] = + RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls); + } } void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls) { - current_registers_[dex_register] = - RegisterValue(RegisterSource::kConstant, value, DexFileReference(nullptr, 0), cls); + if (dex_register < current_registers_.size()) { + current_registers_[dex_register] = + RegisterValue(RegisterSource::kConstant, value, DexFileReference(nullptr, 0), cls); + } } const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) const { @@ -199,7 +217,12 @@ void VeriFlowAnalysis::AnalyzeCode() { work_list.pop_back(); CHECK(IsBranchTarget(dex_pc)); current_registers_ = *dex_registers_[dex_pc].get(); + const uint32_t max_pc = code_item_accessor_.InsnsSizeInCodeUnits(); while (true) { + if (dex_pc >= max_pc) { + // We need to prevent abnormal access for outside of code + break; + } const uint16_t* insns = code_item_accessor_.Insns() + dex_pc; const Instruction& inst = *Instruction::At(insns); ProcessDexInstruction(inst); diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc index e740cf4cbf..6a5c82e49d 100644 --- a/tools/veridex/hidden_api_finder.cc +++ b/tools/veridex/hidden_api_finder.cc @@ -61,7 +61,14 @@ void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver, for (ClassAccessor accessor : dex_file.GetClasses()) { if (class_filter.Matches(accessor.GetDescriptor())) { for (const ClassAccessor::Method& method : accessor.GetMethods()) { - for (const DexInstructionPcPair& inst : method.GetInstructions()) { + CodeItemInstructionAccessor codes = method.GetInstructions(); + const uint32_t max_pc = codes.InsnsSizeInCodeUnits(); + for (const DexInstructionPcPair& inst : codes) { + if (inst.DexPc() >= max_pc) { + // We need to prevent abnormal access for outside of code + break; + } + switch (inst->Opcode()) { case Instruction::CONST_STRING: { dex::StringIndex string_index(inst->VRegB_21c()); |