CVE-2025-48646 AI-generated analysis
platform/frameworks/base, WindowManager / ActivityTaskManager (services/core/java/com/android/server/wm/ActivityStarter.java)
In ActivityStarter's FLAG_ACTIVITY_FORWARD_RESULT trampoline handling, the fix no longer always copies sourceRecord.launchedFromPackage into callingPackage. It now does so only after PackageManagerInternal.getPackageUid shows the package belongs to the same app as callingUid (UserHandle.isSameApp).
Root cause
In ActivityStarter.java, an activity can be started with FLAG_ACTIVITY_FORWARD_RESULT when sourceRecord.launchedFromUid == callingUid. In that case the code treated the source as a trampoline and overwrote callingPackage and callingFeatureId with sourceRecord.launchedFromPackage / launchedFromFeatureId. The removed comment said this was safe because "these two packages come from the same uid". However, the added test shows that launchedFromPackage and launchedFromUid on an ActivityRecord need not match: an activity started through startNextMatchingActivity() can carry the launchedFromPackage of a different (privileged) app while its launchedFromUid is the caller's own uid. Because the copy had no check, the newly started activity could record a launchedFromPackage that the calling uid does not own.
How the fix works
- services/core/java/com/android/server/wm/ActivityStarter.java (FLAG_ACTIVITY_FORWARD_RESULT block, inside the sourceRecord.launchedFromUid == callingUid check): the code reads sourceRecord.launchedFromPackage into a local variable. If it is non-null, the code gets PackageManagerInternal via mService.getPackageManagerInternalLocked() and calls getPackageUid(launchedFromPackage, 0 /* flags */, UserHandle.getUserId(callingUid)).
- callingPackage = launchedFromPackage and callingFeatureId = sourceRecord.launchedFromFeatureId are applied only if UserHandle.isSameApp(packageUid, callingUid) is true. Otherwise callingPackage and callingFeatureId keep the immediate caller's values. The fix also removes the 'this is safe' comment and adds a reference to b/457742426.
- services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java: adds testLaunchedFromPackage_nextMatchingActivity_forwardResult. The test builds P1 -> M1 -> M2, where M2 has launchedFromPackage set to the privileged package but launchedFromUid set to the malicious uid. It mocks mMockPackageManager.getPackageUid for both packages, starts P2 from M2 with FLAG_ACTIVITY_FORWARD_RESULT, and asserts that P2.launchedFromPackage and P2.launchedFromUid are the immediate caller's (malicious) values.
Completeness
- The fix covers the FLAG_ACTIVITY_FORWARD_RESULT branch in ActivityStarter, which rewrites callingPackage and callingFeatureId from sourceRecord. This is the only consumer shown in the material.
- The override is accepted only when UserHandle.isSameApp(packageUid, callingUid) holds.
- This commit does not change how the mismatched ActivityRecord is created in the first place (the test says launchedFromPackage is set to a different app than launchedFromUid via startNextMatchingActivity()). The fix hardens the consumer, not the producer. The material does not show whether other consumers of launchedFromPackage trust it without a uid check.
Behavior changes
- callingPackage and callingFeatureId are now replaced by the source's launchedFromPackage and launchedFromFeatureId only when that package resolves to the same app as callingUid. Otherwise the started activity records the immediate caller's package and feature id.
- When sourceRecord.launchedFromPackage is null, callingPackage is no longer overwritten with null (before the fix it was).
- This start path now makes a PackageManagerInternal.getPackageUid lookup.
Per major
- Android 14: same fix: platform/frameworks/base ad0b8a7b63ac in android-security-14.0.0_r25 (hunk at ActivityStarter.java ~line 990). Per the per-major facts, 5/8 lines of the patched region are present at android-security-14.0.0_r29.
- Android 15: same fix: platform/frameworks/base 04c368b5f527 in android-security-15.0.0_r13, identical hunk at ~line 1030. The test is inserted after the bubble test instead. Per the per-major facts, 5/8 lines of the patched region are present at android-security-15.0.0_r17.
- Android 16: same fix: platform/frameworks/base c148b4fae634 in android-security-16.0.0_r4, identical hunk at ~line 1142. Per the per-major facts, 5/8 lines of the patched region are present at android-security-16.0.0_r8.
- Android 17: not affected: the bulletin does not list Android 17. Per the per-major facts, 5/8 lines of the patched region are present at android-security-17.0.0_r1.
Unknowns
- How launchedFromPackage ends up mismatched with launchedFromUid on the creating path (startNextMatchingActivity) is described only in the test comment. That code is not in the material.
- Whether other consumers of ActivityRecord.launchedFromPackage trust it without a uid check is not shown.
- What getPackageUid returns for a package it cannot resolve, and so exactly how the check behaves in that case, is not shown.
- Why only 5/8 lines of the patched region are counted as present at the latest tags is not explained in the material.
- The material does not say why Android 17 is not listed by the bulletin.