CVE-2025-48526 AI-generated analysis
platform/packages/modules/IntentResolver, ChooserActivity (share sheet), cross-profile target resolution
ChooserActivity now gives the adapter for the non-launched (cross-profile) profile a sanitized copy of the payload intents instead of the caller's original intents. In the copy, package and component are cleared, and so are the selector's package and component.
Root cause
In ChooserActivity.java, the method that builds the profile pager adapters created the personal and the work ChooserGridAdapter with the same unmodified payload intents (mIntents on Android 14, request.getPayloadIntents() on 15/16). Explicit targeting supplied by the caller (package/component, or a selector carrying them) therefore reached the other profile's adapter unchanged. The 15/16 kdoc on ChooserRequest.crossProfilePayloadIntents says such explicit intents "could otherwise be used to bypass the device's cross-profile sharing policy settings". Only initialIntents were already limited to the launched-as profile; payload intents were not.
How the fix works
- java/src/com/android/intentresolver/util/IntentUtils.kt (new): sanitizePayloadIntents(intents) returns a new list. Each intent is copied via Intent(intent), then setPackage(null) and setComponent(null) are called on the copy. If the copy has a selector, it is replaced with a copy of the selector that also has package and component cleared. On Android 14 the function takes List<Intent?> and keeps null entries; on 15/16 it takes List<Intent>.
- Android 14, java/src/com/android/intentresolver/ChooserActivity.java (the pager adapter creation method that takes filterLastUsed and TargetDataLoader): computes crossProfileIntents = sanitizePayloadIntents(mIntents) once. It then passes mIntents to the adapter for the profile equal to findSelectedProfile() (PROFILE_PERSONAL or PROFILE_WORK) and crossProfileIntents to the other adapter. initialIntents handling is unchanged: they still go only to the selected profile.
- Android 15/16, java/src/com/android/intentresolver/data/model/ChooserRequest.kt: adds val crossProfilePayloadIntents by lazy { sanitizePayloadIntents(payloadIntents) } next to payloadIntents.
- Android 15/16, ChooserActivity.createMultiProfilePagerAdapter: drops the initialIntents parameter (both call sites updated) and reads request.getInitialIntents() directly. For each profile it computes isCrossProfile = !profile.equals(launchedAs) and calls createChooserGridAdapter with request.getCrossProfilePayloadIntents() when isCrossProfile is true, otherwise request.getPayloadIntents(). Initial intents are passed only when isCrossProfile is false.
- Android 15/16, tests/unit/.../util/IntentUtilsTest.kt (new): checks that package and component are null on the sanitized intents and on their selectors, for four input shapes. The Profile.kt changes are trailing-comma and import formatting only, with no behavior change.
Completeness
- Covers the Chooser's payload intents (target intent plus additional targets) for every profile other than the launched-as profile. Package, component, and the selector's package and component are cleared on copies, so the launched-as profile still receives the original explicit intents.
- Only package and component (and the same two fields on the selector) are removed. Other intent fields (action, data, type, categories, extras, ClipData, flags) pass through unchanged. The material does not show whether any of those fields can carry targeting information.
- initialIntents are not sanitized. The existing code already passes them only to the launched-as profile adapter (null for the other), so they do not reach cross-profile resolution.
- On Android 14 the change is limited to the single adapter-creation path in ChooserActivity, which builds explicit personal/work adapters. The diff does not touch the ResolverActivity (non-chooser) path, and the material does not show whether that path has the same issue.
- At android-security-14.0.0_r29, ChooserActivity imports prepareCrossProfileIntents from IntentUtils, and IntentUtils.kt has a prepareCrossProfileIntents(contentResolver, targetIntent, ...) function that references IntentForwarderActivity. This points to a later rework of this logic whose full content is not in the material.
Behavior changes
- Targets in the other profile's tab are now resolved from implicit versions of the payload intents. An explicit package/component (or explicit selector) supplied by the sharing app no longer constrains or selects targets in the cross-profile tab, so the set of cross-profile targets may differ from before.
- The intent delivered to a cross-profile target no longer carries the caller's package/component or an explicit selector.
- Android 15/16: the createMultiProfilePagerAdapter signature changed (initialIntents parameter removed). The sanitized list is computed lazily on first access via ChooserRequest.crossProfilePayloadIntents.
- Android 14: each call of the adapter-creation method makes one extra Intent copy per payload intent (and one per selector).
Per major
- Android 14: Same fix (original commit 4ffdecbbef8a, android-security-14.0.0_r22): adds IntentUtils.kt, and ChooserActivity's explicit personal/work adapter creation picks mIntents or sanitizePayloadIntents(mIntents) based on findSelectedProfile(). All 8/8 patched lines are present at android-security-14.0.0_r29. In that tag IntentUtils also has prepareCrossProfileIntents, which ChooserActivity imports (follow-up content not shown).
- Android 15: Adapted fix (923a5673ac9d, android-security-15.0.0_r10): the sanitization is exposed as ChooserRequest.crossProfilePayloadIntents (lazy). createMultiProfilePagerAdapter loops over profileHelper.getProfiles() and uses isCrossProfile to pick the list. The initialIntents parameter is removed and a unit test is added. 6/8 lines are present at android-security-15.0.0_r17, so the region has since changed.
- Android 16: Adapted fix (247af9e3e487, android-security-16.0.0_r1): same shape as 15, but the loop runs over the ProfileRecord collection. ChooserRequest.kt gets the same lazy property. 6/8 lines are present at android-security-16.0.0_r8.
- Android 17: Not affected (not listed by the bulletin). Only 3/8 lines of the patched region are present at android-security-17.0.0_r1.
Unknowns
- The exact policy bypass and the EoP outcome. The 14 commit message only says 'Sanitize cross-profile intents', and the 15/16 kdoc only mentions bypassing cross-profile sharing policy settings.
- The exact trigger path, including whether user interaction (selecting a target in the other profile's tab) is required.
- How the cross-profile sharing policy decides whether targets appear in the other profile's tab (for example, whether a blocked direction shows an empty state).
- Whether ResolverActivity (non-chooser resolution) has the same pattern; the diff does not cover it.
- The content and purpose of the later prepareCrossProfileIntents rework visible at android-security-14.0.0_r29.
- The Android 13 fix (13 is listed as affected by the bulletin) is not in the material.
- What the code at the patched region looks like on Android 17.