Do not accept vdex with dex sections from .dm files

Check that the input vdex does not contain dex files when it originates
from dex metadata files.

(cherry-pick from commit 046cecfed5a4df1e180e3491ba7b566bf50a2026)
(partial cherry-pick from commit
33787687f52798fd5b584edb4c88717d134c367a)
(partial cherry-pick from commit
858cfd81c4fddbc950f9be2e3599b15aabaa501a)

Bug: 111442216
Bug: 178055795
Test: test-art-host

Merged-In: I5b104329af899ea62dc1779800264bad6b5424a4
Change-Id: I5b104329af899ea62dc1779800264bad6b5424a4
(cherry picked from commit eb21b021080547fbddb446935e5a23ec5db8f8e5)
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index a9855cd..95d0f38 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -27,6 +27,7 @@
   AllFields \
   DefaultMethods \
   DexToDexDecompiler \
+  Dex2oatVdexTestDex \
   ErroneousA \
   ErroneousB \
   ErroneousInit \
@@ -211,7 +212,7 @@
 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
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
index 6bccdf5..358ef82 100755
--- a/build/apex/art_apex_test.py
+++ b/build/apex/art_apex_test.py
@@ -798,6 +798,7 @@
     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 fb76dd9..b2ae48d 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -447,6 +447,7 @@
     ],
     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 5654894..abb0bd1 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1600,6 +1600,10 @@
           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 @@
 
   // 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 34c8c5e..ab33c19 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -54,7 +54,6 @@
 
 namespace art {
 
-static constexpr bool kDebugArgs = false;
 static const char* kDisableCompactDex = "--compact-dex-level=none";
 
 using android::base::StringPrintf;
@@ -66,7 +65,6 @@
 
     output_ = "";
     error_msg_ = "";
-    success_ = false;
   }
 
  protected:
@@ -100,7 +98,7 @@
 
     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 +205,8 @@
     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 +230,6 @@
     ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy));
 
     CheckValidity();
-    ASSERT_TRUE(success_);
     CheckResult(expect_use);
   }
 
@@ -508,7 +455,6 @@
     ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, filter, new_args));
 
     CheckValidity();
-    ASSERT_TRUE(success_);
     CheckResult(dex_location,
                 odex_location,
                 app_image_file,
@@ -730,7 +676,6 @@
                          /*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 +688,6 @@
                        /*use_fd=*/ false,
                        /*num_profile_classes=*/ 1);
     CheckValidity();
-    ASSERT_TRUE(success_);
     CheckResult(dex_location, odex_location, app_image_file);
 
     if (app_image) {
@@ -789,7 +733,6 @@
     }
     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 +862,6 @@
     for (size_t i = 0; i != checksums1.size(); ++i) {
       EXPECT_EQ(checksums1[i], checksums2[i]) << i;
     }
-    ASSERT_TRUE(success_);
   }
 
   void RunUnquickenMultiDexCDex() {
@@ -961,7 +903,6 @@
     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 +2004,6 @@
                                     /* 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 0000000..0eddab6
--- /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 fb8a760..8182e16 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 @@
     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/test/Android.bp b/test/Android.bp
index 0f09fcc..6236739 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -925,6 +925,7 @@
         ":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 @@
     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 0000000..e4862bd
--- /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 101fa52..423f9b3 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -965,7 +965,7 @@
   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} ${VDEX_ARGS} --dump-timings --dm-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.dm"
+    vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --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