summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--artd/artd.cc38
-rw-r--r--artd/artd.h7
-rw-r--r--artd/binder/com/android/server/art/IArtd.aidl13
-rw-r--r--libartservice/service/java/com/android/server/art/DexUseManagerLocal.java75
-rw-r--r--libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java15
5 files changed, 134 insertions, 14 deletions
diff --git a/artd/artd.cc b/artd/artd.cc
index 4558b33988..502942e82a 100644
--- a/artd/artd.cc
+++ b/artd/artd.cc
@@ -1103,6 +1103,44 @@ ScopedAStatus Artd::isInDalvikCache(const std::string& in_dexFile, bool* _aidl_r
return NonFatal(ART_FORMAT("Fstab entries not found for '{}'", in_dexFile));
}
+ScopedAStatus Artd::validateDexPath(const std::string& in_dexPath,
+ std::optional<std::string>* _aidl_return) {
+ if (Result<void> result = ValidateDexPath(in_dexPath); !result.ok()) {
+ *_aidl_return = result.error().message();
+ } else {
+ *_aidl_return = std::nullopt;
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Artd::validateClassLoaderContext(const std::string& in_dexPath,
+ const std::string& in_classLoaderContext,
+ std::optional<std::string>* _aidl_return) {
+ if (in_classLoaderContext == ClassLoaderContext::kUnsupportedClassLoaderContextEncoding) {
+ *_aidl_return = std::nullopt;
+ return ScopedAStatus::ok();
+ }
+
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(in_classLoaderContext);
+ if (context == nullptr) {
+ *_aidl_return = ART_FORMAT("Class loader context '{}' is invalid", in_classLoaderContext);
+ return ScopedAStatus::ok();
+ }
+
+ std::vector<std::string> flattened_context = context->FlattenDexPaths();
+ std::string dex_dir = Dirname(in_dexPath);
+ for (const std::string& context_element : flattened_context) {
+ std::string context_path = std::filesystem::path(dex_dir).append(context_element);
+ if (Result<void> result = ValidateDexPath(context_path); !result.ok()) {
+ *_aidl_return = result.error().message();
+ return ScopedAStatus::ok();
+ }
+ }
+
+ *_aidl_return = std::nullopt;
+ return ScopedAStatus::ok();
+}
+
Result<void> Artd::Start() {
OR_RETURN(SetLogVerbosity());
diff --git a/artd/artd.h b/artd/artd.h
index 2386cfbfa1..a4012c6da1 100644
--- a/artd/artd.h
+++ b/artd/artd.h
@@ -166,6 +166,13 @@ class Artd : public aidl::com::android::server::art::BnArtd {
ndk::ScopedAStatus isInDalvikCache(const std::string& in_dexFile, bool* _aidl_return) override;
+ ndk::ScopedAStatus validateDexPath(const std::string& in_dexPath,
+ std::optional<std::string>* _aidl_return) override;
+
+ ndk::ScopedAStatus validateClassLoaderContext(const std::string& in_dexPath,
+ const std::string& in_classLoaderContext,
+ std::optional<std::string>* _aidl_return) override;
+
android::base::Result<void> Start();
private:
diff --git a/artd/binder/com/android/server/art/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl
index 74403485d5..ec57bd451b 100644
--- a/artd/binder/com/android/server/art/IArtd.aidl
+++ b/artd/binder/com/android/server/art/IArtd.aidl
@@ -177,4 +177,17 @@ interface IArtd {
* Throws fatal and non-fatal errors.
*/
boolean isInDalvikCache(@utf8InCpp String dexFile);
+
+ /**
+ * Returns an error message if the given dex path is invalid, or null if the validation
+ * passes.
+ */
+ @nullable @utf8InCpp String validateDexPath(@utf8InCpp String dexPath);
+
+ /**
+ * Returns an error message if the given class loader context is invalid, or null if the
+ * validation passes.
+ */
+ @nullable @utf8InCpp String validateClassLoaderContext(@utf8InCpp String dexPath,
+ @utf8InCpp String classLoaderContext);
}
diff --git a/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java
index 153e83b54b..89fb542c0a 100644
--- a/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java
@@ -77,6 +77,7 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -562,7 +563,7 @@ public class DexUseManagerLocal {
}
mDexUse = new DexUse();
if (proto != null) {
- mDexUse.fromProto(proto);
+ mDexUse.fromProto(proto, this::validateDexPath, this::validateClassLoaderContext);
}
}
}
@@ -587,7 +588,7 @@ public class DexUseManagerLocal {
return !loader.loadingPackageName().equals(owningPackageName) || loader.isolatedProcess();
}
- private static void validateInputs(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ private void validateInputs(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
@NonNull String loadingPackageName,
@NonNull Map<String, String> classLoaderContextByDexContainerFile) {
if (classLoaderContextByDexContainerFile.isEmpty()) {
@@ -596,11 +597,15 @@ public class DexUseManagerLocal {
for (var entry : classLoaderContextByDexContainerFile.entrySet()) {
Utils.assertNonEmpty(entry.getKey());
- if (!Paths.get(entry.getKey()).isAbsolute()) {
- throw new IllegalArgumentException(String.format(
- "Dex container file path must be absolute, got '%s'", entry.getKey()));
+ String errorMsg = validateDexPath(entry.getKey());
+ if (errorMsg != null) {
+ throw new IllegalArgumentException(errorMsg);
}
Utils.assertNonEmpty(entry.getValue());
+ errorMsg = validateClassLoaderContext(entry.getKey(), entry.getValue());
+ if (errorMsg != null) {
+ throw new IllegalArgumentException(errorMsg);
+ }
}
// TODO(b/253570365): Make the validation more strict.
@@ -615,6 +620,29 @@ public class DexUseManagerLocal {
}
}
+ @Nullable
+ private String validateDexPath(@NonNull String dexPath) {
+ try {
+ return mInjector.getArtd().validateDexPath(dexPath);
+ } catch (RemoteException e) {
+ String errorMsg = "Failed to validate dex path " + dexPath;
+ Log.e(TAG, errorMsg, e);
+ return errorMsg;
+ }
+ }
+
+ @Nullable
+ private String validateClassLoaderContext(
+ @NonNull String dexPath, @NonNull String classLoaderContext) {
+ try {
+ return mInjector.getArtd().validateClassLoaderContext(dexPath, classLoaderContext);
+ } catch (RemoteException e) {
+ String errorMsg = "Failed to validate class loader context " + classLoaderContext;
+ Log.e(TAG, errorMsg, e);
+ return errorMsg;
+ }
+ }
+
/** @hide */
@Nullable
public String getSecondaryClassLoaderContext(
@@ -878,10 +906,12 @@ public class DexUseManagerLocal {
}
}
- void fromProto(@NonNull DexUseProto proto) {
+ void fromProto(@NonNull DexUseProto proto,
+ @NonNull Function<String, String> validateDexPath,
+ @NonNull BiFunction<String, String, String> validateClassLoaderContext) {
for (PackageDexUseProto packageProto : proto.getPackageDexUseList()) {
var packageDexUse = new PackageDexUse();
- packageDexUse.fromProto(packageProto);
+ packageDexUse.fromProto(packageProto, validateDexPath, validateClassLoaderContext);
mPackageDexUseByOwningPackageName.put(
Utils.assertNonEmpty(packageProto.getOwningPackageName()), packageDexUse);
}
@@ -914,7 +944,9 @@ public class DexUseManagerLocal {
}
}
- void fromProto(@NonNull PackageDexUseProto proto) {
+ void fromProto(@NonNull PackageDexUseProto proto,
+ @NonNull Function<String, String> validateDexPath,
+ @NonNull BiFunction<String, String, String> validateClassLoaderContext) {
for (PrimaryDexUseProto primaryProto : proto.getPrimaryDexUseList()) {
var primaryDexUse = new PrimaryDexUse();
primaryDexUse.fromProto(primaryProto);
@@ -922,10 +954,20 @@ public class DexUseManagerLocal {
Utils.assertNonEmpty(primaryProto.getDexFile()), primaryDexUse);
}
for (SecondaryDexUseProto secondaryProto : proto.getSecondaryDexUseList()) {
+ String dexFile = Utils.assertNonEmpty(secondaryProto.getDexFile());
+
+ // Skip invalid dex paths persisted by previous versions.
+ String errorMsg = validateDexPath.apply(dexFile);
+ if (errorMsg != null) {
+ Log.e(TAG, errorMsg);
+ continue;
+ }
+
var secondaryDexUse = new SecondaryDexUse();
- secondaryDexUse.fromProto(secondaryProto);
- mSecondaryDexUseByDexFile.put(
- Utils.assertNonEmpty(secondaryProto.getDexFile()), secondaryDexUse);
+ secondaryDexUse.fromProto(secondaryProto,
+ classLoaderContext
+ -> validateClassLoaderContext.apply(dexFile, classLoaderContext));
+ mSecondaryDexUseByDexFile.put(dexFile, secondaryDexUse);
}
}
}
@@ -972,10 +1014,19 @@ public class DexUseManagerLocal {
}
}
- void fromProto(@NonNull SecondaryDexUseProto proto) {
+ void fromProto(@NonNull SecondaryDexUseProto proto,
+ @NonNull Function<String, String> validateClassLoaderContext) {
Utils.check(proto.hasUserId());
mUserHandle = UserHandle.of(proto.getUserId().getValue());
for (SecondaryDexUseRecordProto recordProto : proto.getRecordList()) {
+ // Skip invalid class loader context persisted by previous versions.
+ String errorMsg = validateClassLoaderContext.apply(
+ Utils.assertNonEmpty(recordProto.getClassLoaderContext()));
+ if (errorMsg != null) {
+ Log.e(TAG, errorMsg);
+ continue;
+ }
+
var record = new SecondaryDexUseRecord();
record.fromProto(recordProto);
mRecordByLoader.put(
diff --git a/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java b/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java
index 5850e617d8..cf27d8cba1 100644
--- a/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java
+++ b/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java
@@ -147,6 +147,9 @@ public class DexUseManagerTest {
mTempFile = File.createTempFile("package-dex-usage", ".pb");
mTempFile.deleteOnExit();
+ lenient().when(mArtd.validateDexPath(any())).thenReturn(null);
+ lenient().when(mArtd.validateClassLoaderContext(any(), any())).thenReturn(null);
+
lenient().when(mInjector.getArtd()).thenReturn(mArtd);
lenient().when(mInjector.getCurrentTimeMillis()).thenReturn(0l);
lenient().when(mInjector.getFilename()).thenReturn(mTempFile.getPath());
@@ -699,9 +702,17 @@ public class DexUseManagerTest {
}
@Test(expected = IllegalArgumentException.class)
- public void testNonAbsoluteKey() {
+ public void testInvalidDexPath() throws Exception {
+ lenient().when(mArtd.validateDexPath(any())).thenReturn("invalid");
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of("/a/b.jar", "PCL[]"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidClassLoaderContext() throws Exception {
+ lenient().when(mArtd.validateClassLoaderContext(any(), any())).thenReturn("invalid");
mDexUseManager.notifyDexContainersLoaded(
- mSnapshot, OWNING_PKG_NAME, Map.of("a/b.jar", "CLC"));
+ mSnapshot, OWNING_PKG_NAME, Map.of("/a/b.jar", "PCL[]"));
}
@Test(expected = IllegalArgumentException.class)