CVE-2025-48609 AI-generated analysis
platform/packages/providers/TelephonyProvider, MmsProvider (mms content provider, part/drm row deletion)
MmsProvider.deleteDataRows now verifies that the file path read from a part/drm row's _data column canonically resolves under the provider's "parts" directory before calling File.delete(), reusing the same check openFile already performed.
Root cause
In src/com/android/providers/telephony/MmsProvider.java, deleteDataRows(db, table, selection, selectionArgs) queried the _data column of the part (TABLE_PART) or drm (TABLE_DRM) table and, for every non-null value, executed new File(path).delete() with no check on where the path pointed. The _data value was treated as trusted even though openFile already contained a comment noting that callers may have inserted or updated the _data column with disallowed paths (including via SQL injection) and guarded against it with a canonical-path prefix check. The delete path had no such guard, so a row whose _data pointed outside the parts directory caused the provider to delete that file when the row was deleted (via deleteParts/deleteTempDrmData, or indirectly via deleteMessages which calls deleteParts for each pdu). The bulletin classifies this as High severity DoS.
How the fix works
- MmsProvider.java: new static helper isFilePathCanonical(Context, File) returns file.getCanonicalPath().startsWith(context.getDir(PARTS_DIR_NAME, 0).getCanonicalPath()); on IOException it logs and returns false (fail closed).
- MmsProvider.java:deleteDataRows now takes a Context; for each _data value it skips null, builds File, calls isFilePathCanonical, and if false logs 'deleteFile: path ... does not start with ...' and continues without deleting; only paths under the parts directory reach file.delete(). The DB row is still deleted afterwards via db.delete(table, selection, selectionArgs) as before.
- MmsProvider.java:deleteParts and deleteTempDrmData gain a Context parameter and forward it to deleteDataRows; delete() captures Context context = getContext() once and passes it to deleteMessages, deleteParts and deleteTempDrmData; deleteMessages' per-pdu loop passes context to deleteParts.
- MmsProvider.java:openFile replaced its inline getCanonicalPath().startsWith(...) comparison with the shared isFilePathCanonical(getContext(), filePath); semantics unchanged.
- tests/src/com/android/providers/telephony/MmsProviderTest.java: adds a temp testDir/parts setup and two tests, testDeleteDataRows_pathStartsWithPartsDirectory_canDeleteFile and testDeleteDataRows_invalidPath_cannotDeleteFile, exercising isFilePathCanonical directly.
Completeness
- Covers the _data-driven file deletions that go through deleteDataRows as shown in the diff: deleteParts (called from delete() for the part table and from the deleteMessages per-pdu cascade) and deleteTempDrmData (drm table). The material does not show the full file, so other deletion sites cannot be ruled out.
- The check is a plain string prefix comparison (startsWith) on canonical paths without a trailing separator, the same pattern openFile already used; a directory whose name merely begins with the parts directory name would pass. The material does not show whether such a sibling directory exists.
- The fix only hardens the consumer of _data. It does not change how _data gets written (insert/update paths are not in the diff); the commit message itself says it may be redundant with separate SQL injection protection changes that are not included in this material.
- The same PARTS_DIR_NAME prefix is applied to drm-table rows; the material does not show where drm _data files are normally stored, so whether legitimate drm temp files are always under the parts directory is not established from the diff.
Behavior changes
- Deleting a part or drm row whose _data does not canonically resolve under context.getDir("parts", 0) no longer removes the file; an error is logged ('deleteFile: path ... does not start with ...') and the database row is still deleted, so such files may be orphaned on disk.
- If canonicalization throws IOException, the file is not deleted (previously the delete was attempted on the raw path).
- openFile behavior is unchanged; it now calls the shared helper.
- New public static @VisibleForTesting method isFilePathCanonical on MmsProvider.
Per major
- Android 14: same fix: commit 1552b67718fc in android-security-14.0.0_r25 (cherry-pick of fce8998a1770, Change-Id Ic2072a12bf93be7c17a86b372ae7e7057d7eb237); at android-security-14.0.0_r29, 7/8 lines of the patched region are present.
- Android 15: same fix: commit e40b30ca3fa3 in android-security-15.0.0_r13; identical MmsProvider change at different line offsets (test file differs slightly: no mUserManager field, context field placed separately); 7/8 lines present at android-security-15.0.0_r17.
- Android 16: same fix: commit 37babc702849 in android-security-16.0.0_r4; identical MmsProvider change at different line offsets; 7/8 lines present at android-security-16.0.0_r8.
- Android 17: not affected (not listed by the bulletin); the patched region is present at android-security-17.0.0_r1 (7/8 lines).
Unknowns
- How an out-of-directory _data value can be introduced (insert/update validation and the referenced 'recent sql injection protection changes' are not in the material).
- Permission/URI access requirements and URI-to-table mapping for reaching MmsProvider.delete on part, drm and pdu data.
- Where drm-table _data files normally reside and therefore whether the new check can skip legitimate drm temp file cleanup.
- Which single line of the 8-line patched region is absent in the latest security tags (the 7/8 figure).