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)