Enforce a hard limit for the size of images to be decoded Although standards like JPEG allow for sizes as large as 65,535×65,535 pixels, we realistically cannot fit such large images in memory. Created a test gif since it's the smallest format I could find. It's larger than strictly necessary for the test in case we want to increase the hard limit in the future. Bug: 444671303 Test: manually verified that a notification that causes a crashloop without this change, doesn't anymore Test: LocalImageResolverTest Flag: EXEMPT CVE_FIX (cherry picked from commit 449f35b532d5f680b90c8f9d8150010e7f5f30df) Cherrypick-From: https://googleplex-android-review.googlesource.com/q/commit:4bceac8d45f07c272eced0b8da51513415d7d248 Merged-In: I0c95cfeabe169630286e7af8577c4495c2b1015f Change-Id: I0c95cfeabe169630286e7af8577c4495c2b1015f
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java index c5d74fc..6351c0e 100644 --- a/core/java/com/android/internal/widget/LocalImageResolver.java +++ b/core/java/com/android/internal/widget/LocalImageResolver.java
@@ -47,6 +47,12 @@ static final int DEFAULT_MAX_SAFE_ICON_SIZE_PX = 480; /** + * If an image is larger than this, we won't even attempt to decode it, as we risk taking up all + * of the device memory. + */ + private static final int DEFAULT_DECODE_HARD_LIMIT_PX = 4096; + + /** * Resolve an image from the given Uri using {@link ImageDecoder} if it contains a * bitmap reference. * Negative or zero dimensions will result in icon loaded in its original size. @@ -252,6 +258,16 @@ private static void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info, int maxWidth, int maxHeight) { final Size size = info.getSize(); + + if (size.getWidth() > DEFAULT_DECODE_HARD_LIMIT_PX + || size.getHeight() > DEFAULT_DECODE_HARD_LIMIT_PX) { + // The image is larger than what we can reasonably expect to decode without filling up + // the device memory, so let's bail. + throw new RuntimeException( + "Image dimensions (" + size.getWidth() + "x" + size.getHeight() + + ") exceed the maximum allowed size."); + } + final int originalSize = Math.max(size.getHeight(), size.getWidth()); final int maxSize = Math.max(maxWidth, maxHeight); final double ratio = (originalSize > maxSize)
diff --git a/core/tests/coretests/res/drawable/test16000x16000.gif b/core/tests/coretests/res/drawable/test16000x16000.gif new file mode 100644 index 0000000..d62dded --- /dev/null +++ b/core/tests/coretests/res/drawable/test16000x16000.gif Binary files differ
diff --git a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java index 271a20b..e2ce346 100644 --- a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
@@ -282,6 +282,14 @@ assertThat(d).isNull(); } + @Test(expected = IOException.class) + public void resolveImage_veryLargeResource_throwsException() throws IOException { + // Passing in an unreasonably large image should throw an exception. + Uri uri = Uri.parse("android.resource://" + + mContext.getPackageName() + "/" + R.drawable.test16000x16000); + LocalImageResolver.resolveImage(uri, mContext); + } + @Test public void resolveResourcesForIcon_notAResourceIcon_returnsNull() { Icon icon = Icon.createWithContentUri(Uri.parse("some_uri"));