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 */