From 2c8aeb6ae8a10bc6603b0ca8e3b0039049139b2d Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Thu, 7 Apr 2022 23:24:37 +0000 Subject: [PATCH] [pm] delete unfinished sessions when installer is uninstalled A malicious app can create many new sessions without any permission. Prior to this CL, those sessions might remain for days even after the app has been uninstalled. This change makes PackageInstallerService promptly abandon unfinished sessions as soon as the installer package is uninstalled. BUG: 219030339 Test: atest android.content.pm.cts.InstallSessionCleanupTest Change-Id: I72c4a29e031cb47c16c9a0fbafdf68b75c4d6a55 --- .../server/pm/DeletePackageHelper.java | 12 ++++++- .../server/pm/PackageInstallerService.java | 34 +++++++++++++++++++ .../server/pm/PackageManagerService.java | 6 ++++ .../java/com/android/server/pm/Settings.java | 8 +++-- 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 7dae22a44cc5f..d3d291ea52ac8 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -108,7 +108,7 @@ final class DeletePackageHelper { } /** - * This method is an internal method that could be get invoked either + * This method is an internal method that could be invoked either * to delete an installed package or to clean up a failed installation. * After deleting an installed package, a broadcast is sent to notify any * listeners that the package has been removed. For cleaning up a failed @@ -146,6 +146,8 @@ final class DeletePackageHelper { int[] allUsers; final int freezeUser; final SparseArray priorUserStates; + + final boolean isInstallerPackage; /** enabled state of the uninstalled application */ synchronized (mPm.mLock) { final Computer computer = mPm.snapshotComputer(); @@ -226,6 +228,8 @@ final class DeletePackageHelper { freezeUser = removeUser; priorUserStates = null; } + + isInstallerPackage = mPm.mSettings.isInstallerPackage(packageName); } synchronized (mPm.mInstallLock) { @@ -324,6 +328,12 @@ final class DeletePackageHelper { } } + if (res && isInstallerPackage) { + final PackageInstallerService packageInstallerService = + mPm.mInjector.getPackageInstallerService(); + packageInstallerService.onInstallerPackageDeleted(uninstalledPs.getAppId(), removeUser); + } + return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 9b5984e09c8b9..e406a1a4bca78 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -861,6 +861,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements synchronized (mSessions) { mSessions.put(sessionId, session); } + mPm.addInstallerPackageName(session.getInstallSource()); mCallbacks.notifySessionCreated(session.sessionId, session.userId); @@ -1735,4 +1736,37 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements .setPackage(sessionInfo.installerPackageName); mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId)); } + + /** + * Abandon unfinished sessions if the installer package has been uninstalled. + * @param installerAppId the app ID of the installer package that has been uninstalled. + * @param userId the user that has the installer package uninstalled. + */ + void onInstallerPackageDeleted(int installerAppId, int userId) { + synchronized (mSessions) { + for (int i = 0; i < mSessions.size(); i++) { + final PackageInstallerSession session = mSessions.valueAt(i); + if (!matchesInstaller(session, installerAppId, userId)) { + continue; + } + // Find parent session and only abandon parent session if installer matches + PackageInstallerSession root = !session.hasParentSessionId() + ? session : mSessions.get(session.getParentSessionId()); + if (root != null && matchesInstaller(root, installerAppId, userId) + && !root.isDestroyed()) { + root.abandon(); + } + } + } + } + + private boolean matchesInstaller(PackageInstallerSession session, int installerAppId, + int userId) { + final int installerUid = session.getInstallerUid(); + if (installerAppId == UserHandle.USER_ALL) { + return UserHandle.getAppId(installerUid) == installerAppId; + } else { + return UserHandle.getUid(userId, installerAppId) == installerUid; + } + } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index e1af9a7ab96f9..ccdd913c9e862 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -7167,4 +7167,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService void notifyInstantAppPackageInstalled(String packageName, int[] newUsers) { mInstantAppRegistry.onPackageInstalled(snapshotComputer(), packageName, newUsers); } + + void addInstallerPackageName(InstallSource installSource) { + synchronized (mLock) { + mSettings.addInstallerPackageNames(installSource); + } + } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index b53cfc558f6e6..e6d59d43ffbe1 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -380,8 +380,8 @@ public final class Settings implements Watchable, Snappable { private final SnapshotCache> mPackagesSnapshot; /** - * List of packages that were involved in installing other packages, i.e. are listed - * in at least one app's InstallSource. + * List of packages that were involved in installing other packages, i.e. packages that created + * new sessions or are listed in at least one app's InstallSource. */ @Watched private final WatchedArraySet mInstallerPackages; @@ -5923,4 +5923,8 @@ public final class Settings implements Watchable, Snappable { } } } + + boolean isInstallerPackage(@NonNull String packageName) { + return mInstallerPackages.contains(packageName); + } }