CVE-2025-48567 AI-generated analysis
platform/packages/providers/MediaProvider, private app directory path access checks (MediaProvider.java, util/FileUtils.java)
MediaProvider now NFD-normalizes file paths and strips Unicode default-ignorable code points (e.g. zero-width space) before the regex-based private app directory (Android/data, Android/obb) checks in isPrivatePackagePathNotAccessibleByCaller and isUidAllowedAccessToDataOrObbPathForFuse.
Root cause
The commit message says the lower file system ignores certain characters when saving files, and that malicious apps use these ignorable characters to get past the regex checks that protect private app directories. In isPrivatePackagePathNotAccessibleByCaller and isUidAllowedAccessToDataOrObbPathForFuse, the raw path string was passed to extractPathOwnerPackageName, isExternalMediaDirectory and isCallingIdentityAllowedAccessToDataOrObbPath(extractRelativePathWithDisplayName(path)). A path with default-ignorable code points (the test uses a zero-width space inside 'Android') could therefore fail to match as a private directory path, while the file system treats it as the real one.
How the fix works
- FileUtils.java: adds public static normalizeAndFilterDefaultIgnorableCodepoints(String path). For null or empty input it returns the input. Otherwise it applies java.text.Normalizer NFD and checks each code point with android.icu.lang.UCharacter.hasBinaryProperty(UProperty.DEFAULT_IGNORABLE_CODE_POINT). If any match, it rebuilds the string without them; if none match, it returns the NFD-normalized string.
- MediaProvider.java:isPrivatePackagePathNotAccessibleByCaller: the first statement is now path = FileUtils.normalizeAndFilterDefaultIgnorableCodepoints(path), so extractPathOwnerPackageName, isExternalMediaDirectory and the uid check all see the sanitized path. The final call changed from isUidAllowedAccessToDataOrObbPathForFuse to the new private isUidAllowedAccessToDataOrObbPath, which receives the already normalized path.
- MediaProvider.java:isUidAllowedAccessToDataOrObbPathForFuse (public, @Keep): is now a wrapper that normalizes the path and passes it to the new private isUidAllowedAccessToDataOrObbPath(uid, path). That private method holds the previous body (clearLocalCallingIdentity, then isCallingIdentityAllowedAccessToDataOrObbPath(extractRelativePathWithDisplayName(path)), then restoreLocalCallingIdentity).
- MediaProvider.java:shouldBypassFuseRestrictions: the call changed from isUidAllowedAccessToDataOrObbPathForFuse to the private, non-normalizing isUidAllowedAccessToDataOrObbPath, which receives the raw filePath.
- FileUtils.java:extractRelativePath: 'final String path' changed to 'String path'. No functional change in the shown hunk.
- FileUtilsTest.java: adds testNormalizeAndFilterDefaultIgnorableCodepoints, annotated SdkSuppress minSdkVersion S. It checks that a zero-width space inside 'Android' and the U+FE0F variation selector are removed.
Completeness
- Normalization is added at two points: isPrivatePackagePathNotAccessibleByCaller (before all of its sub-checks) and the public @Keep isUidAllowedAccessToDataOrObbPathForFuse (before it delegates).
- shouldBypassFuseRestrictions now passes the raw filePath to the non-normalizing isUidAllowedAccessToDataOrObbPath. The material shows neither whether its callers normalize filePath nor what effect a non-normalized path has there.
- Only default-ignorable code points are removed, after NFD normalization. The material does not show which characters the lower file system actually ignores.
- Other path helpers in FileUtils (e.g. extractRelativePath, which only got a cosmetic change) are not normalized by this commit. Whether they feed security decisions is not shown.
- On Android 16 the sanitization does nothing unless the aconfig flag enable_path_sanitization is enabled. On 14 and 15 it runs unconditionally.
Behavior changes
- Paths passed to the two normalizing checks are NFD-normalized and stripped of default-ignorable code points before evaluation. A path that contains such code points inside 'Android/data' or 'Android/obb' is now evaluated as the matching private directory path.
- NFD normalization is applied even when no ignorable code points are present, so the string that is checked can differ from the input for precomposed characters.
- Android 16 only: a new fixed-read-only aconfig flag, com.android.providers.media.flags.enable_path_sanitization, gates the behavior.
Per major
- Android 14: Fixed by e599e8d934f2 in android-security-14.0.0_r25 (security-aosp-udc-staging). This is a combined cherry-pick; the commit message references ag/33577707 and ag/33920560. Sanitization is unconditional: the code has no flag check despite the Flag: trailer. At android-security-14.0.0_r29, 6/8 lines of the patched region are present.
- Android 15: Same fix, 2f45f53ce92c in android-security-15.0.0_r13 (security-aosp-24Q3-staging). The MediaProvider.java and FileUtils.java changes are the same as on 14, and sanitization is unconditional. The test also carries @EnableFlags(Flags.FLAG_ENABLE_PATH_SANITIZATION), although FileUtils on this branch has no flag check. At android-security-15.0.0_r17, 6/8 lines of the patched region are present.
- Android 16: Adapted fix, 237fb89f5325 in android-security-16.0.0_r4 (security-aosp-25Q2-staging). It adds the aconfig flag enable_path_sanitization (is_fixed_read_only) to mediaprovider_flags.aconfig, and normalizeAndFilterDefaultIgnorableCodepoints returns its input unchanged when Flags.enablePathSanitization() is false. The hunk that replaces the call with filePath differs: the result is assigned to a boolean isUidAllowedAccessToDataOrObbPath next to owned-photos logic. The test uses SetFlagsRule. At android-security-16.0.0_r8, 7/8 lines of the patched region are present.
- Android 17: Not affected (not listed by the bulletin). At android-security-17.0.0_r1, 6/8 lines of the patched region are present.
Unknowns
- Which exact characters the lower file system ignores; the material only says 'certain characters'.
- Who calls the @Keep isUidAllowedAccessToDataOrObbPathForFuse (e.g. native FUSE code); not shown.
- Whether filePath reaching shouldBypassFuseRestrictions is normalized by its callers, and what passing a non-normalized path to isCallingIdentityAllowedAccessToDataOrObbPath does there.
- The tests testIsDataOrObbRelativePathWithZwsChars_flagEnabled and _flagDisabled named in the commit message are not in the diff, and the contents of the original main CLs are not shown.
- On Android 15 the test's @EnableFlags(Flags.FLAG_ENABLE_PATH_SANITIZATION) is added without visible imports or a flag definition; whether the flag exists on that branch is not shown.
- The default value or release-config state of enable_path_sanitization in Android 16 builds is not shown.