From e0faa5c2558e8b62d9f273433676b17f4e01644c Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 22 Sep 2025 16:23:57 +0100 Subject: [PATCH] PNGImageFormat: Avoid crashing when attempting to write an invalid image The default error handler could cause crashes, so we now set up a custom error handler both when reading and writing PNGs. The HeapBlock and BitmapData automatic variables have moved, so that their destructors will still run as expected in the failure case. Note that it's UB to call longjmp to unwind the stack to the previous setjmp, if said unwinding would normally cause non-trivial destructors to run. --- .../image_formats/juce_PNGLoader.cpp | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/modules/juce_graphics/image_formats/juce_PNGLoader.cpp b/modules/juce_graphics/image_formats/juce_PNGLoader.cpp index eecf7e56d8..172868661f 100644 --- a/modules/juce_graphics/image_formats/juce_PNGLoader.cpp +++ b/modules/juce_graphics/image_formats/juce_PNGLoader.cpp @@ -332,14 +332,6 @@ namespace PNGHelpers static_cast (png_get_io_ptr (png))->write (data, length); } - #if ! JUCE_USING_COREIMAGE_LOADER - static void JUCE_CDECL readCallback (png_structp png, png_bytep data, png_size_t length) - { - static_cast (png_get_io_ptr (png))->read (data, (int) length); - } - - struct PNGErrorStruct {}; - static void JUCE_CDECL errorCallback (png_structp p, png_const_charp) { #ifdef PNG_SETJMP_SUPPORTED @@ -351,6 +343,14 @@ namespace PNGHelpers static void JUCE_CDECL warningCallback (png_structp, png_const_charp) {} + #if ! JUCE_USING_COREIMAGE_LOADER + static void JUCE_CDECL readCallback (png_structp png, png_bytep data, png_size_t length) + { + static_cast (png_get_io_ptr (png))->read (data, (int) length); + } + + struct PNGErrorStruct {}; + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4611) static bool readHeader (InputStream& in, png_structp pngReadStruct, png_infop pngInfoStruct, jmp_buf& errorJumpBuf, @@ -528,15 +528,31 @@ Image PNGImageFormat::decodeImage (InputStream& in) bool PNGImageFormat::writeImageToStream (const Image& image, OutputStream& out) { + if (! image.isValid()) + return false; + using namespace pnglibNamespace; auto width = image.getWidth(); auto height = image.getHeight(); + HeapBlock rowData (width * 4); + const Image::BitmapData srcData (image, Image::BitmapData::readOnly); + auto pngWriteStruct = png_create_write_struct (PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (pngWriteStruct == nullptr) return false; + jmp_buf errorJumpBuf; + png_set_error_fn (pngWriteStruct, &errorJumpBuf, PNGHelpers::errorCallback, PNGHelpers::warningCallback); + + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4611) + + if (setjmp (errorJumpBuf) != 0) + return false; + + JUCE_END_IGNORE_WARNINGS_MSVC + auto pngInfoStruct = png_create_info_struct (pngWriteStruct); if (pngInfoStruct == nullptr) @@ -554,8 +570,6 @@ bool PNGImageFormat::writeImageToStream (const Image& image, OutputStream& out) PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - HeapBlock rowData (width * 4); - png_color_8 sig_bit; sig_bit.red = 8; sig_bit.green = 8; @@ -569,8 +583,6 @@ bool PNGImageFormat::writeImageToStream (const Image& image, OutputStream& out) png_set_shift (pngWriteStruct, &sig_bit); png_set_packing (pngWriteStruct); - const Image::BitmapData srcData (image, Image::BitmapData::readOnly); - for (int y = 0; y < height; ++y) { uint8* dst = rowData;