CVE-2025-48631 AI-generated analysis
platform/frameworks/base, core/java/com/android/internal/widget/LocalImageResolver (MessagingStyle/ConversationStyle image loading via ImageDecoder)
Adds a hard 4096 px per-dimension limit in LocalImageResolver.onHeaderDecoded so that decoding of oversized images is aborted (RuntimeException, surfaced as IOException from resolveImage) instead of being attempted at the risk of exhausting device memory.
Root cause
LocalImageResolver.onHeaderDecoded is the ImageDecoder header listener used by resolveImage. It reads the image size from ImageDecoder.ImageInfo and goes straight to computing a ratio against maxWidth/maxHeight (DEFAULT_MAX_SAFE_ICON_SIZE_PX = 480 when called from resolveImage(Uri, Context)). Nothing rejected very large declared dimensions first. The commit message notes that formats such as JPEG allow up to 65,535x65,535 pixels, which cannot realistically fit in memory, and says a notification caused a crashloop without this change.
How the fix works
- core/java/com/android/internal/widget/LocalImageResolver.java: adds a new constant DEFAULT_DECODE_HARD_LIMIT_PX = 4096. Its comment says images larger than this will not even be decoded.
- LocalImageResolver.onHeaderDecoded: right after info.getSize(), if size.getWidth() > 4096 or size.getHeight() > 4096, it throws RuntimeException("Image dimensions (WxH) exceed the maximum allowed size."). This happens before the existing ratio computation, in the header callback that ImageDecoder calls once the image size is known.
- LocalImageResolver.resolveImage(Uri, Context) (shown in context) wraps any Exception from ImageDecoder.decodeDrawable in an IOException, so callers see the documented IOException.
- core/tests/coretests: adds res/drawable/test16000x16000.gif and the test LocalImageResolverTest.resolveImage_veryLargeResource_throwsException. The test expects an IOException when that resource is resolved through an android.resource:// Uri.
Completeness
- The check covers every decode that routes through onHeaderDecoded. The context shows resolveImage(Uri, Context) doing so; whether the other resolveImage overloads do is not shown.
- The limit is a fixed 4096 px per dimension with a strict greater-than test, so an image of exactly 4096x4096 is still accepted. The test uses 16000x16000.
- Only dimensions are checked. The fix does not bound total pixel count, animated frame count or file size, and the rest of onHeaderDecoded is unchanged.
- LocalImageResolver paths that do not decode through ImageDecoder with this listener are not touched. The test names mention a non-resizing fallback for non-bitmap resource icons, but the material does not show that code.
Behavior changes
- Images with a width or height above 4096 px now fail to resolve. resolveImage throws an IOException that wraps a RuntimeException whose message includes the dimensions.
- Images at or below 4096 px in both dimensions are handled as before.
Per major
- Android 14: same fix: platform/frameworks/base 34926e40b0d8 in android-security-14.0.0_r24 (cherry-pick of 449f35b532d5); onHeaderDecoded hunk at line 249; 8/8 lines present at android-security-14.0.0_r29.
- Android 15: same fix: platform/frameworks/base b645abd5b328 in android-security-15.0.0_r12; same change, onHeaderDecoded hunk at line 252; 8/8 lines present at android-security-15.0.0_r17.
- Android 16: same fix: platform/frameworks/base d6df825fda3a in android-security-16.0.0_r3; same change, onHeaderDecoded hunk at line 252; 8/8 lines present at android-security-16.0.0_r8.
- Android 17: not affected (not listed by the bulletin); the patched region is already present (8/8 lines) at android-security-17.0.0_r1.
Unknowns
- Which process crashloops (the commit message only says a notification caused a crashloop) and the exact call chain from notification handling into LocalImageResolver.resolveImage.
- The rest of onHeaderDecoded and the other resolveImage overloads are not in the material, so it is not shown whether all overloads use this listener or why downscaling alone was not enough.
- How notification callers handle the new IOException.
- How the 4096 value was chosen, beyond the commit message's note that the limit may be raised in the future.