| png_decompress_chunk(png_structrp png_ptr, |
| png_uint_32 chunklength, png_uint_32 prefix_size, |
| png_alloc_size_t *newlength /* must be initialized to the maximum! */, |
| int terminate /*add a '\0' to the end of the uncompressed data*/) |
| { |
| /* TODO: implement different limits for different types of chunk. |
| * |
| * The caller supplies *newlength set to the maximum length of the |
| * uncompressed data, but this routine allocates space for the prefix and |
| * maybe a '\0' terminator too. We have to assume that 'prefix_size' is |
| * limited only by the maximum chunk size. |
| */ |
| png_alloc_size_t limit = PNG_SIZE_MAX; |
| |
| # ifdef PNG_SET_USER_LIMITS_SUPPORTED |
| if (png_ptr->user_chunk_malloc_max > 0 && |
| png_ptr->user_chunk_malloc_max < limit) |
| limit = png_ptr->user_chunk_malloc_max; |
| # elif PNG_USER_CHUNK_MALLOC_MAX > 0 |
| if (PNG_USER_CHUNK_MALLOC_MAX < limit) |
| limit = PNG_USER_CHUNK_MALLOC_MAX; |
| # endif |
| |
| if (limit >= prefix_size + (terminate != 0)) |
| { |
| int ret; |
| |
| limit -= prefix_size + (terminate != 0); |
| |
| if (limit < *newlength) |
| *newlength = limit; |
| |
| /* Now try to claim the stream. */ |
| ret = png_inflate_claim(png_ptr, png_ptr->chunk_name); |
| |
| if (ret == Z_OK) |
| { |
| png_uint_32 lzsize = chunklength - prefix_size; |
| |
| ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/, |
| /* input: */ png_ptr->read_buffer + prefix_size, &lzsize, |
| /* output: */ NULL, newlength); |
| |
| if (ret == Z_STREAM_END) |
| { |
| /* Use 'inflateReset' here, not 'inflateReset2' because this |
| * preserves the previously decided window size (otherwise it would |
| * be necessary to store the previous window size.) In practice |
| * this doesn't matter anyway, because png_inflate will call inflate |
| * with Z_FINISH in almost all cases, so the window will not be |
| * maintained. |
| */ |
| if (inflateReset(&png_ptr->zstream) == Z_OK) |
| { |
| /* Because of the limit checks above we know that the new, |
| * expanded, size will fit in a size_t (let alone an |
| * png_alloc_size_t). Use png_malloc_base here to avoid an |
| * extra OOM message. |
| */ |
| png_alloc_size_t new_size = *newlength; |
| png_alloc_size_t buffer_size = prefix_size + new_size + |
| (terminate != 0); |
| png_bytep text = png_voidcast(png_bytep, png_malloc_base(png_ptr, |
| buffer_size)); |
| |
| if (text != NULL) |
| { |
| ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/, |
| png_ptr->read_buffer + prefix_size, &lzsize, |
| text + prefix_size, newlength); |
| |
| if (ret == Z_STREAM_END) |
| { |
| if (new_size == *newlength) |
| { |
| if (terminate != 0) |
| text[prefix_size + *newlength] = 0; |
| |
| if (prefix_size > 0) |
| memcpy(text, png_ptr->read_buffer, prefix_size); |
| |
| { |
| png_bytep old_ptr = png_ptr->read_buffer; |
| |
| png_ptr->read_buffer = text; |
| png_ptr->read_buffer_size = buffer_size; |
| text = old_ptr; /* freed below */ |
| } |
| } |
| |
| else |
| { |
| /* The size changed on the second read, there can be no |
| * guarantee that anything is correct at this point. |
| * The 'msg' pointer has been set to "unexpected end of |
| * LZ stream", which is fine, but return an error code |
| * that the caller won't accept. |
| */ |
| ret = PNG_UNEXPECTED_ZLIB_RETURN; |
| } |
| } |
| |
| else if (ret == Z_OK) |
| ret = PNG_UNEXPECTED_ZLIB_RETURN; /* for safety */ |
| |
| /* Free the text pointer (this is the old read_buffer on |
| * success) |
| */ |
| png_free(png_ptr, text); |
| |
| /* This really is very benign, but it's still an error because |
| * the extra space may otherwise be used as a Trojan Horse. |
| */ |
| if (ret == Z_STREAM_END && |
| chunklength - prefix_size != lzsize) |
| png_chunk_benign_error(png_ptr, "extra compressed data"); |
| } |
| |
| else |
| { |
| /* Out of memory allocating the buffer */ |
| ret = Z_MEM_ERROR; |
| png_zstream_error(&png_ptr->zstream, Z_MEM_ERROR); |
| } |
| } |
| |
| else |
| { |
| /* inflateReset failed, store the error message */ |
| png_zstream_error(&png_ptr->zstream, ret); |
| |
| if (ret == Z_STREAM_END) |
| ret = PNG_UNEXPECTED_ZLIB_RETURN; |
| } |
| } |
| |
| else if (ret == Z_OK) |
| ret = PNG_UNEXPECTED_ZLIB_RETURN; |
| |
| /* Release the claimed stream */ |
| png_ptr->zowner = 0; |
| } |
| |
| else /* the claim failed */ if (ret == Z_STREAM_END) /* impossible! */ |
| ret = PNG_UNEXPECTED_ZLIB_RETURN; |
| |
| return ret; |
| } |
| |
| else |
| { |
| /* Application/configuration limits exceeded */ |
| png_zstream_error(&png_ptr->zstream, Z_MEM_ERROR); |
| return Z_MEM_ERROR; |
| } |
| } |
| #endif /* READ_COMPRESSED_TEXT */ |
| |
| #ifdef PNG_READ_iCCP_SUPPORTED |
| /* Perform a partial read and decompress, producing 'avail_out' bytes and |
| * reading from the current chunk as required. |
| */ |
| static int |
| png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size, |
| png_uint_32p chunk_bytes, png_bytep next_out, png_alloc_size_t *out_size, |
| int finish) |
| { |
| if (png_ptr->zowner == png_ptr->chunk_name) |
| { |
| int ret; |
| |
| /* next_in and avail_in must have been initialized by the caller. */ |
| png_ptr->zstream.next_out = next_out; |
| png_ptr->zstream.avail_out = 0; /* set in the loop */ |
| |
| do |
| { |
| if (png_ptr->zstream.avail_in == 0) |
| { |
| if (read_size > *chunk_bytes) |
| read_size = (uInt)*chunk_bytes; |
| *chunk_bytes -= read_size; |
| |
| if (read_size > 0) |
| png_crc_read(png_ptr, read_buffer, read_size); |
| |
| png_ptr->zstream.next_in = read_buffer; |
| png_ptr->zstream.avail_in = read_size; |
| } |
| |
| if (png_ptr->zstream.avail_out == 0) |
| { |
| uInt avail = ZLIB_IO_MAX; |
| if (avail > *out_size) |
| avail = (uInt)*out_size; |
| *out_size -= avail; |
| |
| png_ptr->zstream.avail_out = avail; |
| } |
| |
| /* Use Z_SYNC_FLUSH when there is no more chunk data to ensure that all |
| * the available output is produced; this allows reading of truncated |
| * streams. |
| */ |
| ret = inflate(&png_ptr->zstream, |
| *chunk_bytes > 0 ? Z_NO_FLUSH : (finish ? Z_FINISH : |
| Z_SYNC_FLUSH)); |
| } |
| while (ret == Z_OK && (*out_size > 0 || png_ptr->zstream.avail_out > 0)); |
| |
| *out_size += png_ptr->zstream.avail_out; |
| png_ptr->zstream.avail_out = 0; /* Should not be required, but is safe */ |
| |
| /* Ensure the error message pointer is always set: */ |
| png_zstream_error(&png_ptr->zstream, ret); |
| return ret; |
| } |
| |
| else |
| { |
| png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed"); |
| return Z_STREAM_ERROR; |
| } |
| } |
| #endif /* READ_iCCP */ |