summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin F. Haggerty <haggertk@lineageos.org>2021-06-08 07:22:10 -0600
committerKevin F. Haggerty <haggertk@lineageos.org>2021-06-08 07:22:10 -0600
commit8434c818c0ca5711ed4a184f5edaf690af31fc7c (patch)
tree94482ce76d5155917b5cffd363bc56cfcc73504f
parentaad295f10f6e1218f45669d30409c78d5c4bd4bc (diff)
parenta95b62a8c2bc4cf5e95857fd59c70cb166f71ed4 (diff)
Merge tag 'android-11.0.0_r38' into staging/lineage-18.1_merge-android-11.0.0_r38
Android 11.0.0 Release 38 (RQ3A.210605.005) * tag 'android-11.0.0_r38': Fix dm-test invocation dex2oat_vdex_test: add missing dependency on core boot image. dex2oat_vdex_test: add missing dependency on core boot image. Do not accept vdex with dex sections from .dm files Do not accept vdex with dex sections from .dm files Do not accept vdex with dex sections from .dm files Do not crash on out-of-date oat files. Do not accept vdex with dex sections from .dm files Added extra safety in veridex Change-Id: Ia9861e4c35dc0b3af42a64b8fc285e4066b9165c
-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());