CVE-2025-48544 AI-generated analysis
platform/packages/providers/MediaProvider, MediaProvider access checks (AccessChecker / getQueryBuilder)
Removes the internal Bundle extra MediaProvider#INCLUDED_DEFAULT_DIRECTORIES. In its place, an explicit Optional<List<String>> parameter is passed through getQueryBuilder, appendAccessCheckQuery and AccessChecker.getWhereForConstrainedAccess. As a result, only the FUSE directory-rename path supplies the default-directory access SQL, and the contents of an extras Bundle no longer affect it.
Root cause
MediaProvider defined a package-private Bundle key, INCLUDED_DEFAULT_DIRECTORIES ("android:included-default-directories"). The FUSE directory-rename path put it into qbExtras, filled from getIncludedDefaultDirectories(); the removed javadoc describes this as the System Gallery exception for DCIM, Pictures and Movies. AccessChecker.getWhereForDefaultDirectoryMatch(Bundle) read that string list from whatever extras Bundle reached getWhereForConstrainedAccess. For each directory it added a "relative_path LIKE '<dir>/%'" clause as an extra OR option in the constrained-access WHERE clause for the FILES case. The only protection was four extras.remove(INCLUDED_DEFAULT_DIRECTORIES) calls, including ones in query, delete and update. The commit message says the extra "can be misused by setting it in the bundle". Each added OR option widens the set of rows that match the constrained-access clause.
How the fix works
- src/com/android/providers/media/MediaProvider.java: removes the INCLUDED_DEFAULT_DIRECTORIES constant, so the Bundle key no longer exists.
- src/com/android/providers/media/AccessChecker.java:getWhereForConstrainedAccess: the signature changes from (identity, uriType, forWrite, Bundle extras) to (identity, uriType, forWrite, Optional<List<String>> includedDefaultDirectoriesOptional). The default-directory SQL is added only when the Optional isPresent(). getWhereForDefaultDirectoryMatch now takes a List<String> instead of reading the list from a Bundle.
- src/com/android/providers/media/MediaProvider.java:getQueryBuilder / getQueryBuilderInternal / appendAccessCheckQuery: each gains the Optional<List<String>> parameter and passes it, not extras, to getWhereForConstrainedAccess. All other getQueryBuilder call sites, and getWhereClauseForMatchableVisibleFromFilePath, pass Optional.empty().
- src/com/android/providers/media/MediaProvider.java:updateDatabaseForFuseRename (5- and 6-argument overloads) and deleteForFuseRename: gain the Optional parameter and forward it to getQueryBuilder. The 4-argument overload and the oldPathGrantedUri rename path pass Optional.empty().
- src/com/android/providers/media/MediaProvider.java: the directory-rename loop no longer builds a qbExtras Bundle. It passes new Bundle() and Optional.of(getIncludedDefaultDirectories()) directly, which makes it the only caller that supplies a non-empty list.
- src/com/android/providers/media/MediaProvider.java: the four extras.remove(INCLUDED_DEFAULT_DIRECTORIES) / queryArgs.remove(...) calls are deleted because the key no longer exists.
- tests/src/com/android/providers/media/AccessCheckerTest.java: existing tests are updated to pass Optional.empty(). A new assertion checks that Optional.of(DCIM, Pictures, Movies) produces the three RELATIVE_PATH LIKE clauses for FILES.
Completeness
- The fix covers this mechanism. The Bundle key is deleted and the directory list is now carried only as a typed method parameter, so Bundle contents can no longer feed the default-directory clause. The signature change makes every getQueryBuilder call site choose a value explicitly, and all of them except the directory-rename loop pass Optional.empty().
- The diff does not change getIncludedDefaultDirectories() itself, and the material does not show how it is gated.
- getWhereForDefaultDirectoryMatch still builds each LIKE clause by concatenating the directory name into the SQL string. The input now comes only from the internal getIncludedDefaultDirectories() list.
- The qbExtras Bundle parameter remains on updateDatabaseForFuseRename and deleteForFuseRename. Other extras keys, such as QUERY_ARG_REDACTED_URI and QUERY_ARG_RELATED_URI, are still handled by the existing extras.remove pattern, which this fix does not change.
Behavior changes
- A Bundle containing the key android:included-default-directories no longer has any effect on MediaProvider access checks, because the key no longer exists.
- No API or error-code changes. The System Gallery directory-rename behaviour is kept via Optional.of(getIncludedDefaultDirectories()).
- Internal signature changes: AccessChecker.getWhereForConstrainedAccess and MediaProvider.getQueryBuilder, getQueryBuilderInternal, appendAccessCheckQuery, updateDatabaseForFuseRename and deleteForFuseRename gain an Optional<List<String>> parameter.
Per major
- Android 14: Same fix: f79b709905cf in android-security-14.0.0_r22 (cherry-pick of a631dca1a8c0). appendAccessCheckQuery has a single getWhereForConstrainedAccess call. At android-security-14.0.0_r29, 7 of 8 lines of the patched region are present.
- Android 15: Adapted fix: c34a7f642548 in android-security-15.0.0_r10. The logic is the same, but appendAccessCheckQuery has two getWhereForConstrainedAccess call sites (the isLatestSelectionOnlyRequired branches), and both switch to the Optional parameter. At android-security-15.0.0_r17, 7 of 8 lines of the patched region are present.
- Android 16: Adapted fix: a5f1f1827339 in android-security-16.0.0_r1, same shape as Android 15 (two call sites in appendAccessCheckQuery). At android-security-16.0.0_r8, 7 of 8 lines of the patched region are present.
- Android 17: Not affected (not listed by the bulletin). At android-security-17.0.0_r1, 7 of 8 lines of the patched region are present.
Unknowns
- Which MediaProvider entry point let a caller-supplied Bundle containing INCLUDED_DEFAULT_DIRECTORIES reach getQueryBuilder without the key being stripped.
- Whether getQueryBuilder call sites without a strip call (for example the clearLocalCallingIdentity wrapper, the playlist-members delete, and the read/write access probes) received caller-controlled extras.
- How getIncludedDefaultDirectories() is gated (not shown in the material).
- Whether any test beyond the AccessCheckerTest update exercises the end-to-end entry point.