CVE-2026-45517 AI-generated analysis
platform/packages/providers/MediaProvider, photopicker preselection (PickerUriResolver, and MediaQueryForPreSelection on 16/17)
Adds structural checks (scheme, authority, picker path pattern) and a current-user-id check to caller-supplied preselection URIs in PickerUriResolver.processUrisForSelection, ahead of the per-URI permission check, so malformed, foreign-authority or cross-user URIs are dropped before their ids reach the picker query.
Root cause
PickerUriResolver.processUrisForSelection takes the URI strings an app passes for preselection (EXTRA_PICKER_PRE_SELECTION_URIS, delivered as QUERY_ID_SELECTION per the tests) and runs Uri.parse on each. Before the fix, its only check was checkUriPermission(uri, -1, callingUid), after which the URI went into accessibleUris and its id was placed in QUERY_LOCAL_ID_SELECTION / QUERY_CLOUD_ID_SELECTION. Nothing checked that the URI was a content:// URI with MediaStore.AUTHORITY and a valid picker path shape. Nothing checked that the user id segment (content://media/picker/<user-id>/...) matched the current user profile. According to the commit message, the user id check prevents data mismatch during database queries. The bulletin rates the issue High, type information disclosure.
How the fix works
- src/com/android/providers/media/PickerUriResolver.java: adds a static LocalUriMatcher sStaticUriMatcher = new LocalUriMatcher(MediaStore.AUTHORITY) and a new public static isValidPickerUri(Uri). It returns false for null and for any scheme other than ContentResolver.SCHEME_CONTENT. Otherwise it returns true only if sStaticUriMatcher.matchUri(uri, /* allowHidden */ false) returns PICKER_ID or PICKER_GET_CONTENT_ID (16/17 also accept PICKER_TRANSCODED_ID). Binding the matcher to MediaStore.AUTHORITY is what enforces the authority check.
- PickerUriResolver.processUrisForSelection: inside the per-URI loop and before checkUriPermission, skips (continue, with Log.d) any URI that fails isValidPickerUri. It then skips any URI where getUserId(uriForSelection) != UserHandle.myUserId(). Only URIs that pass both checks are permission-checked and added to accessibleUris.
- Android 16/17 only: getUserId(Uri) becomes public, and the same two checks are added to src/com/android/providers/media/photopicker/v2/model/MediaQueryForPreSelection.java, ahead of PickerUriResolver.checkUriPermission(appContext, uri, -1, callingPackageUid).
- Tests: in PickerUriResolverTest, testProcessUrisForSelection_permissionChecks and a cloud-authority test now build picker URIs with UserHandle.myUserId() instead of TEST_USER. 16/17 also add PickerDataLayerV2Test cases for cross-user, malformed, fake-authority and invalid-segment URIs.
Completeness
- Covers the Android 15 processUrisForSelection path: scheme, authority (via LocalUriMatcher bound to MediaStore.AUTHORITY), path pattern (PICKER_ID / PICKER_GET_CONTENT_ID) and user id equality with UserHandle.myUserId().
- The Android 15 commit only touches PickerUriResolver.java and its test. The 16/17 commits also patch photopicker/v2/model/MediaQueryForPreSelection.java and accept PICKER_TRANSCODED_ID. The material does not show whether a v2 MediaQueryForPreSelection path exists on the Android 15 branch.
- The user id check compares against UserHandle.myUserId(), which the commit message describes as the current active user profile. URIs for other profiles are rejected rather than resolved against that profile.
- The existing checkUriPermission step is unchanged. The fix adds filters in front of it and does not change the permission semantics.
Behavior changes
- Preselection URIs are silently dropped, with a Log.d message, unless they have the content scheme and match the PICKER_ID or PICKER_GET_CONTENT_ID pattern of LocalUriMatcher for MediaStore.AUTHORITY (on 16/17, PICKER_TRANSCODED_ID also matches).
- Preselection URIs whose user id segment differs from the current user are silently dropped with a Log.d message.
- A new public static helper, PickerUriResolver.isValidPickerUri(Uri), is introduced. On 16/17, PickerUriResolver.getUserId(Uri) becomes public.
Per major
- Android 14: not affected per the bulletin (not listed). 6/8 lines of the patched region are present at android-security-14.0.0_r29, but no fix is recorded there.
- Android 15: same fix (RESTRICT AUTOMERGE cherry-pick 1b032be59978, Change-Id If872e5ee84f18abde0551fb52ca025a9d3808cfa) in android-security-15.0.0_r17. It touches only PickerUriResolver.java and its test, and accepts PICKER_ID and PICKER_GET_CONTENT_ID.
- Android 16: adapted fix (32217e55e510) in android-security-16.0.0_r8: the same PickerUriResolver changes, plus PICKER_TRANSCODED_ID accepted, getUserId made public, the same checks added to photopicker/v2/model/MediaQueryForPreSelection.java, and new PickerDataLayerV2Test cases.
- Android 17: adapted fix (50d220c204df) in android-security-17.0.0_r1, with the same code changes as the Android 16 commit.
Unknowns
- Whether photopicker/v2/model/MediaQueryForPreSelection.java exists on the Android 15 branch and, if so, why the 15 commit does not patch it.
- The exact LocalUriMatcher path patterns for PICKER_ID / PICKER_GET_CONTENT_ID (not shown). So it is unclear whether getUserId can still throw NumberFormatException after isValidPickerUri passes, and how the surrounding catch handles that.
- The concrete mismatch mechanism and what data was exposed. The material only says the fix prevents data mismatch during database queries.