diff options
author | Andreas Kling <kling@serenityos.org> | 2021-01-06 01:32:59 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-01-06 01:34:25 +0100 |
commit | 92fe63fbecedb1266e472861aba9afed7d8f87f9 (patch) | |
tree | 4ca385a05b465c2c04d5a4a2f2cc4b8e26e0bc04 | |
parent | 4a7d948f14f6b5b21af1a8b1551169d8a7601c66 (diff) | |
download | serenity-92fe63fbecedb1266e472861aba9afed7d8f87f9.zip |
LibGfx: Tolerate PNGs with fewer palette entries than possible
We shouldn't reject indexed palette PNGs just because they have fewer
palette entries than the bit depth allows. Instead, we need to check
for OOB palette accesses and fail the decode *then*.
-rw-r--r-- | Libraries/LibGfx/PNGLoader.cpp | 21 |
1 files changed, 14 insertions, 7 deletions
diff --git a/Libraries/LibGfx/PNGLoader.cpp b/Libraries/LibGfx/PNGLoader.cpp index 7f945e55ab..6d6492aa56 100644 --- a/Libraries/LibGfx/PNGLoader.cpp +++ b/Libraries/LibGfx/PNGLoader.cpp @@ -362,7 +362,7 @@ ALWAYS_INLINE static void unpack_triplets_without_alpha(PNGLoadingContext& conte } } -NEVER_INLINE FLATTEN static void unfilter(PNGLoadingContext& context) +NEVER_INLINE FLATTEN static bool unfilter(PNGLoadingContext& context) { // First unpack the scanlines to RGBA: switch (context.color_type) { @@ -435,6 +435,8 @@ NEVER_INLINE FLATTEN static void unfilter(PNGLoadingContext& context) auto* palette_index = context.scanlines[y].data.data(); for (int i = 0; i < context.width; ++i) { auto& pixel = (Pixel&)context.bitmap->scanline(y)[i]; + if (palette_index[i] >= context.palette_data.size()) + return false; auto& color = context.palette_data.at((int)palette_index[i]); auto transparency = context.palette_transparency_data.size() >= palette_index[i] + 1u ? context.palette_transparency_data.data()[palette_index[i]] @@ -454,6 +456,8 @@ NEVER_INLINE FLATTEN static void unfilter(PNGLoadingContext& context) auto bit_offset = (8 - context.bit_depth) - (context.bit_depth * (i % pixels_per_byte)); auto palette_index = (palette_indexes[i / pixels_per_byte] >> bit_offset) & mask; auto& pixel = (Pixel&)context.bitmap->scanline(y)[i]; + if ((size_t)palette_index >= context.palette_data.size()) + return false; auto& color = context.palette_data.at(palette_index); auto transparency = context.palette_transparency_data.size() >= palette_index + 1u ? context.palette_transparency_data.data()[palette_index] @@ -513,6 +517,8 @@ NEVER_INLINE FLATTEN static void unfilter(PNGLoadingContext& context) continue; } } + + return true; } static bool decode_png_header(PNGLoadingContext& context) @@ -633,9 +639,7 @@ static bool decode_png_bitmap_simple(PNGLoadingContext& context) return false; } - unfilter(context); - - return true; + return unfilter(context); } static int adam7_height(PNGLoadingContext& context, int pass) @@ -733,7 +737,10 @@ static bool decode_adam7_pass(PNGLoadingContext& context, Streamer& streamer, in } subimage_context.bitmap = Bitmap::create(context.bitmap->format(), { subimage_context.width, subimage_context.height }); - unfilter(subimage_context); + if (!unfilter(subimage_context)) { + subimage_context.bitmap = nullptr; + return false; + } // Copy the subimage data into the main image according to the pass pattern for (int y = 0, dy = adam7_starty[pass]; y < subimage_context.height && dy < context.height; ++y, dy += adam7_stepy[pass]) { @@ -771,8 +778,8 @@ static bool decode_png_bitmap(PNGLoadingContext& context) if (context.width == -1 || context.height == -1) return false; // Didn't see an IHDR chunk. - if (context.color_type == 3 && context.palette_data.size() < (1u << context.bit_depth)) - return false; // Didn't see an PLTE chunk for a palettized image, or not enough entries. + if (context.color_type == 3 && context.palette_data.is_empty()) + return false; // Didn't see a PLTE chunk for a palettized image, or it was empty. unsigned long srclen = context.compressed_data.size() - 6; unsigned long destlen = 0; |