CVE-2025-26417 AI-generated analysis
platform/packages/providers/DownloadProvider, DownloadProvider content provider (insert path / destination file validation)
Adds an ownership check to DownloadProvider's insert destination-path validation: if MediaStore already has a record for the target file path, the calling UID must own a package equal to that record's OWNER_PACKAGE_NAME, otherwise a SecurityException is thrown.
Root cause
In src/com/android/providers/downloads/DownloadProvider.java, the insert path validated a caller-supplied destination file via Helpers.checkDestinationFilePathRestrictions (with allowDownloadsDirOnly=true). It did not check whether the target file already existed as a MediaStore record owned by another package. Per the commit message, this let an app access any file in Downloads through the insert method, whichever package owned it. The bulletin classifies this as information disclosure.
How the fix works
- DownloadProvider.java, destination-check method around line 1120 (the one calling Helpers.checkDestinationFilePathRestrictions with allowDownloadsDirOnly=true): after the existing restriction check, a new call checkWhetherCallingAppHasAccess(file.getPath(), Binder.getCallingUid()) is added.
- DownloadProvider.java:checkWhetherCallingAppHasAccess (new private method): acquires a ContentProviderClient for MediaStore.AUTHORITY, builds the files URI with Helpers.getContentUriForPath(getContext(), filePath) wrapped in MediaStore.setIncludePending so pending rows are included, then queries _ID and OWNER_PACKAGE_NAME with selection DATA=? bound to filePath.
- If a row is found and its OWNER_PACKAGE_NAME is non-null, the method gets getPackageManager().getPackagesForUid(uid) for the calling UID and requires that list to contain the owner package name. If it does not, the method throws SecurityException("Caller does not have access to this path"), which aborts the insert.
- Adds import java.util.Arrays for the Arrays.asList membership test.
Completeness
- Covers only the insert destination-path validation that calls Helpers.checkDestinationFilePathRestrictions. The diff changes no other DownloadProvider entry points.
- Fail-open behavior: if the MediaStore ContentProviderClient cannot be acquired (client == null) or the query throws RemoteException, the method logs a warning and returns without denying the insert.
- If no MediaStore row matches DATA=filePath, the insert is allowed, so the check does not cover files in Downloads that MediaStore has not indexed.
- If the matching row's OWNER_PACKAGE_NAME is null, or getPackagesForUid returns null, the check is skipped and the insert proceeds.
- Authorization is by package name membership in the caller UID's package list, so any package sharing the caller's UID is treated as the owner.
- Matching is an exact string comparison of file.getPath() against the DATA column. The diff does not show whether the path is normalized before this check.
Behavior changes
- Insert requests whose destination file already has a MediaStore record owned by a different package now fail with SecurityException("Caller does not have access to this path").
- Each call to the destination check now runs an additional query to MediaStore (including pending rows).
- New warning logs under Constants.TAG when MediaStore cannot be acquired or queried. In those cases the old (unchecked) behavior remains.
Per major
- Android 14: same fix: commit d37a58ed0acb in android-security-14.0.0_r18; all 8/8 patched-region lines present at android-security-14.0.0_r29.
- Android 15: same fix: commit ed764e06106a in android-security-15.0.0_r6, identical diff (same blob indices 77fe8832..7ae06574); 8/8 lines present at android-security-15.0.0_r17.
- Android 16: not affected (the bulletin does not list it); patched code present (8/8 lines) at android-security-16.0.0_r8.
- Android 17: not affected (the bulletin does not list it); patched code present (8/8 lines) at android-security-17.0.0_r1.
Unknowns
- The name of the enclosing method at line ~1120 and how insert() reaches it are not shown in the provided hunk.
- Whether the destination path is canonicalized before this check is not visible in the material.
- The commit message describes the exact disclosure mechanism only at a high level.
- Whether Android 12/12L/13 received an adapted commit is not shown in the material.