runtime: add -Xdeny-art-apex-data-files

This option prevents the runtime from loading AOT artifacts installed
in /data/misc/apexdata/com.android.art.

Bug: 192049377
Test: manually adding option and running odsign_e2e_tests
Test: adding option and looking at proc/maps for system_server and zygote

Change-Id: I56c7ce55b64de72faf39a06238089fe4b6b84b88
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 629dee8..a1f1945 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -719,7 +719,8 @@
 
     if (!IsBootImage() && boot_image_filename_.empty()) {
       DCHECK(!IsBootImageExtension());
-      boot_image_filename_ = GetDefaultBootImageLocation(android_root_);
+      boot_image_filename_ =
+          GetDefaultBootImageLocation(android_root_, /*deny_art_apex_data_files=*/false);
     }
 
     if (dex_filenames_.empty() && zip_fd_ == -1) {
diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc
index 45c3e3e..cb0023e 100644
--- a/libartbase/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -313,7 +313,8 @@
   return kDefaultBcpExtensionJar;
 }
 
-std::string GetDefaultBootImageLocation(const std::string& android_root) {
+std::string GetDefaultBootImageLocation(const std::string& android_root,
+                                        bool deny_art_apex_data_files) {
   constexpr static const char* kJavalibBootArt = "javalib/boot.art";
   constexpr static const char* kEtcBootImageProf = "etc/boot-image.prof";
 
@@ -321,9 +322,9 @@
   //  - the primary boot image in the ART APEX (contains the Core Libraries)
   //  - the boot image extensions (contains framework libraries) on the system partition, or
   //    in the ART APEX data directory, if an update for the ART module has been been installed.
-  if (kIsTargetBuild) {
+  if (kIsTargetBuild && !deny_art_apex_data_files) {
     // If the ART APEX has been updated, the compiled boot image extension will be in the ART APEX
-    // data directory (assuming there is space). Otherwise, for a factory installed ART APEX it is
+    // data directory (assuming there is space and we trust the artifacts there). Otherwise, for a factory installed ART APEX it is
     // under $ANDROID_ROOT/framework/.
     const std::string first_extension_jar{GetFirstBootClasspathExtensionJar(android_root)};
     const std::string boot_extension_image = GetApexDataBootImage(first_extension_jar);
@@ -354,7 +355,7 @@
   if (android_root.empty()) {
     return "";
   }
-  return GetDefaultBootImageLocation(android_root);
+  return GetDefaultBootImageLocation(android_root, /*deny_art_apex_data_files=*/false);
 }
 
 static std::string GetDalvikCacheDirectory(std::string_view root_directory,
@@ -624,8 +625,11 @@
 #endif
 }
 
-bool LocationIsTrusted(const std::string& location) {
-  return LocationIsOnSystem(location) || LocationIsOnArtApexData(location);
+bool LocationIsTrusted(const std::string& location, bool trust_art_apex_data_files) {
+  if (LocationIsOnSystem(location)) {
+    return true;
+  }
+  return LocationIsOnArtApexData(location) & trust_art_apex_data_files;
 }
 
 bool ArtModuleRootDistinctFromAndroidRoot() {
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
index 6af82ef..c1b0095 100644
--- a/libartbase/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -74,7 +74,8 @@
 std::string GetDefaultBootImageLocation(std::string* error_msg);
 
 // Returns the default boot image location, based on the passed `android_root`.
-std::string GetDefaultBootImageLocation(const std::string& android_root);
+std::string GetDefaultBootImageLocation(const std::string& android_root,
+                                        bool deny_art_apex_data_files);
 
 // Return true if we found the dalvik cache and stored it in the dalvik_cache argument.
 // `have_android_data` will be set to true if we have an ANDROID_DATA that exists,
@@ -152,7 +153,7 @@
 // Returns whether the location is trusted for loading oat files. Trusted locations are protected
 // by dm-verity or fs-verity. The recognized locations are on /system or
 // /data/misc/apexdata/com.android.art.
-bool LocationIsTrusted(const std::string& location);
+bool LocationIsTrusted(const std::string& location, bool trust_art_apex_data_files);
 
 // Compare the ART module root against android root. Returns true if they are
 // both known and distinct. This is meant to be a proxy for 'running with apex'.
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 1518195..67ee940 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -454,7 +454,7 @@
   // zip_file_only_contains_uncompressed_dex_ is only set during fetching the dex checksums.
   DCHECK(required_dex_checksums_attempted_);
   if (only_load_trusted_executable_ &&
-      !LocationIsTrusted(file.GetLocation()) &&
+      !LocationIsTrusted(file.GetLocation(), !Runtime::Current()->DenyArtApexDataFiles()) &&
       file.ContainsDexCode() &&
       zip_file_only_contains_uncompressed_dex_) {
     LOG(ERROR) << "Not loading "
@@ -565,7 +565,7 @@
   // Check if `location` could have an oat file in the ART APEX data directory. If so, and the
   // file exists, use it.
   const std::string apex_data_file = GetApexDataOdexFilename(location, isa);
-  if (!apex_data_file.empty()) {
+  if (!apex_data_file.empty() && !Runtime::Current()->DenyArtApexDataFiles()) {
     if (OS::FileExists(apex_data_file.c_str(), /*check_file_type=*/true)) {
       *oat_filename = apex_data_file;
       return true;
@@ -818,6 +818,12 @@
     return nullptr;
   }
 
+  if (LocationIsOnArtApexData(filename_) && Runtime::Current()->DenyArtApexDataFiles()) {
+    LOG(WARNING) << "OatFileAssistant rejected file " << filename_
+                 << ": ART apexdata is untrusted.";
+    return nullptr;
+  }
+
   std::string error_msg;
   bool executable = oat_file_assistant_->load_executable_;
   if (android::base::EndsWith(filename_, kVdexExtension)) {
@@ -857,7 +863,7 @@
     }
   } else {
     if (executable && oat_file_assistant_->only_load_trusted_executable_) {
-      executable = LocationIsTrusted(filename_);
+      executable = LocationIsTrusted(filename_, /*trust_art_apex_data_files=*/ true);
     }
     VLOG(oat) << "Loading " << filename_ << " with executable: " << executable;
     if (use_fd_) {
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index a891aa3..542ea09 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -72,7 +72,7 @@
 
   WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
   CHECK(!only_use_system_oat_files_ ||
-        LocationIsTrusted(oat_file->GetLocation()) ||
+        LocationIsTrusted(oat_file->GetLocation(), !Runtime::Current()->DenyArtApexDataFiles()) ||
         !oat_file->IsExecutable())
       << "Registering a non /system oat file: " << oat_file->GetLocation();
   DCHECK(oat_file != nullptr);
@@ -756,6 +756,11 @@
     return;
   }
 
+  if (LocationIsOnArtApexData(odex_filename) && Runtime::Current()->DenyArtApexDataFiles()) {
+    // Ignore vdex file associated with this odex file as the odex file is not trustworthy.
+    return;
+  }
+
   {
     WriterMutexLock mu(self, *Locks::oat_file_manager_lock_);
     if (verification_thread_pool_ == nullptr) {
@@ -800,7 +805,11 @@
 
   for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) {
     if (boot_set.find(oat_file.get()) == boot_set.end()) {
-      if (!LocationIsTrusted(oat_file->GetLocation())) {
+      // This method is called during runtime initialization before we can call
+      // Runtime::Current()->DenyArtApexDataFiles(). Since we don't want to fail hard if
+      // the ART APEX data files are untrusted, just treat them as trusted for the check here.
+      const bool trust_art_apex_data_files = true;
+      if (!LocationIsTrusted(oat_file->GetLocation(), trust_art_apex_data_files)) {
         // When the file is not in a trusted location, we check whether the oat file has any
         // AOT or DEX code. It is a fatal error if it has.
         if (CompilerFilter::IsAotCompilationEnabled(oat_file->GetCompilerFilter()) ||
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 99704df..af45213 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -417,6 +417,8 @@
           .IntoKey(M::UseStderrLogger)
       .Define("-Xonly-use-system-oat-files")
           .IntoKey(M::OnlyUseTrustedOatFiles)
+      .Define("-Xdeny-art-apex-data-files")
+          .IntoKey(M::DenyArtApexDataFiles)
       .Define("-Xverifier-logging-threshold=_")
           .WithType<unsigned int>()
           .IntoKey(M::VerifierLoggingThreshold)
@@ -726,7 +728,9 @@
   }
 
   if (!args.Exists(M::CompilerCallbacksPtr) && !args.Exists(M::Image)) {
-    std::string image_locations = GetDefaultBootImageLocation(GetAndroidRoot());
+    const bool deny_art_apex_data_files = args.Exists(M::DenyArtApexDataFiles);
+    std::string image_locations =
+        GetDefaultBootImageLocation(GetAndroidRoot(), deny_art_apex_data_files);
     args.Set(M::Image, ParseStringList<':'>::Split(image_locations));
   }
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index cf96092..7a9c7fa 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1334,6 +1334,12 @@
   // Reload all the flags value (from system properties and device configs).
   ReloadAllFlags(__FUNCTION__);
 
+  deny_art_apex_data_files_ = runtime_options.Exists(Opt::DenyArtApexDataFiles);
+  if (deny_art_apex_data_files_) {
+    // We will run slower without those files if the system has taken an ART APEX update.
+    LOG(WARNING) << "ART APEX data files are untrusted.";
+  }
+
   // Early override for logging output.
   if (runtime_options.Exists(Opt::UseStderrLogger)) {
     android::base::SetLogger(android::base::StderrLogger);
@@ -1400,6 +1406,19 @@
         GetSystemImageFilename(image_locations_[0].c_str(), instruction_set_));
     std::string system_oat_location = ImageHeader::GetOatLocationFromImageLocation(
         image_locations_[0]);
+
+    if (deny_art_apex_data_files_ && (LocationIsOnArtApexData(system_oat_filename) ||
+                                      LocationIsOnArtApexData(system_oat_location))) {
+      // This code path exists for completeness, but we don't expect it to be hit.
+      //
+      // `deny_art_apex_data_files` defaults to false unless set at the command-line. The image
+      // locations come from the -Ximage argument and it would need to be specified as being on
+      // the ART APEX data directory. This combination of flags would say apexdata is compromised,
+      // use apexdata to load image files, which is obviously not a good idea.
+      LOG(ERROR) << "Could not open boot oat file from untrusted location: " << system_oat_filename;
+      return false;
+    }
+
     std::string error_msg;
     std::unique_ptr<OatFile> oat_file(OatFile::Open(/*zip_fd=*/ -1,
                                                     system_oat_filename,
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 2ce631c..ff7eb9b 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -902,6 +902,10 @@
     return result;
   }
 
+  bool DenyArtApexDataFiles() const {
+    return deny_art_apex_data_files_;
+  }
+
   // Whether or not we use MADV_RANDOM on files that are thought to have random access patterns.
   // This is beneficial for low RAM devices since it reduces page cache thrashing.
   bool MAdviseRandomAccess() const {
@@ -1385,6 +1389,9 @@
   // indirection is changed. This is intended only for testing JNI id swapping.
   bool automatically_set_jni_ids_indirection_;
 
+  // True if files in /data/misc/apexdata/com.android.art are considered untrustworthy.
+  bool deny_art_apex_data_files_;
+
   // Saved environment.
   class EnvSnapshot {
    public:
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 44e842b..f5085e0 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -171,6 +171,7 @@
 RUNTIME_OPTIONS_KEY (Unit,                UseStderrLogger)
 
 RUNTIME_OPTIONS_KEY (Unit,                OnlyUseTrustedOatFiles)
+RUNTIME_OPTIONS_KEY (Unit,                DenyArtApexDataFiles)
 RUNTIME_OPTIONS_KEY (unsigned int,        VerifierLoggingThreshold,       100)
 
 RUNTIME_OPTIONS_KEY (bool,                FastClassNotFoundException,     true)