[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
This commit is contained in:
Songchun Fan
2022-04-07 23:24:37 +00:00
parent 7605f736e4
commit 2c8aeb6ae8
4 changed files with 57 additions and 3 deletions

View File

@@ -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<TempUserState> 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;
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -380,8 +380,8 @@ public final class Settings implements Watchable, Snappable {
private final SnapshotCache<WatchedArrayMap<String, PackageSetting>> 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<String> mInstallerPackages;
@@ -5923,4 +5923,8 @@ public final class Settings implements Watchable, Snappable {
}
}
}
boolean isInstallerPackage(@NonNull String packageName) {
return mInstallerPackages.contains(packageName);
}
}