mirror of
https://git.code.sf.net/p/libpng/code.git
synced 2025-07-10 18:04:09 +02:00
226 lines
7.7 KiB
C
226 lines
7.7 KiB
C
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 */
|