CVE-2025-48559 AI-generated analysis
platform/frameworks/base, AppOps (services/core/java/com/android/server/appop/AppOpsService.java)
AppOpsService.verifyAndGetBypass no longer always accepts attribution tags for the root UID: a tag is now valid only when the proxy package is null or a system package. finishProxyOperation now passes the proxy identity into verification, so unverified tags from non-system proxies are not treated as valid on the finish path.
Root cause
In AppOpsService.verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName, suppressErrorLogs), the uid == Process.ROOT_UID branch returned PackageVerificationResult(null, isAttributionTagValid = true) whatever the proxy was. The special proxied apps branch (RestrictionBypass.UNRESTRICTED) already limited the tag bypass to proxies that are null or system packages; root had no such limit. The commit message says a malicious proxy app could exploit this, and the new code comment describes the risk as abusive apps clogging the appops system with unlimited attribution tags via proxy calls. A second defect: the proxy finish path called finishOperationUnchecked with only the proxied uid/package, and that function called the 3-argument verifyAndGetBypass without proxy information. As a result, for the special proxied apps the attribution tag was reported valid even when the proxy was a non-system app.
How the fix works
- AppOpsService.java:verifyAndGetBypass (6-arg overload): the Process.ROOT_UID branch now returns isAttributionTagValid = isPackageNullOrSystem(proxyPackageName, proxyUid) instead of a constant true. The bypass (null) is unchanged, so root calls without a proxy (proxyPackageName null) behave as before.
- AppOpsService.java:isPackageNullOrSystem (new helper): returns true if packageName is null, true if the appId is in (0, Process.FIRST_APPLICATION_UID), otherwise mPackageManagerInternal.isSystemPackage(packageName).
- AppOpsService.java:verifyAndGetBypass (special proxied apps branch, RestrictionBypass.UNRESTRICTED): the inline proxyIsSystemAppOrNull computation is replaced by the same isPackageNullOrSystem helper, so both branches apply one rule.
- AppOpsService.java:finishOperationUnchecked: a new overload takes (clientId, code, proxyUid, proxyPackageName, proxiedUid, proxiedPackageName, attributionTag) and calls verifyAndGetBypass(proxiedUid, proxiedPackageName, attributionTag, proxyUid, proxyPackageName). The old 5-arg overload delegates to it with proxyUid -1 and proxyPackageName null. Lookups, logging and the isolated-process check now use proxiedUid/proxiedPackageName.
- AppOpsService.java:finishProxyOperation: now calls the new overload with proxyUid and resolvedProxyPackageName as well as the proxied identity, so the finish path checks the tag with proxy-aware logic.
Completeness
- Covers the ROOT_UID branch of verifyAndGetBypass for any caller that supplies proxy information, and makes finishProxyOperation supply it. The special-proxied-apps branch now uses the same helper.
- Non-proxy paths that call the 3-arg verifyAndGetBypass (e.g. setMode, shown in context) and the 5-arg finishOperationUnchecked pass no proxy, so root and the special UIDs still get isAttributionTagValid = true there. The fix does not restrict these paths.
- The helper trusts any proxy appId between 1 and FIRST_APPLICATION_UID-1 without asking the package manager. Proxies with appId 0, a negative appId, or appId >= FIRST_APPLICATION_UID are checked via isSystemPackage. Whether isSystemPackage covers all intended cases (e.g. updated system apps) is not shown in the material.
- noteProxyOperation and startProxyOperation are not modified by this commit. The material does not show their code, so whether they already passed the proxy identity to verifyAndGetBypass is not shown.
- The SDK sandbox branch and the regular app branch of verifyAndGetBypass are unchanged.
Behavior changes
- Attribution tags supplied for proxied uid 0 (root) through a non-system, non-null proxy package are now treated as invalid and set to null on paths that pass the proxy identity.
- finishProxyOperation now checks attribution tag validity with the proxy identity, so for root and the special proxied apps the tag may be set to null on finish where it was previously kept. Error logs on that path now report the proxied uid/package explicitly.
- In the special proxied apps branch, a proxy with appId 0 and a non-null package name is now checked via isSystemPackage. Before, it was trusted without a check.
- No new exception or return code. Failures still result in the tag being set to null or in the existing Slog.e paths.
Per major
- Android 14: Same fix: RESTRICT AUTOMERGE cherry-pick 1c6779245598 in android-security-14.0.0_r22 (frameworks/base). Uses the 5-arg finishOperationUnchecked without virtualDeviceId and inline isolated-process logging.
- Android 15: Adapted fix: 7b88db4928f3 in android-security-15.0.0_r10. Same logic, plus the extra virtualDeviceId parameter on finishOperationUnchecked, the logVerifyAndGetBypassFailure helper, and the op.mDeviceAttributedOps/getPersistentId lookup.
- Android 16: Adapted fix: f745b6763bff in android-security-16.0.0_r1. Same as 15, except the attributed op lookup uses getPersistentDeviceIdForOp(virtualDeviceId, code).
- Android 17: Not affected per the bulletin (Android 17 is not listed). At android-security-17.0.0_r1, 5/8 lines of the patched region are present.
Unknowns
- The code of noteProxyOperation/startProxyOperation and of the 3-arg verifyAndGetBypass wrapper is not in the material, so how the proxy identity flows on those paths is not shown.
- Which binder permission gates the proxy op entry points, and so the exact caller needed to reach the ROOT_UID branch with a non-system proxy.
- The concrete memory or behavior impact checked by AppOpsMemoryUsageTest is not shown.
- Why the patched region shows only partial line matches (4/8 or 5/8) at the latest security tags.