diff options
Diffstat (limited to 'pngrutil.c')
-rw-r--r-- | pngrutil.c | 461 |
1 files changed, 264 insertions, 197 deletions
diff --git a/pngrutil.c b/pngrutil.c index fe230c048..67ede6034 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -1,7 +1,7 @@ /* pngrutil.c - utilities to read a PNG file * - * Last changed in libpng 1.2.43 [January 27, 2010] + * Last changed in libpng 1.2.43 [February 18, 2010] * Copyright (c) 1998-2009 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) @@ -26,7 +26,8 @@ #ifdef PNG_FLOATING_POINT_SUPPORTED # ifdef WIN32_WCE_OLD /* The strtod() function is not supported on WindowsCE */ -__inline double png_strtod(png_structp png_ptr, PNG_CONST char *nptr, char **endptr) +__inline double png_strtod(png_structp png_ptr, PNG_CONST char *nptr, + char **endptr) { double result = 0; int len; @@ -217,222 +218,205 @@ png_crc_error(png_structp png_ptr) #if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \ defined(PNG_READ_iCCP_SUPPORTED) -/* - * Decompress trailing data in a chunk. The assumption is that chunkdata - * points at an allocated area holding the contents of a chunk with a - * trailing compressed part. What we get back is an allocated area - * holding the original prefix part and an uncompressed version of the - * trailing part (the malloc area passed in is freed). - */ -void /* PRIVATE */ -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_size_t +png_inflate(png_structp png_ptr, const png_byte *data, png_size_t size, + png_bytep output, png_size_t output_size) { - static PNG_CONST char msg[] = "Error decoding compressed chunk"; - png_charp text; - png_size_t text_size; + png_size_t count = 0; + + png_ptr->zstream.next_in = (png_bytep)data; /* const_cast: VALID */ + png_ptr->zstream.avail_in = size; - if (comp_type == PNG_COMPRESSION_TYPE_BASE) + while (1) { - int ret = Z_OK; - png_size_t buffer_size; + int ret, avail; - png_ptr->zstream.next_in = (png_bytep)(png_ptr->chunkdata + prefix_size); - png_ptr->zstream.avail_in = (uInt)(chunklength - prefix_size); + /* 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 = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.avail_out = png_ptr->zbuf_size; - text_size = 0; - text = NULL; - buffer_size = 0; + ret = inflate(&png_ptr->zstream, Z_NO_FLUSH); + avail = png_ptr->zbuf_size - png_ptr->zstream.avail_out; - while (png_ptr->zstream.avail_in) + /* 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) { - 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_error(png_ptr, - "Not enough memory to decompress chunk"); - text_size = 0; - break; - } - 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); - buffer_size = text_size; - break; - } - if (!png_ptr->zstream.avail_out || ret == Z_STREAM_END) - { - if (text == NULL) /* Initialize the decompression buffer */ - { - 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_error(png_ptr, - "Not enough memory to decompress chunk."); - text_size = 0; - break; - } - png_memcpy(text + prefix_size, png_ptr->zbuf, - text_size - prefix_size); - png_memcpy(text, png_ptr->chunkdata, prefix_size); - *(text + text_size) = 0x00; - buffer_size = text_size; - } - else /* Enlarge the decompression buffer */ - { - png_charp tmp = text; - png_size_t new_text_size; - - new_text_size = text_size + png_ptr->zbuf_size - - png_ptr->zstream.avail_out; - - if (new_text_size > buffer_size) - { - if (png_ptr->zstream.avail_out) - buffer_size = new_text_size; - else - buffer_size += buffer_size; - - text = (png_charp)png_malloc_warn(png_ptr, - buffer_size + 1); - - if (text == NULL) - { - png_warning(png_ptr, - "Not enough memory to decompress chunk.."); - break; - } - - png_memcpy(text, tmp, text_size); - png_free(png_ptr, tmp); - } - /* FIX ME: zTXt chunk written by pngtest is 6 bytes too large */ - 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 (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 (text != NULL && buffer_size > text_size) - { - /* Reduce text allocation to actual size */ - png_charp tmp; - - tmp = text; - text = (png_charp)png_malloc_warn(png_ptr, - (png_uint_32)(text_size)); + if (ret == Z_OK) + continue; - if (text == NULL) - text = tmp; - - else - { - png_memcpy(text, tmp, text_size + 1); - png_free(png_ptr, tmp); - } + /* 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. */ - if (ret != Z_STREAM_END) + /* 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_STDIO_SUPPORTED) && !defined(_WIN32_WCE) - char umsg[52]; + 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 + } - if (ret == Z_BUF_ERROR) - png_snprintf(umsg, 52, - "Buffer error in compressed datastream in %s chunk", - png_ptr->chunk_name); + png_warning(png_ptr, msg); + } + + /* 0 means an error - notice that this code simple ignores + * zero length compressed chunks as a result. + */ + return 0; + } +} - else if (ret == Z_DATA_ERROR) - png_snprintf(umsg, 52, - "Data error in compressed datastream in %s chunk", - png_ptr->chunk_name); +/* + * Decompress trailing data in a chunk. The assumption is that chunkdata + * points at an allocated area holding the contents of a chunk with a + * trailing compressed part. What we get back is an allocated area + * holding the original prefix part and an uncompressed version of the + * trailing part (the malloc area passed in is freed). + */ +void /* PRIVATE */ +png_decompress_chunk(png_structp png_ptr, int comp_type, + png_size_t chunklength, + png_size_t prefix_size, png_size_t *newlength) +{ + /* The caller should guarantee this */ + if (prefix_size > chunklength) + { + /* The recovery is to delete the chunk. */ + png_warning(png_ptr, "invalid chunklength"); + prefix_size = 0; /* To delete everything */ + } - else - png_snprintf(umsg, 52, - "Incomplete compressed datastream in %s chunk", - png_ptr->chunk_name); + 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*/); - png_warning(png_ptr, umsg); + /* Now check the limits on this chunk - if the limit fails the + * compressed data will be removed, the prefix will remain. + */ +#ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED + if (png_ptr->user_chunk_malloc_max && + (prefix_size + expanded_size >= png_ptr->user_chunk_malloc_max - 1)) #else - png_warning(png_ptr, - "Incomplete compressed datastream in chunk other than IDAT"); + if ((PNG_USER_CHUNK_MALLOC_MAX > 0) && + prefix_size + expanded_size >= PNG_USER_CHUNK_MALLOC_MAX - 1) #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; - } + png_warning(png_ptr, "Exceeded size limit while expanding chunk"); - inflateReset(&png_ptr->zstream); - png_ptr->zstream.avail_in = 0; + /* 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); - png_free(png_ptr, png_ptr->chunkdata); - png_ptr->chunkdata = text; - *newlength=text_size; + 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_STDIO_SUPPORTED) && !defined(_WIN32_WCE) char umsg[50]; - png_snprintf(umsg, 50, "Unknown zTXt compression type %d", comp_type); +#if defined(PNG_STDIO_SUPPORTED) && !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 @@ -1179,6 +1163,24 @@ png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) png_debug(1, "in png_handle_sPLT"); +#ifdef PNG_USER_LIMITS_SUPPORTED + + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for sPLT"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before sPLT"); else if (png_ptr->mode & PNG_HAVE_IDAT) @@ -1211,7 +1213,8 @@ png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) png_ptr->chunkdata[slength] = 0x00; - for (entry_start = (png_bytep)png_ptr->chunkdata; *entry_start; entry_start++) + for (entry_start = (png_bytep)png_ptr->chunkdata; *entry_start; + entry_start++) /* Empty loop to find end of name */ ; ++entry_start; @@ -1965,6 +1968,23 @@ png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) png_debug(1, "in png_handle_tEXt"); +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for tEXt"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before tEXt"); @@ -2050,6 +2070,23 @@ png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) png_debug(1, "in png_handle_zTXt"); +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for zTXt"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before zTXt"); @@ -2154,6 +2191,23 @@ png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) png_debug(1, "in png_handle_iTXt"); +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for iTXt"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before iTXt"); @@ -2281,6 +2335,23 @@ png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) png_debug(1, "in png_handle_unknown"); +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for unknown chunk"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + if (png_ptr->mode & PNG_HAVE_IDAT) { #ifdef PNG_USE_LOCAL_ARRAYS @@ -2321,7 +2392,8 @@ png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) png_memcpy((png_charp)png_ptr->unknown_chunk.name, (png_charp)png_ptr->chunk_name, png_sizeof(png_ptr->unknown_chunk.name)); - png_ptr->unknown_chunk.name[png_sizeof(png_ptr->unknown_chunk.name)-1] = '\0'; + png_ptr->unknown_chunk.name[png_sizeof(png_ptr->unknown_chunk.name)-1] + = '\0'; png_ptr->unknown_chunk.size = (png_size_t)length; if (length == 0) png_ptr->unknown_chunk.data = NULL; @@ -2793,7 +2865,8 @@ png_do_read_interlace(png_structp png_ptr) default: { png_size_t pixel_bytes = (row_info->pixel_depth >> 3); - png_bytep sp = row + (png_size_t)(row_info->width - 1) * pixel_bytes; + png_bytep sp = row + (png_size_t)(row_info->width - 1) + * pixel_bytes; png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; int jstop = png_pass_inc[pass]; @@ -2988,9 +3061,6 @@ png_read_finish_row(png_structp png_ptr) png_pass_start[png_ptr->pass]) / png_pass_inc[png_ptr->pass]; - png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, - png_ptr->iwidth) + 1; - if (!(png_ptr->transformations & PNG_INTERLACE)) { png_ptr->num_rows = (png_ptr->height + @@ -3049,7 +3119,7 @@ png_read_finish_row(png_structp png_ptr) { if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in || png_ptr->idat_size) - png_warning(png_ptr, "Extra compressed data"); + png_warning(png_ptr, "Extra compressed data."); png_ptr->mode |= PNG_AFTER_IDAT; png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; break; @@ -3071,7 +3141,7 @@ png_read_finish_row(png_structp png_ptr) } if (png_ptr->idat_size || png_ptr->zstream.avail_in) - png_warning(png_ptr, "Extra compression data"); + png_warning(png_ptr, "Extra compression data."); inflateReset(&png_ptr->zstream); @@ -3117,16 +3187,12 @@ png_read_start_row(png_structp png_ptr) png_pass_inc[png_ptr->pass] - 1 - png_pass_start[png_ptr->pass]) / png_pass_inc[png_ptr->pass]; - - png_ptr->irowbytes = - PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1; } else #endif /* PNG_READ_INTERLACING_SUPPORTED */ { png_ptr->num_rows = png_ptr->height; png_ptr->iwidth = png_ptr->width; - png_ptr->irowbytes = png_ptr->rowbytes + 1; } max_pixel_depth = png_ptr->pixel_depth; @@ -3283,7 +3349,8 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) png_debug1(3, "iwidth = %lu,", png_ptr->iwidth); png_debug1(3, "num_rows = %lu,", png_ptr->num_rows); png_debug1(3, "rowbytes = %lu,", png_ptr->rowbytes); - png_debug1(3, "irowbytes = %lu", png_ptr->irowbytes); + png_debug1(3, "irowbytes = %lu", + PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1); png_ptr->flags |= PNG_FLAG_ROW_INIT; } |