diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2021-04-08 16:40:57 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2021-04-08 16:40:57 +0000 |
commit | e6f5d1ee2edff8e1af22fd86fc8a93b91a20a829 (patch) | |
tree | 22f2166ca2b0a3ba6dbee939d75574fd64e13762 | |
parent | 2235a18908d0fc900cde0448f549e480e919c5cf (diff) | |
parent | 5a0381fa7c4042a3d813bb2c5b9bbf33d245aaf4 (diff) | |
download | art-pie-security-release.tar.gz |
Merge cherrypicks of [14126781, 14126782, 14127202, 14128466, 14127516, 14128057, 14127204, 14128747, 14128708, 14128059, 14128686, 14128127, 14128507, 14128809, 14128810, 14128811, 14128812] into security-aosp-pi-releaseandroid-security-9.0.0_r76android-security-9.0.0_r75android-security-9.0.0_r74android-security-9.0.0_r73android-security-9.0.0_r72android-security-9.0.0_r71android-security-9.0.0_r70android-security-9.0.0_r69pie-security-release
Change-Id: I888b5ee13c99b52a3faafa880aff6549bd90bf04
-rw-r--r-- | build/Android.gtest.mk | 3 | ||||
-rw-r--r-- | dex2oat/Android.bp | 1 | ||||
-rw-r--r-- | dex2oat/dex2oat.cc | 7 | ||||
-rw-r--r-- | dex2oat/dex2oat_test.cc | 112 | ||||
-rw-r--r-- | dex2oat/dex2oat_vdex_test.cc | 135 | ||||
-rw-r--r-- | runtime/dex2oat_environment_test.h | 103 | ||||
-rw-r--r-- | test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java | 83 | ||||
-rwxr-xr-x | test/etc/run-test-jar | 2 |
8 files changed, 334 insertions, 112 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index b483e5f6f2..a3cea4c763 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 \ @@ -157,7 +158,7 @@ ART_GTEST_class_table_test_DEX_DEPS := XandY ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes ART_GTEST_dexlayout_test_DEX_DEPS := ManyMethods -ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps MainUncompressed EmptyUncompressed +ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Dex2oatVdexTestDex ManyMethods Statics VerifierDeps MainUncompressed EmptyUncompressed 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 diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index b793406ba0..420dddd6c3 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -276,6 +276,7 @@ art_cc_test { ], srcs: [ "dex2oat_test.cc", + "dex2oat_vdex_test.cc", "dex2oat_image_test.cc", "linker/elf_writer_test.cc", "linker/image_test.cc", diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index fe927bbc1c..609b806bb9 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1467,6 +1467,11 @@ class Dex2Oat FINAL { LOG(WARNING) << "Could not open vdex file in DexMetadata archive: " << error_msg; } else { input_vdex_file_ = std::make_unique<VdexFile>(input_file.release()); + 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"; } } } @@ -3124,6 +3129,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 a229d7dd71..a48d15d40a 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -47,7 +47,7 @@ namespace art { static constexpr size_t kMaxMethodIds = 65535; -static constexpr bool kDebugArgs = false; + static const char* kDisableCompactDex = "--compact-dex-level=none"; using android::base::StringPrintf; @@ -59,7 +59,6 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { output_ = ""; error_msg_ = ""; - success_ = false; } protected: @@ -89,7 +88,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"; } @@ -175,107 +174,8 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { EXPECT_EQ(expected, actual); } - int Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* error_msg) { - Runtime* runtime = Runtime::Current(); - - const std::vector<gc::space::ImageSpace*>& image_spaces = - runtime->GetHeap()->GetBootImageSpaces(); - if (image_spaces.empty()) { - *error_msg = "No image location found for Dex2Oat."; - return false; - } - std::string image_location = image_spaces[0]->GetImageLocation(); - - std::vector<std::string> argv; - argv.push_back(runtime->GetCompilerExecutable()); - - if (runtime->IsJavaDebuggable()) { - argv.push_back("--debuggable"); - } - runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); - - 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.push_back("--boot-image=" + image_location); - - std::vector<std::string> compiler_options = runtime->GetCompilerOptions(); - argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); - - 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; - } - - int link[2]; - - if (pipe(link) == -1) { - return false; - } - - pid_t pid = fork(); - if (pid == -1) { - return false; - } - - if (pid == 0) { - // We need dex2oat to actually log things. - setenv("ANDROID_LOG_TAGS", "*:d", 1); - dup2(link[1], STDERR_FILENO); - close(link[0]); - close(link[1]); - std::vector<const char*> c_args; - for (const std::string& str : argv) { - c_args.push_back(str.c_str()); - } - c_args.push_back(nullptr); - execv(c_args[0], const_cast<char* const*>(c_args.data())); - exit(1); - UNREACHABLE(); - } else { - close(link[1]); - char buffer[128]; - memset(buffer, 0, 128); - ssize_t bytes_read = 0; - - while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) { - output_ += std::string(buffer, bytes_read); - } - close(link[0]); - int status = -1; - if (waitpid(pid, &status, 0) != -1) { - success_ = (status == 0); - } - return status; - } - } - std::string output_ = ""; std::string error_msg_ = ""; - bool success_ = false; }; class Dex2oatSwapTest : public Dex2oatTest { @@ -299,7 +199,6 @@ class Dex2oatSwapTest : public Dex2oatTest { GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy); CheckValidity(); - ASSERT_TRUE(success_); CheckResult(expect_use); } @@ -524,7 +423,6 @@ class Dex2oatVeryLargeTest : public Dex2oatTest { GenerateOdexForTest(dex_location, odex_location, filter, new_args); CheckValidity(); - ASSERT_TRUE(success_); CheckResult(dex_location, odex_location, app_image_file, @@ -748,7 +646,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); @@ -761,7 +658,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) { @@ -807,7 +703,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, @@ -929,7 +824,6 @@ class Dex2oatUnquickenTest : public Dex2oatTest { } ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file"; CheckResult(dex_location, odex_location); - ASSERT_TRUE(success_); } void RunUnquickenMultiDexCDex() { @@ -972,7 +866,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) { @@ -2003,7 +1896,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(), 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 00a95cc7bd..08c7e0ca3f 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: @@ -203,6 +205,107 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { return odex_dir_; } + int Dex2Oat( + const std::vector<std::string>& dex2oat_args, + std::string* output, + std::string* error_msg) { + Runtime* runtime = Runtime::Current(); + + const std::vector<gc::space::ImageSpace*>& image_spaces = + runtime->GetHeap()->GetBootImageSpaces(); + if (image_spaces.empty()) { + *error_msg = "No image location found for Dex2Oat."; + return false; + } + std::string image_location = image_spaces[0]->GetImageLocation(); + + std::vector<std::string> argv; + argv.push_back(runtime->GetCompilerExecutable()); + + if (runtime->IsJavaDebuggable()) { + argv.push_back("--debuggable"); + } + runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); + + 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.push_back("--boot-image=" + image_location); + + std::vector<std::string> compiler_options = runtime->GetCompilerOptions(); + argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); + + 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; + } + + int link[2]; + + if (pipe(link) == -1) { + ::testing::AssertionFailure() << "Failed to pipe: " << *error_msg; + } + + pid_t pid = fork(); + if (pid == -1) { + ::testing::AssertionFailure() << "Failed to fork: " << *error_msg; + } + + if (pid == 0) { + // We need dex2oat to actually log things. + setenv("ANDROID_LOG_TAGS", "*:d", 1); + dup2(link[1], STDERR_FILENO); + close(link[0]); + close(link[1]); + std::vector<const char*> c_args; + for (const std::string& str : argv) { + c_args.push_back(str.c_str()); + } + c_args.push_back(nullptr); + execv(c_args[0], const_cast<char* const*>(c_args.data())); + exit(1); + UNREACHABLE(); + } else { + close(link[1]); + char buffer[128]; + memset(buffer, 0, 128); + ssize_t bytes_read = 0; + + while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) { + *output += std::string(buffer, bytes_read); + } + close(link[0]); + int status = -1; + if (waitpid(pid, &status, 0) != -1) { + ::testing::AssertionFailure() << "dex2oat fork/exec failed: " << *error_msg; + } + return status; + } + } + private: std::string scratch_dir_; std::string odex_oat_dir_; 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 fa6ef54c93..b3965c91c3 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -750,7 +750,7 @@ if [ "$PREBUILD" = "y" ]; then elif [ "$TEST_DM" = "y" ]; then dex2oat_cmdline="${dex2oat_cmdline} --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} --dump-timings --dm-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.dm" + vdex_cmdline="${dex2oat_cmdline} --copy-dex-files=false --dump-timings --dm-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.dm" elif [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex" fi |