summaryrefslogtreecommitdiff
path: root/runtime/class_loader_context.cc
diff options
context:
space:
mode:
author Calin Juravle <calin@google.com> 2020-04-06 19:29:45 -0700
committer Calin Juravle <calin@google.com> 2020-04-08 23:49:43 +0000
commitb495e7ffac8782dd2fc7c1a7fa7c69da71d98c1c (patch)
tree723728edcc663257640a6d0b82785992174c6f1b /runtime/class_loader_context.cc
parent82cc2a247d9fb9bb8d1d28a09d978f66d0b1c4dd (diff)
Log an error when an app loads duplicate dex files
Creating a class loader with duplicate dex files in its classpath is most likely an unintended bug. That leads to rejecting any compiled code and hurts performance by extracting in memory. Test: run-test gtest Bug: 149410951 Change-Id: Ieebb69c6bd03acbe95dd8bedb6101d70390b92d8
Diffstat (limited to 'runtime/class_loader_context.cc')
-rw-r--r--runtime/class_loader_context.cc99
1 files changed, 68 insertions, 31 deletions
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 98db62ef38..d2119db3ee 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -1277,6 +1277,43 @@ static inline bool AbsolutePathHasRelativeSuffix(const std::string& path,
(std::string_view(path).substr(/*pos*/ path.size() - suffix.size()) == suffix);
}
+// Returns true if the given dex names are mathing, false otherwise.
+static bool AreDexNameMatching(const std::string& actual_dex_name,
+ const std::string& expected_dex_name) {
+ // Compute the dex location that must be compared.
+ // We shouldn't do a naive comparison `actual_dex_name == expected_dex_name`
+ // because even if they refer to the same file, one could be encoded as a relative location
+ // and the other as an absolute one.
+ bool is_dex_name_absolute = IsAbsoluteLocation(actual_dex_name);
+ bool is_expected_dex_name_absolute = IsAbsoluteLocation(expected_dex_name);
+ bool dex_names_match = false;
+
+ if (is_dex_name_absolute == is_expected_dex_name_absolute) {
+ // If both locations are absolute or relative then compare them as they are.
+ // This is usually the case for: shared libraries and secondary dex files.
+ dex_names_match = (actual_dex_name == expected_dex_name);
+ } else if (is_dex_name_absolute) {
+ // The runtime name is absolute but the compiled name (the expected one) is relative.
+ // This is the case for split apks which depend on base or on other splits.
+ dex_names_match =
+ AbsolutePathHasRelativeSuffix(actual_dex_name, expected_dex_name);
+ } else if (is_expected_dex_name_absolute) {
+ // The runtime name is relative but the compiled name is absolute.
+ // There is no expected use case that would end up here as dex files are always loaded
+ // with their absolute location. However, be tolerant and do the best effort (in case
+ // there are unexpected new use case...).
+ dex_names_match =
+ AbsolutePathHasRelativeSuffix(expected_dex_name, actual_dex_name);
+ } else {
+ // Both locations are relative. In this case there's not much we can be sure about
+ // except that the names are the same. The checksum will ensure that the files are
+ // are same. This should not happen outside testing and manual invocations.
+ dex_names_match = (actual_dex_name == expected_dex_name);
+ }
+
+ return dex_names_match;
+}
+
bool ClassLoaderContext::ClassLoaderInfoMatch(
const ClassLoaderInfo& info,
const ClassLoaderInfo& expected_info,
@@ -1305,37 +1342,7 @@ bool ClassLoaderContext::ClassLoaderInfoMatch(
if (verify_names) {
for (size_t k = 0; k < info.classpath.size(); k++) {
- // Compute the dex location that must be compared.
- // We shouldn't do a naive comparison `info.classpath[k] == expected_info.classpath[k]`
- // because even if they refer to the same file, one could be encoded as a relative location
- // and the other as an absolute one.
- bool is_dex_name_absolute = IsAbsoluteLocation(info.classpath[k]);
- bool is_expected_dex_name_absolute = IsAbsoluteLocation(expected_info.classpath[k]);
- bool dex_names_match = false;
-
-
- if (is_dex_name_absolute == is_expected_dex_name_absolute) {
- // If both locations are absolute or relative then compare them as they are.
- // This is usually the case for: shared libraries and secondary dex files.
- dex_names_match = (info.classpath[k] == expected_info.classpath[k]);
- } else if (is_dex_name_absolute) {
- // The runtime name is absolute but the compiled name (the expected one) is relative.
- // This is the case for split apks which depend on base or on other splits.
- dex_names_match =
- AbsolutePathHasRelativeSuffix(info.classpath[k], expected_info.classpath[k]);
- } else if (is_expected_dex_name_absolute) {
- // The runtime name is relative but the compiled name is absolute.
- // There is no expected use case that would end up here as dex files are always loaded
- // with their absolute location. However, be tolerant and do the best effort (in case
- // there are unexpected new use case...).
- dex_names_match =
- AbsolutePathHasRelativeSuffix(expected_info.classpath[k], info.classpath[k]);
- } else {
- // Both locations are relative. In this case there's not much we can be sure about
- // except that the names are the same. The checksum will ensure that the files are
- // are same. This should not happen outside testing and manual invocations.
- dex_names_match = (info.classpath[k] == expected_info.classpath[k]);
- }
+ bool dex_names_match = AreDexNameMatching(info.classpath[k], expected_info.classpath[k]);
// Compare the locations.
if (!dex_names_match) {
@@ -1393,4 +1400,34 @@ bool ClassLoaderContext::ClassLoaderInfoMatch(
}
}
+std::vector<const DexFile*> ClassLoaderContext::CheckForDuplicateDexFiles(
+ const std::vector<const DexFile*>& dex_files_to_check) {
+ DCHECK(dex_files_open_attempted_);
+ DCHECK(dex_files_open_result_);
+
+ std::vector<const DexFile*> result;
+
+ if (special_shared_library_) {
+ return result;
+ }
+
+ std::vector<ClassLoaderInfo*> work_list;
+ work_list.push_back(class_loader_chain_.get());
+ while (!work_list.empty()) {
+ ClassLoaderInfo* info = work_list.back();
+ work_list.pop_back();
+ for (size_t k = 0; k < info->classpath.size(); k++) {
+ for (const DexFile* dex_file : dex_files_to_check) {
+ if (info->checksums[k] == dex_file->GetLocationChecksum()
+ && AreDexNameMatching(info->classpath[k], dex_file->GetLocation())) {
+ result.push_back(dex_file);
+ }
+ }
+ }
+ AddToWorkList(info, work_list);
+ }
+
+ return result;
+}
+
} // namespace art