Load /data oat/odex files non-executable when only_use_system_oat_files is set.

Make it still ok to use the vdex and the class status in the oat file.
This means:
- If dex code is uncompressed in APK we'll use that
- If dex code is compressed in APK, we'll use the dex code in the vdex.

This is a temporary solution in order to almost mimic performance-wise what
full stack integrity wants to achieve (priv-apps running from verified,
uncompressed dex code in APK).

This is to assess the impact of full stack integrity on privileged apps.

This CL doesn't ensure any integrity, and doesn't check whether the vdex has
been "tainted".

bug: 30972906

Change-Id: If0a6ab26dd6211290db53b7f02d88d45aff68c0c
diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc
index db49860..c2639a6 100644
--- a/runtime/base/file_utils.cc
+++ b/runtime/base/file_utils.cc
@@ -353,4 +353,9 @@
   return 0;
 }
 
+bool LocationIsOnSystem(const char* location) {
+  UniqueCPtr<const char[]> path(realpath(location, nullptr));
+  return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str());
+}
+
 }  // namespace art
diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h
index e4555ad..cac0950 100644
--- a/runtime/base/file_utils.h
+++ b/runtime/base/file_utils.h
@@ -82,6 +82,9 @@
 // Madvise the largest page aligned region within begin and end.
 int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice);
 
+// Return whether the location is on system (i.e. android root).
+bool LocationIsOnSystem(const char* location);
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_BASE_FILE_UTILS_H_
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 8707e73..20f9aaa 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -71,9 +71,12 @@
 
 OatFileAssistant::OatFileAssistant(const char* dex_location,
                                    const InstructionSet isa,
-                                   bool load_executable)
+                                   bool load_executable,
+                                   bool only_load_system_executable)
     : OatFileAssistant(dex_location,
-                       isa, load_executable,
+                       isa,
+                       load_executable,
+                       only_load_system_executable,
                        -1 /* vdex_fd */,
                        -1 /* oat_fd */,
                        -1 /* zip_fd */) {}
@@ -82,11 +85,13 @@
 OatFileAssistant::OatFileAssistant(const char* dex_location,
                                    const InstructionSet isa,
                                    bool load_executable,
+                                   bool only_load_system_executable,
                                    int vdex_fd,
                                    int oat_fd,
                                    int zip_fd)
     : isa_(isa),
       load_executable_(load_executable),
+      only_load_system_executable_(only_load_system_executable),
       odex_(this, /*is_oat_location*/ false),
       oat_(this, /*is_oat_location*/ true),
       zip_fd_(zip_fd) {
@@ -1120,6 +1125,10 @@
   if (!load_attempted_) {
     load_attempted_ = true;
     if (filename_provided_) {
+      bool executable = oat_file_assistant_->load_executable_;
+      if (executable && oat_file_assistant_->only_load_system_executable_) {
+        executable = LocationIsOnSystem(filename_.c_str());
+      }
       std::string error_msg;
       if (use_fd_) {
         if (oat_fd_ >= 0 && vdex_fd_ >= 0) {
@@ -1128,7 +1137,7 @@
                                     filename_.c_str(),
                                     nullptr,
                                     nullptr,
-                                    oat_file_assistant_->load_executable_,
+                                    executable,
                                     false /* low_4gb */,
                                     oat_file_assistant_->dex_location_.c_str(),
                                     &error_msg));
@@ -1138,7 +1147,7 @@
                                   filename_.c_str(),
                                   nullptr,
                                   nullptr,
-                                  oat_file_assistant_->load_executable_,
+                                  executable,
                                   false /* low_4gb */,
                                   oat_file_assistant_->dex_location_.c_str(),
                                   &error_msg));
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 6c01c1e..a614030 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -119,9 +119,13 @@
   //
   // load_executable should be true if the caller intends to try and load
   // executable code for this dex location.
+  //
+  // only_load_system_executable should be true if the caller intends to have
+  // only oat files from /system loaded executable.
   OatFileAssistant(const char* dex_location,
                    const InstructionSet isa,
-                   bool load_executable);
+                   bool load_executable,
+                   bool only_load_system_executable = false);
 
   // Similar to this(const char*, const InstructionSet, bool), however, if a valid zip_fd is
   // provided, vdex, oat, and zip files will be read from vdex_fd, oat_fd and zip_fd respectively.
@@ -129,6 +133,7 @@
   OatFileAssistant(const char* dex_location,
                    const InstructionSet isa,
                    bool load_executable,
+                   bool only_load_system_executable,
                    int vdex_fd,
                    int oat_fd,
                    int zip_fd);
@@ -487,6 +492,9 @@
   // Whether we will attempt to load oat files executable.
   bool load_executable_ = false;
 
+  // Whether only oat files on /system are loaded executable.
+  const bool only_load_system_executable_ = false;
+
   // Cached value of the required dex checksums.
   // This should be accessed only by the GetRequiredDexChecksums() method.
   std::vector<uint32_t> cached_required_dex_checksums_;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index a98da0f..50f5e7a 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -246,6 +246,7 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
                                       kRuntimeISA,
                                       false,
