CVE-2024-31315 AI-generated analysis
platform/frameworks/base, system_server notification service (com.android.server.notification.ManagedServices)
ManagedServices now checks that a managed service component (e.g. a NotificationListenerService) still declares the required bind permission before rebinding it, and removes approved component entries that lost the permission when their package changes.
Root cause
In services/core/java/com/android/server/notification/ManagedServices.java, two rebind paths only checked that the package or the flattened component name was in the approved list (isPackageOrComponentAllowed): reregisterService, and the enabled branch of the mMutex-guarded path near line 1150. Neither path checked that the resolved ServiceInfo still carried mConfig.bindPermission. As a result, an approved component whose package was updated so that the service no longer declared the bind permission could still be bound. The package-change handling also did not remove such entries from mApproved.
How the fix works
- ManagedServices.java: adds componentHasBindPermission(ComponentName, int userId). It resolves the component with getServiceInfo(component, userId) and returns true only if info is non-null and mConfig.bindPermission.equals(info.permission).
- ManagedServices.java: adds isPackageOrComponentAllowedWithPermission(ComponentName, int userId), which is (approved as flattened component OR approved as package) AND componentHasBindPermission.
- ManagedServices.java:reregisterService now calls isPackageOrComponentAllowedWithPermission(cn, userId) before registerService, in place of the two isPackageOrComponentAllowed checks.
- ManagedServices.java: the mMutex-guarded path near line 1150 ('if (enabled)') makes the same replacement before registerServiceLocked. If the check fails, it logs 'no longer has permission to be bound'.
- ManagedServices.java: adds trimApprovedListsForInvalidServices(packageName, userId). Holding the mApproved lock, it goes through every approval type for the user. Any entry for that package that parses as a ComponentName is removed when componentHasBindPermission is false.
- ManagedServices.java: in the package-change loop over uidList (reached from onPackagesChanged in the new test), trimApprovedListsForInvalidServices(pkgName, userId) is called for each uid where isPackageAllowed is true.
- ManagedServicesTest.java: the existing reregisterService tests now mock ServiceInfo (mockServiceInfoWithMetaData), because getServiceInfo is now consulted. The new testUpgradeAppNoPermissionNoRebind checks that after onPackagesChanged, a component whose ServiceInfo.permission is not the bind permission is no longer enabled, while a sibling component that keeps the permission stays enabled.
Completeness
- Covers the two rebind paths shown in the diff (reregisterService and the mMutex 'enabled' branch), plus the package-change path through trimApprovedListsForInvalidServices.
- trimApprovedListsForInvalidServices only removes entries that parse as a ComponentName. Package-level approval entries (plain package names) stay in mApproved. In the two patched paths, the per-component permission check still applies to those entries.
- trimApprovedListsForInvalidServices is called only when isPackageAllowed(pkgName, userId) is true in the package-change loop. The material does not show other ways mApproved changes (e.g. XML load, migrateToXml, addApprovedList) re-checking the permission.
- The initial binding and discovery paths are not in the diff. The material does not show whether they already filter on the bind permission.
- componentHasBindPermission returns false when getServiceInfo returns null, so a component that cannot be resolved is not rebound (fails closed).
Behavior changes
- reregisterService and the mMutex 'enabled' branch skip binding when the resolved ServiceInfo.permission does not equal the configured bind permission, or when the ServiceInfo cannot be resolved.
- On a package change, approved component entries for that package that lack the bind permission are removed from mApproved. With DEBUG on, this logs 'Removing ... from approved list; no bind permission found'.
- The patched rebind paths now do an extra getServiceInfo lookup per component.
- Tests of reregisterService must mock getServiceInfo, or the rebind no longer happens.
Per major
- Android 14: same fix: commit 0a645bd7c46f (cherry-pick of 24b13a64f9f5) in android-security-14.0.0_r9. At android-security-14.0.0_r29, 7/8 lines of the patched region are present.
- Android 15: not affected per the bulletin. At android-security-15.0.0_r17, 7/8 lines of the patched region are present.
- Android 16: not affected per the bulletin. At android-security-16.0.0_r8, 7/8 lines of the patched region are present.
- Android 17: not affected per the bulletin. At android-security-17.0.0_r1, 5/8 lines of the patched region are present. The reason for the difference is not shown.
Unknowns
- The diff context does not show the name of the method that contains the mMutex 'if (enabled)' hunk near line 1150, or what calls it.
- Whether the initial discovery and registration paths (not in the diff) already filter components by bind permission.
- Which ManagedServices subclasses were the concrete affected surface. The bulletin only gives EoP/High.
- Which code calls reregisterService in production.
- Why only 5/8 lines of the patched region are present at android-security-17.0.0_r1.