summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2021-05-04 17:48:16 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2021-05-04 17:48:16 +0000
commit4e6cbddac8c7a49209b6a9381b62c8c60a73307b (patch)
treea64a6da79ff33aa225c206c410342a1ab6292935
parent26c50e14c9049f256eefe12de511db4d4b3b7611 (diff)
parent9a0b12f40faf7800a867df027bba3a02cc488a5e (diff)
downloadart-android11-platform-release.tar.gz
Change-Id: I57526e0bf8d55f4b521f697ef53934cea8853f14
-rw-r--r--build/Android.gtest.mk7
-rwxr-xr-xbuild/apex/art_apex_test.py1
-rw-r--r--dex2oat/Android.bp1
-rw-r--r--dex2oat/dex2oat.cc6
-rw-r--r--dex2oat/dex2oat_test.cc168
-rw-r--r--dex2oat/dex2oat_vdex_test.cc135
-rw-r--r--runtime/dex2oat_environment_test.h58
-rw-r--r--runtime/elf_file_impl.h4
-rw-r--r--runtime/oat_file.cc12
-rw-r--r--test/Android.bp7
-rw-r--r--test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java83
-rwxr-xr-xtest/etc/run-test-jar2
-rw-r--r--tools/veridex/flow_analysis.cc37
-rw-r--r--tools/veridex/hidden_api_finder.cc9
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());