+                                      false,
                                       vdex_fd.get(),
                                       odex_fd.get(),
                                       zip_fd.get());
@@ -285,6 +286,7 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
                                       kRuntimeISA,
                                       false,
+                                      false,
                                       vdex_fd.get(),
                                       -1 /* oat_fd */,
                                       zip_fd.get());
@@ -319,6 +321,7 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
                                       kRuntimeISA,
                                       false,
+                                      false,
                                       -1 /* vdex_fd */,
                                       odex_fd.get(),
                                       zip_fd.get());
@@ -342,6 +345,7 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
                                       kRuntimeISA,
                                       false,
+                                      false,
                                       -1 /* vdex_fd */,
                                       -1 /* oat_fd */,
                                       zip_fd);
@@ -1439,6 +1443,60 @@
                 default_filter, false, false, relative_context.get()));
 }
 
+TEST_F(OatFileAssistantTest, SystemOdex) {
+  std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
+  std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
+  std::string system_location = GetAndroidRoot() + "/OatUpToDate.jar";
+
+  std::string error_msg;
+
+  Copy(GetDexSrc1(), dex_location);
+  EXPECT_FALSE(LocationIsOnSystem(dex_location.c_str()));
+
+  {
+    OatFileAssistant oat_file_assistant(dex_location.c_str(),
+                                        kRuntimeISA,
+                                        true,
+                                        false);
+    int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+    EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+    EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
+  }
+
+  {
+    OatFileAssistant oat_file_assistant(dex_location.c_str(),
+                                        kRuntimeISA,
+                                        true,
+                                        true);
+    int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+    EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+    EXPECT_FALSE(oat_file_assistant.GetBestOatFile()->IsExecutable());
+  }
+
+  Copy(GetDexSrc1(), system_location);
+  EXPECT_TRUE(LocationIsOnSystem(system_location.c_str()));
+
+  {
+    OatFileAssistant oat_file_assistant(system_location.c_str(),
+                                        kRuntimeISA,
+                                        true,
+                                        false);
+    int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+    EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+    EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
+  }
+
+  {
+    OatFileAssistant oat_file_assistant(system_location.c_str(),
+                                        kRuntimeISA,
+                                        true,
+                                        true);
+    int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+    EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+    EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
+  }
+}
+
 // TODO: More Tests:
 //  * Test class linker falls back to unquickened dex for DexNoOat
 //  * Test class linker falls back to unquickened dex for MultiDexNoOat
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 91a138a..e77d4d8 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -54,15 +54,11 @@
 // If true, we attempt to load the application image if it exists.
 static constexpr bool kEnableAppImage = true;
 
-static bool OatFileIsOnSystem(const std::unique_ptr<const OatFile>& oat_file) {
-  UniqueCPtr<const char[]> path(realpath(oat_file->GetLocation().c_str(), nullptr));
-  return path != nullptr && android::base::StartsWith(oat_file->GetLocation(),
-                                                      GetAndroidRoot().c_str());
-}
-
 const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr<const OatFile> oat_file) {
   WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
-  CHECK(!only_use_system_oat_files_ || OatFileIsOnSystem(oat_file))
+  CHECK(!only_use_system_oat_files_ ||
+        LocationIsOnSystem(oat_file->GetLocation().c_str()) ||
+        !oat_file->IsExecutable())
       << "Registering a non /system oat file: " << oat_file->GetLocation();
   DCHECK(oat_file != nullptr);
   if (kIsDebugBuild) {
@@ -422,7 +418,8 @@
 
   OatFileAssistant oat_file_assistant(dex_location,
                                       kRuntimeISA,
-                                      !runtime->IsAotCompiler());
+                                      !runtime->IsAotCompiler(),
+                                      only_use_system_oat_files_);
 
   // Lock the target oat location to avoid races generating and loading the
   // oat file.
@@ -435,8 +432,7 @@
 
   const OatFile* source_oat_file = nullptr;
 
-  // No point in trying to make up-to-date if we can only use system oat files.
-  if (!only_use_system_oat_files_ && !oat_file_assistant.IsUpToDate()) {
+  if (!oat_file_assistant.IsUpToDate()) {
     // Update the oat file on disk if we can, based on the --compiler-filter
     // option derived from the current runtime options.
     // This may fail, but that's okay. Best effort is all that matters here.
@@ -472,9 +468,7 @@
   // Get the oat file on disk.
   std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
 
-  if (oat_file != nullptr && only_use_system_oat_files_ && !OatFileIsOnSystem(oat_file)) {
-    // If the oat file is not on /system, don't use it.
-  } else  if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
+  if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
     // Prevent oat files from being loaded if no class_loader or dex_elements are provided.
     // This can happen when the deprecated DexFile.<init>(String) is called directly, and it
     // could load oat files without checking the classpath, which would be incorrect.
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index dd6b7ba..038474e 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -127,6 +127,9 @@
 
   std::set<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_);
   bool have_non_pic_oat_file_;
+
+  // Only use the compiled code in an OAT file when the file is on /system. If the OAT file
+  // is not on /system, don't load it "executable".
   bool only_use_system_oat_files_;
 
   DISALLOW_COPY_AND_ASSIGN(OatFileManager);