From 1cf4cdce9afb316c4790a4515cc3c6d7b0ccf8ee Mon Sep 17 00:00:00 2001 From: "jdrew%mozilla.com" Date: Mon, 8 Mar 2010 20:28:47 +0000 Subject: [PATCH] Bug 497056. Patch by glennrp+bmo@gmail.com; r=joe, sr=roc, a=beltzner git-svn-id: svn://10.0.0.236/trunk@259922 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/modules/libimg/png/MOZCHANGES | 3 + mozilla/modules/libimg/png/pngrutil.c | 316 ++++++++++-------- .../libpr0n/decoders/png/nsPNGDecoder.cpp | 110 +++++- .../libpr0n/encoders/png/nsPNGEncoder.cpp | 2 +- 4 files changed, 280 insertions(+), 151 deletions(-) diff --git a/mozilla/modules/libimg/png/MOZCHANGES b/mozilla/modules/libimg/png/MOZCHANGES index 1e8af5ecc02..960a3b8df04 100644 --- a/mozilla/modules/libimg/png/MOZCHANGES +++ b/mozilla/modules/libimg/png/MOZCHANGES @@ -1,6 +1,9 @@ Changes made to pristine png source by mozilla.org developers. +2010/02/16 -- Ported performance improvements to pngrutil.c and pngpread.c + from libpng-1.4.1 (bug #497056). + 2009/02/19 -- Synced with libpng-1.2.35 (bug #478901). 2008/08/21 -- Synced with libpng-1.2.31 (bug #418900). diff --git a/mozilla/modules/libimg/png/pngrutil.c b/mozilla/modules/libimg/png/pngrutil.c index 25d1d27c5da..3c89f659edb 100644 --- a/mozilla/modules/libimg/png/pngrutil.c +++ b/mozilla/modules/libimg/png/pngrutil.c @@ -209,6 +209,95 @@ png_crc_error(png_structp png_ptr) #if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \ defined(PNG_READ_iCCP_SUPPORTED) +static png_size_t +png_inflate(png_structp png_ptr, const png_byte *data, png_size_t size, + png_bytep output, png_size_t output_size) +{ + png_size_t count = 0; + + png_ptr->zstream.next_in = (png_bytep)data; /* const_cast: VALID */ + png_ptr->zstream.avail_in = size; + + while (1) + { + int ret, avail; + + /* Reset the output buffer each time round - we empty it + * after every inflate call. + */ + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = png_ptr->zbuf_size; + + ret = inflate(&png_ptr->zstream, Z_NO_FLUSH); + avail = png_ptr->zbuf_size - png_ptr->zstream.avail_out; + + /* First copy/count any new output - but only if we didn't + * get an error code. + */ + if ((ret == Z_OK || ret == Z_STREAM_END) && avail > 0) + { + if (output != 0 && output_size > count) + { + int copy = output_size - count; + if (avail < copy) copy = avail; + png_memcpy(output + count, png_ptr->zbuf, copy); + } + count += avail; + } + + if (ret == Z_OK) + continue; + + /* Termination conditions - always reset the zstream, it + * must be left in inflateInit state. + */ + png_ptr->zstream.avail_in = 0; + inflateReset(&png_ptr->zstream); + + if (ret == Z_STREAM_END) + return count; /* NOTE: may be zero. */ + + /* Now handle the error codes - the API always returns 0 + * and the error message is dumped into the uncompressed + * buffer if available. + */ + { + char *msg, umsg[52]; + if (png_ptr->zstream.msg != 0) + msg = png_ptr->zstream.msg; + else + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + switch (ret) + { + case Z_BUF_ERROR: + msg = "Buffer error in compressed datastream in %s chunk"; + break; + case Z_DATA_ERROR: + msg = "Data error in compressed datastream in %s chunk"; + break; + default: + msg = "Incomplete compressed datastream in %s chunk"; + break; + } + + png_snprintf(umsg, sizeof umsg, msg, png_ptr->chunk_name); + msg = umsg; +#else + msg = "Damaged compressed datastream in chunk other than IDAT"; +#endif + } + + png_warning(png_ptr, msg); + } + + /* 0 means an error - notice that this code simple ignores + * zero length compressed chunks as a result. + */ + return 0; + } +} + /* * Decompress trailing data in a chunk. The assumption is that chunkdata * points at an allocated area holding the contents of a chunk with a @@ -221,165 +310,98 @@ png_decompress_chunk(png_structp png_ptr, int comp_type, png_size_t chunklength, png_size_t prefix_size, png_size_t *newlength) { - static PNG_CONST char msg[] = "Error decoding compressed text"; - png_charp text; - png_size_t text_size; - - if (comp_type == PNG_COMPRESSION_TYPE_BASE) + /* The caller should guarantee this */ + if (prefix_size > chunklength) { - int ret = Z_OK; - png_ptr->zstream.next_in = (png_bytep)(png_ptr->chunkdata + prefix_size); - png_ptr->zstream.avail_in = (uInt)(chunklength - prefix_size); - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; - - text_size = 0; - text = NULL; - - while (png_ptr->zstream.avail_in) - { - ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); - if (ret != Z_OK && ret != Z_STREAM_END) - { - if (png_ptr->zstream.msg != NULL) - png_warning(png_ptr, png_ptr->zstream.msg); - else - png_warning(png_ptr, msg); - inflateReset(&png_ptr->zstream); - png_ptr->zstream.avail_in = 0; - - if (text == NULL) - { - text_size = prefix_size + png_sizeof(msg) + 1; - text = (png_charp)png_malloc_warn(png_ptr, text_size); - if (text == NULL) - { - png_free(png_ptr, png_ptr->chunkdata); - png_ptr->chunkdata = NULL; - png_error(png_ptr, "Not enough memory to decompress chunk"); - } - png_memcpy(text, png_ptr->chunkdata, prefix_size); - } - - text[text_size - 1] = 0x00; - - /* Copy what we can of the error message into the text chunk */ - text_size = (png_size_t)(chunklength - - (text - png_ptr->chunkdata) - 1); - if (text_size > png_sizeof(msg)) - text_size = png_sizeof(msg); - png_memcpy(text + prefix_size, msg, text_size); - break; - } - if (!png_ptr->zstream.avail_out || ret == Z_STREAM_END) - { - if (text == NULL) - { - text_size = prefix_size + - png_ptr->zbuf_size - png_ptr->zstream.avail_out; - text = (png_charp)png_malloc_warn(png_ptr, text_size + 1); - if (text == NULL) - { - png_free(png_ptr, png_ptr->chunkdata); - png_ptr->chunkdata = NULL; - png_error(png_ptr, - "Not enough memory to decompress chunk."); - } - png_memcpy(text + prefix_size, png_ptr->zbuf, - text_size - prefix_size); - png_memcpy(text, png_ptr->chunkdata, prefix_size); - *(text + text_size) = 0x00; - } - else - { - png_charp tmp; - - tmp = text; - text = (png_charp)png_malloc_warn(png_ptr, - (png_uint_32)(text_size + - png_ptr->zbuf_size - png_ptr->zstream.avail_out + 1)); - if (text == NULL) - { - png_free(png_ptr, tmp); - png_free(png_ptr, png_ptr->chunkdata); - png_ptr->chunkdata = NULL; - png_error(png_ptr, - "Not enough memory to decompress chunk.."); - } - png_memcpy(text, tmp, text_size); - png_free(png_ptr, tmp); - png_memcpy(text + text_size, png_ptr->zbuf, - (png_ptr->zbuf_size - png_ptr->zstream.avail_out)); - text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; - *(text + text_size) = 0x00; - } - if (ret == Z_STREAM_END) - break; - else - { - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; - } - } - } - if (ret != Z_STREAM_END) - { -#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) - char umsg[52]; - - if (ret == Z_BUF_ERROR) - png_snprintf(umsg, 52, - "Buffer error in compressed datastream in %s chunk", - png_ptr->chunk_name); - else if (ret == Z_DATA_ERROR) - png_snprintf(umsg, 52, - "Data error in compressed datastream in %s chunk", - png_ptr->chunk_name); - else - png_snprintf(umsg, 52, - "Incomplete compressed datastream in %s chunk", - png_ptr->chunk_name); - png_warning(png_ptr, umsg); -#else - png_warning(png_ptr, - "Incomplete compressed datastream in chunk other than IDAT"); -#endif - text_size = prefix_size; - if (text == NULL) - { - text = (png_charp)png_malloc_warn(png_ptr, text_size+1); - if (text == NULL) - { - png_free(png_ptr, png_ptr->chunkdata); - png_ptr->chunkdata = NULL; - png_error(png_ptr, "Not enough memory for text."); - } - png_memcpy(text, png_ptr->chunkdata, prefix_size); - } - *(text + text_size) = 0x00; - } - - inflateReset(&png_ptr->zstream); - png_ptr->zstream.avail_in = 0; - - png_free(png_ptr, png_ptr->chunkdata); - png_ptr->chunkdata = text; - *newlength=text_size; + /* The recovery is to delete the chunk. */ + png_warning(png_ptr, "invalid chunklength"); + prefix_size = 0; /* To delete everything */ } + + else if (comp_type == PNG_COMPRESSION_TYPE_BASE) + { + png_size_t expanded_size = png_inflate(png_ptr, + (png_bytep)(png_ptr->chunkdata + prefix_size), + chunklength - prefix_size, + 0/*output*/, 0/*output size*/); + + /* Now check the limits on this chunk - if the limit fails the + * compressed data will be removed, the prefix will remain. + */ + if (prefix_size + expanded_size >= 3999999L) + png_warning(png_ptr, "Exceeded size limit while expanding chunk"); + + /* If the size is zero either there was an error and a message + * has already been output (warning) or the size really is zero + * and we have nothing to do - the code will exit through the + * error case below. + */ + else if (expanded_size > 0) + { + /* Success (maybe) - really uncompress the chunk. */ + png_size_t new_size = 0; + png_charp text = png_malloc_warn(png_ptr, + prefix_size + expanded_size + 1); + + if (text != NULL) + { + png_memcpy(text, png_ptr->chunkdata, prefix_size); + new_size = png_inflate(png_ptr, + (png_bytep)(png_ptr->chunkdata + prefix_size), + chunklength - prefix_size, + (png_bytep)(text + prefix_size), expanded_size); + text[prefix_size + expanded_size] = 0; /* just in case */ + + if (new_size == expanded_size) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = text; + *newlength = prefix_size + expanded_size; + return; /* The success return! */ + } + + png_warning(png_ptr, "png_inflate logic error"); + png_free(png_ptr, text); + } + else + png_warning(png_ptr, "Not enough memory to decompress chunk"); + } + } + else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */ { -#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) char umsg[50]; - png_snprintf(umsg, 50, "Unknown zTXt compression type %d", comp_type); +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + png_snprintf(umsg, sizeof umsg, "Unknown zTXt compression type %d", comp_type); png_warning(png_ptr, umsg); #else png_warning(png_ptr, "Unknown zTXt compression type"); #endif - *(png_ptr->chunkdata + prefix_size) = 0x00; - *newlength = prefix_size; + /* The recovery is to simply drop the data. */ } + + /* Generic error return - leave the prefix, delete the compressed + * data, reallocate the chunkdata to remove the potentially large + * amount of compressed data. + */ + { + png_charp text = png_malloc_warn(png_ptr, prefix_size + 1); + if (text != NULL) + { + if (prefix_size > 0) + png_memcpy(text, png_ptr->chunkdata, prefix_size); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = text; + + /* This is an extra zero in the 'uncompressed' part. */ + *(png_ptr->chunkdata + prefix_size) = 0x00; + } + /* Ignore a malloc error here - it is safe. */ + } + + *newlength = prefix_size; } #endif diff --git a/mozilla/modules/libpr0n/decoders/png/nsPNGDecoder.cpp b/mozilla/modules/libpr0n/decoders/png/nsPNGDecoder.cpp index 6922ad3c382..bccabfbcde8 100644 --- a/mozilla/modules/libpr0n/decoders/png/nsPNGDecoder.cpp +++ b/mozilla/modules/libpr0n/decoders/png/nsPNGDecoder.cpp @@ -66,6 +66,14 @@ static void PNGAPI frame_info_callback(png_structp png_ptr, png_uint_32 frame_nu static void PNGAPI end_callback(png_structp png_ptr, png_infop info_ptr); static void PNGAPI error_callback(png_structp png_ptr, png_const_charp error_msg); static void PNGAPI warning_callback(png_structp png_ptr, png_const_charp warning_msg); +#ifdef PNG_USER_MEM_SUPPORTED +static png_voidp PNGAPI malloc_callback(png_structp png_ptr, +# if PNG_LIBPNG_VER < 10400 + png_size_t size); +# else + png_alloc_size_t size); +# endif +#endif #ifdef PR_LOGGING static PRLogModuleInfo *gPNGLog = PR_NewLogModule("PNGDecoder"); @@ -231,8 +239,21 @@ NS_IMETHODIMP nsPNGDecoder::Init(imgILoad *aLoad) /* Initialize the container's source image header. */ /* Always decode to 24 bit pixdepth */ - mPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING, - NULL, error_callback, warning_callback); +#ifdef PNG_USER_MEM_SUPPORTED + if (gfxPlatform::GetCMSMode() != eCMSMode_Off) { + mPNG = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, + NULL, + error_callback, + warning_callback, + NULL, + malloc_callback, + NULL); + } else +#endif + mPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING, + NULL, error_callback, + warning_callback); + if (!mPNG) { return NS_ERROR_OUT_OF_MEMORY; } @@ -243,15 +264,29 @@ NS_IMETHODIMP nsPNGDecoder::Init(imgILoad *aLoad) return NS_ERROR_OUT_OF_MEMORY; } -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +#ifndef MOZPNGCONF_H +# if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) || \ + defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED) /* Ignore unused chunks */ if (!gfxPlatform::IsCMSEnabled()) { png_set_keep_unknown_chunks(mPNG, 1, color_chunks, 2); } png_set_keep_unknown_chunks(mPNG, 1, unused_chunks, (int)sizeof(unused_chunks)/5); +# endif + +# if PNG_LIBPNG_VER < 10401 +# if defined(PNG_WRITE_SUPPORTED) + if (gfxPlatform::GetCMSMode() != eCMSMode_Off) { + /* Increase speed of decompressing large iCCP chunks (default buffer + size is 8192) */ + png_set_compression_buffer_size(mPNG, (png_size_t)32768L); + } +# endif +# endif #endif + /* use this as libpng "progressive pointer" (retrieve in callbacks) */ png_set_progressive_read_fn(mPNG, static_cast(this), info_callback, row_callback, end_callback); @@ -661,6 +696,30 @@ info_callback(png_structp png_ptr, png_infop info_ptr) longjmp(decoder->mPNG->jmpbuf, 5); // NS_ERROR_OUT_OF_MEMORY } } + +#ifdef PNG_USER_MEM_SUPPORTED + /* Revert to the default memory allocator */ + if (gfxPlatform::GetCMSMode() != eCMSMode_Off) + png_set_mem_fn(decoder->mPNG, NULL, NULL, NULL); +#endif + +#ifndef MOZPNGCONF_H +# if PNG_LIBPNG_VER < 10401 +# if defined(PNG_WRITE_SUPPORTED) + /* Revert to the default zlib buffer size */ + if (gfxPlatform::GetCMSMode() != eCMSMode_Off) { + png_set_compression_buffer_size(decoder->mPNG, (png_size_t)8192); + } +# endif +# endif +#endif + + /* Reject any ancillary chunk after IDAT with a bad CRC (bug #397593). + * It would be better to show the default frame (if one has already been + * successfully decoded) before bailing, but it's simpler to just bail + * out with an error message. + */ + png_set_crc_action(png_ptr, NULL, PNG_CRC_ERROR_QUIT); if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) decoder->mFrame = nsnull; @@ -873,3 +932,48 @@ warning_callback(png_structp png_ptr, png_const_charp warning_msg) { PR_LOG(gPNGLog, PR_LOG_WARNING, ("libpng warning: %s\n", warning_msg)); } + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replacement libpng memory allocator that has a 4MB limit */ +# if PNG_LIBPNG_VER < 10400 +png_voidp malloc_callback(png_structp png_ptr, png_size_t size) { +# else +png_voidp malloc_callback(png_structp png_ptr, png_alloc_size_t size) { +# endif + + png_voidp ret; + + if (png_ptr == NULL || size == 0) + return (png_voidp) (NULL); + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) { + return NULL; + } +#endif + if (size > (png_uint_32)4000000L) { + return NULL; + } +#if defined(__TURBOC__) && !defined(__FLAT__) + if (size != (unsigned long)size) + ret = NULL; + else + ret = farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + if (size != (unsigned long)size) + ret = NULL; + else + ret = halloc(size, 1); +# else + if (size != (size_t)size) + ret = NULL; + else + ret = malloc((size_t)size); +# endif +#endif + + return (ret); +} +#endif /* PNG_USER_MEM_SUPPORTED */ + diff --git a/mozilla/modules/libpr0n/encoders/png/nsPNGEncoder.cpp b/mozilla/modules/libpr0n/encoders/png/nsPNGEncoder.cpp index ef1909a13df..8284db032cf 100644 --- a/mozilla/modules/libpr0n/encoders/png/nsPNGEncoder.cpp +++ b/mozilla/modules/libpr0n/encoders/png/nsPNGEncoder.cpp @@ -135,7 +135,7 @@ NS_IMETHODIMP nsPNGEncoder::StartImageEncode(PRUint32 aWidth, // initialize mPNG = png_create_write_struct(PNG_LIBPNG_VER_STRING, - png_voidp_NULL, + nsnull, ErrorCallback, ErrorCallback); if (! mPNG)