From e757ab24c8f1e6fa054d90da3a89c004aeba019e Mon Sep 17 00:00:00 2001 From: Jackal Guo Date: Thu, 25 Feb 2021 16:21:02 +0800 Subject: [PATCH] Grant package visibility to dependent library Packages should be granted visibility to packages that they list in their tags. Bug: 174856119 Test: atest AppsFilterTest Test: manual test by using an application without permission nor tag and try to get the package info of the package which declares the library. Change-Id: Idce9c457e238c4a34a8bdca089ce5f86b7f62177 --- .../com/android/server/pm/AppsFilter.java | 47 +++++++ .../com/android/server/pm/AppsFilterTest.java | 117 ++++++++++++++++++ 2 files changed, 164 insertions(+) diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 1acbabda9e199..ca8202f5f94b5 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -104,6 +104,12 @@ public class AppsFilter implements Watchable, Snappable { */ private final SparseSetArray mQueriesViaComponent = new SparseSetArray<>(); + /** + * A mapping from the set of App IDs that query other App IDs via library name to the + * list of packages that they can see. + */ + private final SparseSetArray mQueryableViaUsesLibrary = new SparseSetArray<>(); + /** * Executor for running reasonably short background tasks such as building the initial * visibility cache. @@ -239,6 +245,7 @@ public class AppsFilter implements Watchable, Snappable { Snapshots.copy(mImplicitlyQueryable, orig.mImplicitlyQueryable); Snapshots.copy(mQueriesViaPackage, orig.mQueriesViaPackage); Snapshots.copy(mQueriesViaComponent, orig.mQueriesViaComponent); + Snapshots.copy(mQueryableViaUsesLibrary, orig.mQueryableViaUsesLibrary); mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute; mForceQueryable.addAll(orig.mForceQueryable); mForceQueryableByDevicePackageNames = orig.mForceQueryableByDevicePackageNames; @@ -508,6 +515,22 @@ public class AppsFilter implements Watchable, Snappable { return false; } + private static boolean canQueryViaUsesLibrary(AndroidPackage querying, + AndroidPackage potentialTarget) { + if (potentialTarget.getLibraryNames().isEmpty()) { + return false; + } + final List libNames = potentialTarget.getLibraryNames(); + for (int i = 0, size = libNames.size(); i < size; i++) { + final String libName = libNames.get(i); + if (querying.getUsesLibraries().contains(libName) + || querying.getUsesOptionalLibraries().contains(libName)) { + return true; + } + } + return false; + } + private static boolean matchesProviders( Set queriesAuthorities, AndroidPackage potentialTarget) { for (int p = ArrayUtils.size(potentialTarget.getProviders()) - 1; p >= 0; p--) { @@ -707,6 +730,9 @@ public class AppsFilter implements Watchable, Snappable { || canQueryAsInstaller(existingSetting, newPkg)) { mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId); } + if (canQueryViaUsesLibrary(existingPkg, newPkg)) { + mQueryableViaUsesLibrary.add(existingSetting.appId, newPkgSetting.appId); + } } // now we'll evaluate our new package's ability to see existing packages if (!mForceQueryable.contains(existingSetting.appId)) { @@ -718,6 +744,9 @@ public class AppsFilter implements Watchable, Snappable { || canQueryAsInstaller(newPkgSetting, existingPkg)) { mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId); } + if (canQueryViaUsesLibrary(newPkg, existingPkg)) { + mQueryableViaUsesLibrary.add(newPkgSetting.appId, existingSetting.appId); + } } // if either package instruments the other, mark both as visible to one another if (newPkgSetting.pkg != null && existingSetting.pkg != null @@ -1035,6 +1064,10 @@ public class AppsFilter implements Watchable, Snappable { for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) { mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId); } + mQueryableViaUsesLibrary.remove(setting.appId); + for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) { + mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), setting.appId); + } mForceQueryable.remove(setting.appId); @@ -1315,6 +1348,18 @@ public class AppsFilter implements Watchable, Snappable { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary"); + if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queryable for library users"); + } + return false; + } + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + return true; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -1394,6 +1439,8 @@ public class AppsFilter implements Watchable, Snappable { filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), mImplicitlyQueryable, " ", expandPackages); } + pw.println(" queryable via uses-library:"); + dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", expandPackages); } private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId, diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index 9a52643b57f2b..9f428c7cbded5 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -31,6 +31,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageParser; +import android.content.pm.PackageParser.SigningDetails; import android.content.pm.Signature; import android.content.pm.UserInfo; import android.content.pm.parsing.ParsingPackage; @@ -141,6 +142,10 @@ public class AppsFilterTest { return pkg(packageName).addReceiver(receiver); } + private static ParsingPackage pkgWithSharedLibrary(String packageName, String libName) { + return pkg(packageName).addLibraryName(libName); + } + private static ParsedActivity createActivity(String packageName, IntentFilter[] filters) { ParsedActivity activity = new ParsedActivity(); activity.setPackageName(packageName); @@ -412,6 +417,118 @@ public class AppsFilterTest { SYSTEM_USER)); } + @Test + public void testNoUsesLibrary_Filters() throws Exception { + final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, + mMockExecutor); + + simulateAddBasicAndroid(appsFilter); + appsFilter.onSystemReady(); + + final Signature mockSignature = Mockito.mock(Signature.class); + final SigningDetails mockSigningDetails = new SigningDetails( + new Signature[]{mockSignature}, + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2); + + final PackageSetting target = simulateAddPackage(appsFilter, + pkgWithSharedLibrary("com.some.package", "com.some.shared_library"), + DUMMY_TARGET_APPID, + setting -> setting.setSigningDetails(mockSigningDetails) + .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); + final PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package"), DUMMY_CALLING_APPID); + + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); + } + + @Test + public void testUsesLibrary_DoesntFilter() throws Exception { + final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, + mMockExecutor); + + simulateAddBasicAndroid(appsFilter); + appsFilter.onSystemReady(); + + final Signature mockSignature = Mockito.mock(Signature.class); + final SigningDetails mockSigningDetails = new SigningDetails( + new Signature[]{mockSignature}, + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2); + + final PackageSetting target = simulateAddPackage(appsFilter, + pkgWithSharedLibrary("com.some.package", "com.some.shared_library"), + DUMMY_TARGET_APPID, + setting -> setting.setSigningDetails(mockSigningDetails) + .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); + final PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package").addUsesLibrary("com.some.shared_library"), + DUMMY_CALLING_APPID); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); + } + + @Test + public void testUsesOptionalLibrary_DoesntFilter() throws Exception { + final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, + mMockExecutor); + + simulateAddBasicAndroid(appsFilter); + appsFilter.onSystemReady(); + + final Signature mockSignature = Mockito.mock(Signature.class); + final SigningDetails mockSigningDetails = new SigningDetails( + new Signature[]{mockSignature}, + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2); + + final PackageSetting target = simulateAddPackage(appsFilter, + pkgWithSharedLibrary("com.some.package", "com.some.shared_library"), + DUMMY_TARGET_APPID, + setting -> setting.setSigningDetails(mockSigningDetails) + .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); + final PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package").addUsesOptionalLibrary("com.some.shared_library"), + DUMMY_CALLING_APPID); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); + } + + @Test + public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception { + final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, + mMockExecutor); + + simulateAddBasicAndroid(appsFilter); + appsFilter.onSystemReady(); + + final Signature mockSignature = Mockito.mock(Signature.class); + final SigningDetails mockSigningDetails = new SigningDetails( + new Signature[]{mockSignature}, + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2); + + final PackageSetting target = simulateAddPackage(appsFilter, + pkgWithSharedLibrary("com.some.package", "com.some.shared_library"), + DUMMY_TARGET_APPID, + setting -> setting.setSigningDetails(mockSigningDetails) + .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); + final PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package_a").setSharedUserId("com.some.uid"), + DUMMY_CALLING_APPID); + simulateAddPackage(appsFilter, pkg("com.some.other.package_b") + .setSharedUserId("com.some.uid").addUsesLibrary("com.some.shared_library"), + DUMMY_CALLING_APPID); + + // Although package_a doesn't use library, it should be granted visibility. It's because + // package_a shares userId with package_b, and package_b uses that shared library. + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); + } + @Test public void testForceQueryable_SystemDoesntFilter() throws Exception { final AppsFilter appsFilter =