[libpng16] Deflate/inflate was reworked to move common zlib calls into single
functions [rw]util.c. A new shared keyword check routine was also added
and the 'zbuf' is no longer allocated on progressive read. It is now
possible to call png_inflate() incrementally.
diff --git a/ANNOUNCE b/ANNOUNCE
index 4a0fdde..f91b5e4 100644
--- a/ANNOUNCE
+++ b/ANNOUNCE
@@ -1,5 +1,5 @@
-Libpng 1.6.0beta17 - March 8, 2012
+Libpng 1.6.0beta17 - March 9, 2012
This is not intended to be a public release. It will be replaced
within a few weeks by a public version or by another test version.
@@ -286,9 +286,13 @@
If the call to deflateInit2() is wrong a png_warning will be issued
(in fact this is harmless, but the PNG data produced may be sub-optimal).
-Version 1.6.0beta17 [March 8, 2012]
+Version 1.6.0beta17 [March 9, 2012]
Fixed PNG_LIBPNG_BUILD_BASE_TYPE definition.
- Reject iCCP chunk after the first, even if the first one is invalid.
+ Reject all iCCP chunks after the first, even if the first one is invalid.
+ Deflate/inflate was reworked to move common zlib calls into single
+ functions [rw]util.c. A new shared keyword check routine was also added
+ and the 'zbuf' is no longer allocated on progressive read. It is now
+ possible to call png_inflate() incrementally.
Send comments/corrections/commendations to png-mng-implement at lists.sf.net
(subscription required; visit
diff --git a/CHANGES b/CHANGES
index 2e118e5..080a0d7 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4037,9 +4037,13 @@
If the call to deflateInit2() is wrong a png_warning will be issued
(in fact this is harmless, but the PNG data produced may be sub-optimal).
-Version 1.6.0beta17 [March 8, 2012]
+Version 1.6.0beta17 [March 9, 2012]
Fixed PNG_LIBPNG_BUILD_BASE_TYPE definition.
- Reject iCCP chunk after the first, even if the first one is invalid.
+ Reject all iCCP chunks after the first, even if the first one is invalid.
+ Deflate/inflate was reworked to move common zlib calls into single
+ functions [rw]util.c. A new shared keyword check routine was also added
+ and the 'zbuf' is no longer allocated on progressive read. It is now
+ possible to call png_inflate() incrementally.
Send comments/corrections/commendations to png-mng-implement at lists.sf.net
(subscription required; visit
diff --git a/png.c b/png.c
index 3f1b7f7..cf8c29c 100644
--- a/png.c
+++ b/png.c
@@ -283,41 +283,29 @@
*/
if (png_user_version_check(&create_struct, user_png_ver))
{
- /* TODO: delay initializing the zlib structure until it really is
- * needed.
- */
- /* Initialize zbuf - compression/decompression buffer */
- create_struct.zbuf_size = PNG_ZBUF_SIZE;
- create_struct.zbuf = png_voidcast(png_bytep,
- png_malloc_warn(&create_struct, create_struct.zbuf_size));
+ png_structrp png_ptr = png_voidcast(png_structrp,
+ png_malloc_warn(&create_struct, sizeof *png_ptr));
- /* Finally allocate the png_struct itself. */
- if (create_struct.zbuf != NULL)
+ if (png_ptr != NULL)
{
- png_structrp png_ptr = png_voidcast(png_structrp,
- png_malloc_warn(&create_struct, sizeof *png_ptr));
+ /* png_ptr->zstream holds a back-pointer to the png_struct, so
+ * this can only be done now:
+ */
+ create_struct.zstream.zalloc = png_zalloc;
+ create_struct.zstream.zfree = png_zfree;
+ create_struct.zstream.opaque = png_ptr;
- if (png_ptr != NULL)
- {
-# ifdef PNG_SETJMP_SUPPORTED
- /* Eliminate the local error handling: */
- create_struct.jmp_buf_ptr = NULL;
- create_struct.jmp_buf_size = 0;
- create_struct.longjmp_fn = 0;
-# endif
+# ifdef PNG_SETJMP_SUPPORTED
+ /* Eliminate the local error handling: */
+ create_struct.jmp_buf_ptr = NULL;
+ create_struct.jmp_buf_size = 0;
+ create_struct.longjmp_fn = 0;
+# endif
- *png_ptr = create_struct;
+ *png_ptr = create_struct;
- /* png_ptr->zstream holds a back-pointer to the png_struct, so
- * this can only be done now:
- */
- png_ptr->zstream.zalloc = png_zalloc;
- png_ptr->zstream.zfree = png_zfree;
- png_ptr->zstream.opaque = png_ptr;
-
- /* This is the successful return point */
- return png_ptr;
- }
+ /* This is the successful return point */
+ return png_ptr;
}
}
}
@@ -325,15 +313,6 @@
/* A longjmp because of a bug in the application storage allocator or a
* simple failure to allocate the png_struct.
*/
- if (create_struct.zbuf != NULL)
- {
- png_bytep zbuf = create_struct.zbuf;
-
- /* Ensure we don't keep on returning to this point: */
- create_struct.zbuf = NULL;
- png_free(&create_struct, zbuf);
- }
-
return NULL;
}
@@ -768,13 +747,13 @@
#else
# ifdef __STDC__
return PNG_STRING_NEWLINE \
- "libpng version 1.6.0beta17 - March 6, 2012" PNG_STRING_NEWLINE \
+ "libpng version 1.6.0beta17 - March 9, 2012" PNG_STRING_NEWLINE \
"Copyright (c) 1998-2012 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \
"Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \
"Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \
PNG_STRING_NEWLINE;
# else
- return "libpng version 1.6.0beta17 - March 6, 2012\
+ return "libpng version 1.6.0beta17 - March 9, 2012\
Copyright (c) 1998-2012 Glenn Randers-Pehrson\
Copyright (c) 1996-1997 Andreas Dilger\
Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.";
@@ -867,6 +846,7 @@
if (png_ptr == NULL)
return Z_STREAM_ERROR;
+ /* WARNING: this resets the window bits to the maximum! */
return (inflateReset(&png_ptr->zstream));
}
#endif /* PNG_READ_SUPPORTED */
@@ -882,6 +862,76 @@
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+/* Ensure that png_ptr->zstream.msg holds some appropriate error message string.
+ * If it doesn't 'ret' is used to set it to something appropriate, even in cases
+ * like Z_OK or Z_STREAM_END where the error code is apparently a success code.
+ */
+void /* PRIVATE */
+png_zstream_error(png_structrp png_ptr, int ret)
+{
+ /* Translate 'ret' into an appropriate error string, priority is given to the
+ * one in zstream if set. This always returns a string, even in cases like
+ * Z_OK or Z_STREAM_END where the error code is a success code.
+ */
+ if (png_ptr->zstream.msg == NULL) switch (ret)
+ {
+ default:
+ case Z_OK:
+ png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return code");
+ break;
+
+ case Z_STREAM_END:
+ /* Normal exit */
+ png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected end of LZ stream");
+ break;
+
+ case Z_NEED_DICT:
+ /* This means the deflate stream did not have a dictionary; this
+ * indicates a bogus PNG.
+ */
+ png_ptr->zstream.msg = PNGZ_MSG_CAST("missing LZ dictionary");
+ break;
+
+ case Z_ERRNO:
+ /* gz APIs only: should not happen */
+ png_ptr->zstream.msg = PNGZ_MSG_CAST("zlib IO error");
+ break;
+
+ case Z_STREAM_ERROR:
+ /* internal libpng error */
+ png_ptr->zstream.msg = PNGZ_MSG_CAST("bad parameters to zlib");
+ break;
+
+ case Z_DATA_ERROR:
+ png_ptr->zstream.msg = PNGZ_MSG_CAST("damaged LZ stream");
+ break;
+
+ case Z_MEM_ERROR:
+ png_ptr->zstream.msg = PNGZ_MSG_CAST("insufficient memory");
+ break;
+
+ case Z_BUF_ERROR:
+ /* End of input or output; not a problem if the caller is doing
+ * incremental read or write.
+ */
+ png_ptr->zstream.msg = PNGZ_MSG_CAST("truncated");
+ break;
+
+ case Z_VERSION_ERROR:
+ png_ptr->zstream.msg = PNGZ_MSG_CAST("unsupported zlib version");
+ break;
+
+ case PNG_UNEXPECTED_ZLIB_RETURN:
+ /* Compile errors here mean that zlib now uses the value co-opted in
+ * pngpriv.h for PNG_UNEXPECTED_ZLIB_RETURN; update the switch above
+ * and change pngpriv.h. Note that this message is "... return",
+ * whereas the default/Z_OK one is "... return code".
+ */
+ png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return");
+ break;
+ }
+}
+
/* png_convert_size: a PNGAPI but no longer in png.h, so deleted
* at libpng 1.5.5!
*/
diff --git a/png.h b/png.h
index 748582a..c75b3b6 100644
--- a/png.h
+++ b/png.h
@@ -1,7 +1,7 @@
/* png.h - header file for PNG reference library
*
- * libpng version 1.6.0beta17 - March 6, 2012
+ * libpng version 1.6.0beta17 - March 9, 2012
* Copyright (c) 1998-2012 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.)
@@ -11,7 +11,7 @@
* Authors and maintainers:
* libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat
* libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger
- * libpng versions 0.97, January 1998, through 1.6.0beta17 - March 6, 2012: Glenn
+ * libpng versions 0.97, January 1998, through 1.6.0beta17 - March 9, 2012: Glenn
* See also "Contributing Authors", below.
*
* Note about libpng version numbers:
@@ -198,7 +198,7 @@
*
* This code is released under the libpng license.
*
- * libpng versions 1.2.6, August 15, 2004, through 1.6.0beta17, March 6, 2012, are
+ * libpng versions 1.2.6, August 15, 2004, through 1.6.0beta17, March 9, 2012, are
* Copyright (c) 2004, 2006-2012 Glenn Randers-Pehrson, and are
* distributed according to the same disclaimer and license as libpng-1.2.5
* with the following individual added to the list of Contributing Authors:
@@ -310,7 +310,7 @@
* Y2K compliance in libpng:
* =========================
*
- * March 6, 2012
+ * March 9, 2012
*
* Since the PNG Development group is an ad-hoc body, we can't make
* an official declaration.
@@ -376,7 +376,7 @@
/* Version information for png.h - this should match the version in png.c */
#define PNG_LIBPNG_VER_STRING "1.6.0beta17"
#define PNG_HEADER_VERSION_STRING \
- " libpng version 1.6.0beta17 - March 6, 2012\n"
+ " libpng version 1.6.0beta17 - March 9, 2012\n"
#define PNG_LIBPNG_VER_SONUM 16
#define PNG_LIBPNG_VER_DLLNUM 16
@@ -1047,7 +1047,7 @@
#ifdef PNG_READ_SUPPORTED
/* Reset the compression stream */
-PNG_EXPORT(10, int, png_reset_zstream, (png_structrp png_ptr));
+PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED);
#endif
/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */
diff --git a/pngget.c b/pngget.c
index e2c9175..0fedb44 100644
--- a/pngget.c
+++ b/pngget.c
@@ -1070,7 +1070,24 @@
png_size_t PNGAPI
png_get_compression_buffer_size(png_const_structrp png_ptr)
{
- return (png_ptr ? png_ptr->zbuf_size : 0);
+ if (png_ptr == NULL)
+ return 0;
+
+# ifdef PNG_WRITE_SUPPORTED
+ if (png_ptr->mode & PNG_IS_READ_STRUCT)
+# endif
+ {
+# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+ return png_ptr->IDAT_read_size;
+# else
+ return PNG_IDAT_READ_SIZE;
+# endif
+ }
+
+# ifdef PNG_WRITE_SUPPORTED
+ else
+ return png_ptr->zbuffer_size;
+# endif
}
#ifdef PNG_SET_USER_LIMITS_SUPPORTED
diff --git a/pngpread.c b/pngpread.c
index c7de794..aed706f 100644
--- a/pngpread.c
+++ b/pngpread.c
@@ -838,7 +838,7 @@
png_crc_finish(png_ptr, 0);
png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
png_ptr->mode |= PNG_AFTER_IDAT;
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
+ png_ptr->zowner = 0;
}
}
@@ -894,7 +894,7 @@
{
/* Terminate the decompression. */
png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
+ png_ptr->zowner = 0;
/* This may be a truncated stream (missing or
* damaged end code). Treat that as a warning.
@@ -923,7 +923,7 @@
/* Extra data. */
png_warning(png_ptr, "Extra compressed data in IDAT");
png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
+ png_ptr->zowner = 0;
/* Do no more processing; skip the unprocessed
* input check below.
diff --git a/pngpriv.h b/pngpriv.h
index af5dae0..313f0fe 100644
--- a/pngpriv.h
+++ b/pngpriv.h
@@ -467,6 +467,7 @@
#define PNG_HAVE_PNG_SIGNATURE 0x1000
#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */
#define PNG_HAVE_iCCP 0x4000
+#define PNG_IS_READ_STRUCT 0x8000 /* Else is a write struct */
/* Flags for the transformations the PNG library does on the image data */
#define PNG_BGR 0x0001
@@ -512,9 +513,9 @@
/* Flags for the png_ptr->flags rather than declaring a byte for each one */
#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001
#define PNG_FLAG_ZSTREAM_INITIALIZED 0x0002 /* Added to libpng-1.6.0 */
-#define PNG_FLAG_ZSTREAM_IN_USE 0x0004 /* Added to libpng-1.6.0 */
+ /* 0x0004 unused */
#define PNG_FLAG_ZSTREAM_ENDED 0x0008 /* Added to libpng-1.6.0 */
-#define PNG_FLAG_ZSTREAM_CMF_FIXUP 0x0010 /* Added to libpng-1.6.0 */
+ /* 0x0010 unused */
/* 0x0020 unused */
#define PNG_FLAG_ROW_INIT 0x0040
#define PNG_FLAG_FILLER_AFTER 0x0080
@@ -551,18 +552,6 @@
#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \
PNG_FLAG_CRC_CRITICAL_MASK)
-/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib
- * can handle at once. This type need be no larger than 16 bits (so maximum of
- * 65535), this define allows us to discover how big it is, but limited by the
- * maximuum for png_size_t. The value can be overriden in a library build
- * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably
- * lower value (e.g. 255 works). A lower value may help memory usage (slightly)
- * and may even improve performance on some systems (and degrade it on others.)
- */
-#ifndef ZLIB_IO_MAX
-# define ZLIB_IO_MAX ((uInt)-1)
-#endif
-
/* Save typing and make code easier to understand */
#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \
@@ -736,7 +725,18 @@
*/
/* Zlib support */
-PNG_INTERNAL_FUNCTION(void,png_inflate_claim,(png_structrp png_ptr),PNG_EMPTY);
+#define PNG_UNEXPECTED_ZLIB_RETURN (-7)
+PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret),
+ PNG_EMPTY);
+ /* Used by the zlib handling functions to ensure that z_stream::msg is always
+ * set before they return.
+ */
+
+#ifdef PNG_WRITE_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr,
+ png_compression_bufferp *list),PNG_EMPTY);
+ /* Free the buffer list used by the compressed write code. */
+#endif
#if defined PNG_FLOATING_POINT_SUPPORTED &&\
!defined PNG_FIXED_POINT_MACRO_SUPPORTED
@@ -851,15 +851,16 @@
/* Write the IHDR chunk, and update the png_struct with the necessary
* information.
*/
-PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr, png_uint_32 width,
- png_uint_32 height, int bit_depth, int color_type, int compression_method,
- int filter_method, int interlace_method),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr,
+ png_uint_32 width, png_uint_32 height, int bit_depth, int color_type,
+ int compression_method, int filter_method, int interlace_method),PNG_EMPTY);
PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr,
png_const_colorp palette, png_uint_32 num_pal),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_write_IDAT,(png_structrp png_ptr, png_bytep data,
- png_size_t length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr,
+ png_const_bytep row_data, png_alloc_size_t row_data_length, int flush),
+ PNG_EMPTY);
PNG_INTERNAL_FUNCTION(void,png_write_IEND,(png_structrp png_ptr),PNG_EMPTY);
@@ -921,15 +922,9 @@
#endif
/* Chunks that have keywords */
-#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
- defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
-PNG_INTERNAL_FUNCTION(png_size_t,png_check_keyword,(png_structrp png_ptr,
- png_const_charp key, png_charpp new_key),PNG_EMPTY);
-#endif
-
#ifdef PNG_WRITE_tEXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr, png_const_charp key,
- png_const_charp text, png_size_t text_len),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr,
+ png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY);
#endif
#ifdef PNG_WRITE_zTXt_SUPPORTED
@@ -1054,8 +1049,24 @@
PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr,
png_row_infop row_info),PNG_EMPTY);
-/* Finish a row while reading, dealing with interlacing passes, etc. */
-PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr),PNG_EMPTY);
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr,
+ png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY);
+ /* Read 'avail_out' bytes of data from the IDAT stream. If the output buffer
+ * is NULL the function checks, instead, for the end of the stream. In this
+ * case a benign error will be issued if the stream end is not found or if
+ * extra data has to be consumed.
+ */
+PNG_INTERNAL_FUNCTION(void,png_read_finish_IDAT,(png_structrp png_ptr),
+ PNG_EMPTY);
+ /* This cleans up when the IDAT LZ stream does not end when the last image
+ * byte is read; there is still some pending input.
+ */
+
+PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr),
+ PNG_EMPTY);
+ /* Finish a row while reading, dealing with interlacing passes, etc. */
+#endif
/* Initialize the row buffers, etc. */
PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY);
diff --git a/pngread.c b/pngread.c
index 59721fe..68304b8 100644
--- a/pngread.c
+++ b/pngread.c
@@ -48,6 +48,14 @@
if (png_ptr != NULL)
{
+ png_ptr->mode = PNG_IS_READ_STRUCT;
+
+ /* Adding in libpng-1.6.0; this can be used to detect a read structure if
+ * required (it will be zero in a write structure.)
+ */
+# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+ png_ptr->IDAT_read_size = PNG_IDAT_READ_SIZE;
+# endif
/* TODO: delay this, it can be done in png_init_io (if the app doesn't
* do it itself) avoiding setting the default function if it is not
* required.
@@ -268,8 +276,6 @@
void PNGAPI
png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row)
{
- int ret;
-
png_row_info row_info;
if (png_ptr == NULL)
@@ -423,56 +429,8 @@
if (!(png_ptr->mode & PNG_HAVE_IDAT))
png_error(png_ptr, "Invalid attempt to read row data");
- png_ptr->zstream.next_out = png_ptr->row_buf;
- /* TODO: WARNING: BAD NEWS ALERT: this fails, terminally, if the row width is
- * bigger than a uInt.
- */
- png_ptr->zstream.avail_out = (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth,
- png_ptr->iwidth) + 1);
-
- do
- {
- if (!(png_ptr->zstream.avail_in))
- {
- while (png_ptr->idat_size == 0)
- {
- png_crc_finish(png_ptr, 0);
-
- png_ptr->idat_size = png_read_chunk_header(png_ptr);
- if (png_ptr->chunk_name != png_IDAT)
- png_error(png_ptr, "Not enough image data");
- }
- png_ptr->zstream.avail_in = png_ptr->zbuf_size;
- png_ptr->zstream.next_in = png_ptr->zbuf;
- if (png_ptr->zbuf_size > png_ptr->idat_size)
- png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size;
- png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in);
- png_ptr->idat_size -= png_ptr->zstream.avail_in;
- }
-
- /* Use NO_FLUSH, not SYNC_FLUSH, here because we keep reading data until
- * we have a row to process (so leave it to zlib to decide when to flush
- * the output.)
- */
- ret = inflate(&png_ptr->zstream, Z_NO_FLUSH);
-
- if (ret == Z_STREAM_END)
- {
- if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in ||
- png_ptr->idat_size)
- png_benign_error(png_ptr, "Extra compressed data");
- png_ptr->mode |= PNG_AFTER_IDAT;
- /* Release the stream */
- png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
- break;
- }
-
- if (ret != Z_OK)
- png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg :
- "Decompression error");
-
- } while (png_ptr->zstream.avail_out);
+ /* Fill the row with IDAT data: */
+ png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1);
if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE)
{
@@ -702,7 +660,10 @@
if (png_ptr == NULL)
return;
- png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */
+ /* If png_read_end is called in the middle of reading the rows there may
+ * still be pending IDAT data and an owned zstream. Deal with this here.
+ */
+ png_read_finish_IDAT(png_ptr);
#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
/* Report invalid palette index; added at libng-1.5.10 */
@@ -851,10 +812,9 @@
png_destroy_gamma_table(png_ptr);
#endif
- png_free(png_ptr, png_ptr->zbuf);
png_free(png_ptr, png_ptr->big_row_buf);
png_free(png_ptr, png_ptr->big_prev_row);
- png_free(png_ptr, png_ptr->chunkdata);
+ png_free(png_ptr, png_ptr->read_buffer);
#ifdef PNG_READ_QUANTIZE_SUPPORTED
png_free(png_ptr, png_ptr->palette_lookup);
diff --git a/pngrutil.c b/pngrutil.c
index fd89c89..2befd4a 100644
--- a/pngrutil.c
+++ b/pngrutil.c
@@ -203,17 +203,20 @@
int /* PRIVATE */
png_crc_finish(png_structrp png_ptr, png_uint_32 skip)
{
- png_uint_32 i;
- uInt istop = png_ptr->zbuf_size;
-
- for (i = skip; i > istop; i -= istop)
+ /* The size of the local buffer for inflate is a good guess as to a
+ * reasonable size to use for buffering reads from the application.
+ */
+ while (skip > 0)
{
- png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
- }
+ png_uint_32 len;
+ png_byte tmpbuf[PNG_INFLATE_BUF_SIZE];
- if (i)
- {
- png_crc_read(png_ptr, png_ptr->zbuf, i);
+ len = sizeof tmpbuf;
+ if (len > skip)
+ len = skip;
+ skip -= len;
+
+ png_crc_read(png_ptr, tmpbuf, len);
}
if (png_crc_error(png_ptr))
@@ -277,12 +280,72 @@
return (0);
}
-/* png_inflate_claim: claim the zstream for some nefarious purpose that involves
- * decompression.
+/* Manage the read buffer; this simply reallocates the buffer if it is not small
+ * enough (or if it is not allocated). The routine returns a pointer to the
+ * buffer, if an error occurs and 'warn' is set the routine returns NULL, else
+ * it will call png_error (via png_malloc) on failure. (warn == 2 means
+ * 'silent').
*/
-void /* PRIVATE */
-png_inflate_claim(png_structrp png_ptr)
+static png_bytep
+png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn)
{
+ png_bytep buffer = png_ptr->read_buffer;
+
+ if (buffer != NULL && new_size > png_ptr->read_buffer_size)
+ {
+ png_ptr->read_buffer = NULL;
+ png_ptr->read_buffer = NULL;
+ png_ptr->read_buffer_size = 0;
+ png_free(png_ptr, buffer);
+ buffer = NULL;
+ }
+
+ if (buffer == NULL)
+ {
+ buffer = png_voidcast(png_bytep, png_malloc_base(png_ptr, new_size));
+
+ if (buffer != NULL)
+ {
+ png_ptr->read_buffer = buffer;
+ png_ptr->read_buffer_size = new_size;
+ }
+
+ else if (warn < 2) /* else silent */
+ {
+ (warn ? png_chunk_warning : png_chunk_error)(png_ptr,
+ "insufficient memory to read chunk");
+ }
+ }
+
+ return buffer;
+}
+
+/* png_inflate_claim: claim the zstream for some nefarious purpose that involves
+ * decompression. Returns Z_OK on success, else a zlib error code. It checks
+ * the owner but, in final release builds, just issues a warning if some other
+ * chunk apparently owns the stream. Prior to release it does a png_error.
+ */
+static int
+png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)
+{
+ if (png_ptr->zowner != 0)
+ {
+ char msg[64];
+
+ PNG_STRING_FROM_CHUNK(msg, png_ptr->zowner);
+ /* So the message that results is "<chunk> using zstream"; this is an
+ * internal error, but is very useful for debugging. i18n requirements
+ * are minimal.
+ */
+ (void)png_safecat(msg, sizeof msg, 4, " using zstream");
+# if PNG_LIBPNG_BUILD_BASE_TYPE == PNG_LIBPNG_BUILD_STABLE
+ png_chunk_warning(png_ptr, msg);
+ png_ptr->zowner = 0;
+# else
+ png_chunk_error(png_ptr, msg);
+# endif
+ }
+
/* Implementation note: unlike 'png_deflate_claim' this internal function
* does not take the size of the data as an argument. Some efficiency could
* be gained by using this when it is known *if* the zlib stream itself does
@@ -291,227 +354,193 @@
* that because, for systems with with limited capabilities, we would
* otherwise reject the applications attempts to use a smaller window size.
* (zlib doesn't have an interface to say "this or lower"!)
+ *
+ * inflateReset2 was added to zlib 1.2.4; before this the window could not be
+ * reset, therefore it is necessary to always allocate the maximum window
+ * size with earlier zlibs just in case later compressed chunks need it.
*/
- if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_IN_USE))
{
int ret; /* zlib return code */
- /* Do this for safety now. */
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_ENDED;
+ /* Set this for safety, just in case the previous owner left pointers to
+ * memory allocations.
+ */
+ png_ptr->zstream.next_in = NULL;
+ png_ptr->zstream.avail_in = 0;
+ png_ptr->zstream.next_out = NULL;
+ png_ptr->zstream.avail_out = 0;
if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED)
- ret = inflateReset(&png_ptr->zstream);
+ {
+# if ZLIB_VERNUM < 0x1240
+ ret = inflateReset(&png_ptr->zstream);
+# else
+ ret = inflateReset2(&png_ptr->zstream, 0/*use stream windowBits*/);
+# endif
+ }
else
{
- ret = inflateInit(&png_ptr->zstream);
+# if ZLIB_VERNUM < 0x1240
+ ret = inflateInit(&png_ptr->zstream);
+# else
+ ret = inflateInit2(&png_ptr->zstream, 0/*use stream windowBits*/);
+# endif
if (ret == Z_OK)
png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;
}
if (ret == Z_OK)
- png_ptr->flags |= PNG_FLAG_ZSTREAM_IN_USE;
+ png_ptr->zowner = owner;
else
- {
- /* A problem; the flags are set ok, but we need a credible error
- * message.
- */
- if (png_ptr->zstream.msg != NULL)
- png_error(png_ptr, png_ptr->zstream.msg);
+ png_zstream_error(png_ptr, ret);
- else
- png_error(png_ptr, "zlib initialization error");
- }
+ return ret;
+ }
+}
+
+/* png_inflate now returns zlib error codes including Z_OK and Z_STREAM_END to
+ * allow the caller to do multiple calls if required. If the 'finish' flag is
+ * set Z_FINISH will be passed to the final inflate() call and Z_STREAM_END must
+ * be returned or there has been a problem, otherwise Z_SYNC_FLUSH is used and
+ * Z_OK or Z_STREAM_END will be returned on success.
+ *
+ * The input and output sizes are updated to the actual amounts of data consumed
+ * or written, not the amount available (as in a z_stream). The data pointers
+ * are not changed, so the next input is (data+input_size) and the next
+ * available output is (output+output_size).
+ */
+static int
+png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish,
+ /* INPUT: */ png_const_bytep input, png_uint_32p input_size_ptr,
+ /* OUTPUT: */ png_bytep output, png_alloc_size_t *output_size_ptr)
+{
+ if (png_ptr->zowner == owner) /* Else not claimed */
+ {
+ int ret;
+ png_alloc_size_t avail_out = *output_size_ptr;
+ png_uint_32 avail_in = *input_size_ptr;
+
+ /* zlib can't necessarily handle more than 65535 bytes at once (i.e. it
+ * can't even necessarily handle 65536 bytes) because the type uInt is
+ * "16 bits or more". Consequently it is necessary to chunk the input to
+ * zlib. This code uses ZLIB_IO_MAX, from pngpriv.h, as the maximum (the
+ * maximum value that can be stored in a uInt.) It is possible to set
+ * ZLIB_IO_MAX to a lower value in pngpriv.h and this may sometimes have
+ * a performance advantage, because it reduces the amount of data accessed
+ * at each step and that may give the OS more time to page it in.
+ */
+ png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input);
+ /* avail_in and avail_out are set below from 'size' */
+ png_ptr->zstream.avail_in = 0;
+ png_ptr->zstream.avail_out = 0;
+
+ /* Read directly into the output if it is available (this is set to
+ * a local buffer below if output is NULL).
+ */
+ if (output != NULL)
+ png_ptr->zstream.next_out = output;
+
+ do
+ {
+ uInt avail;
+ Byte local_buffer[PNG_INFLATE_BUF_SIZE];
+
+ /* zlib INPUT BUFFER */
+ /* The setting of 'avail_in' used to be outside the loop; by setting it
+ * inside it is possible to chunk the input to zlib and simply rely on
+ * zlib to advance the 'next_in' pointer. This allows arbitrary
+ * amounts of data to be passed through zlib at the unavoidable cost of
+ * requiring a window save (png_memcpy of up to 32768 output bytes)
+ * every ZLIB_IO_MAX input bytes.
+ */
+ avail_in += png_ptr->zstream.avail_in; /* not consumed last time */
+
+ avail = ZLIB_IO_MAX;
+
+ if (avail_in < avail)
+ avail = (uInt)avail_in; /* safe: < than ZLIB_IO_MAX */
+
+ avail_in -= avail;
+ png_ptr->zstream.avail_in = avail;
+
+ /* zlib OUTPUT BUFFER */
+ avail_out += png_ptr->zstream.avail_out; /* not written last time */
+
+ avail = ZLIB_IO_MAX; /* maximum zlib can process */
+
+ if (output == NULL)
+ {
+ /* Reset the output buffer each time round if output is NULL and
+ * make available the full buffer, up to 'remaining_space'
+ */
+ png_ptr->zstream.next_out = local_buffer;
+ if (sizeof local_buffer < avail)
+ avail = sizeof local_buffer;
+ }
+
+ if (avail_out < avail)
+ avail = (uInt)avail_out; /* safe: < ZLIB_IO_MAX */
+
+ png_ptr->zstream.avail_out = avail;
+ avail_out -= avail;
+
+ /* zlib inflate call */
+ /* In fact 'avail_out' may be 0 at this point, that happens at the end
+ * of the read when the final LZ end code was not passed at the end of
+ * the previous chunk of input data. Tell zlib if we have reached the
+ * end of the output buffer.
+ */
+ ret = inflate(&png_ptr->zstream, avail_out > 0 ? Z_NO_FLUSH :
+ (finish ? Z_FINISH : Z_SYNC_FLUSH));
+ } while (ret == Z_OK);
+
+ /* For safety kill the local buffer pointer now */
+ if (output == NULL)
+ png_ptr->zstream.next_out = NULL;
+
+ /* Claw back the 'size' and 'remaining_space' byte counts. */
+ avail_in += png_ptr->zstream.avail_in;
+ avail_out += png_ptr->zstream.avail_out;
+
+ /* Update the input and output sizes; the updated values are the amount
+ * consumed or written, effectively the inverse of what zlib uses.
+ */
+ if (avail_out > 0)
+ *output_size_ptr -= avail_out;
+
+ if (avail_in > 0)
+ *input_size_ptr -= avail_in;
+
+ /* Ensure png_ptr->zstream.msg is set (even in the success case!) */
+ png_zstream_error(png_ptr, ret);
+ return ret;
}
else
- png_error(png_ptr, "zstream already in use (internal error)");
-}
-
-#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED
-/* png_inflate: returns one of the following error codes. If output is not NULL
- * the uncompressed data is placed there, up to output_size. output_size is
- * adjusted to the actual amount of uncompressed data. If
- * PNG_INFLATE_TRUNCATED is returned partial output will have been produced; no
- * error or warning is produced in this case.
- */
-#define PNG_INFLATE_OK 1
-#define PNG_INFLATE_TRUNCATED 2 /* output_size will be unchanged */
-#define PNG_INFLATE_ERROR 3 /* a chunk warning has been output */
-
-static void
-stash_error(png_structrp png_ptr, png_const_charp message)
-{
- size_t len = strlen(message);
-
- if (len >= png_ptr->zbuf_size)
- len = png_ptr->zbuf_size-1;
-
- memcpy(png_ptr->zbuf, message, len);
- png_ptr->zbuf[len] = 0;
-}
-
-static int
-png_inflate(png_structrp png_ptr, png_bytep data, png_uint_32 input_size,
- png_bytep output, png_alloc_size_t *output_size)
-{
- int ret;
- png_alloc_size_t avail_out = *output_size;
- png_uint_32 avail_in = input_size;
-
- /* zlib can't necessarily handle more than 65535 bytes at once (i.e. it can't
- * even necessarily handle 65536 bytes) because the type uInt is "16 bits or
- * more". Consequently it is necessary to chunk the input to zlib. This
- * code uses ZLIB_IO_MAX, from pngpriv.h, as the maximum (the maximum value
- * that can be stored in a uInt.) It is possible to set ZLIB_IO_MAX to a
- * lower value in pngpriv.h and this may sometimes have a performance
- * advantage, because it forces access of the input data to be separated from
- * at least some of the use by some period of time.
- */
- png_ptr->zstream.next_in = data;
- /* avail_in and avail_out are set below from 'size' */
- png_ptr->zstream.avail_in = 0;
- png_ptr->zstream.avail_out = 0;
-
- /* Read directly into the output if it is available (this is set to
- * png_ptr->zbuf below if output is NULL).
- */
- if (output != NULL)
- png_ptr->zstream.next_out = output;
-
- do
{
- uInt avail;
-
- /* zlib INPUT BUFFER */
- /* The setting of 'avail_in' used to be outside the loop; by setting it
- * inside it is possible to chunk the input to zlib and simply rely on
- * zlib to advance the 'next_in' pointer. This allows arbitrary amounts o
- * data to be passed through zlib at the unavoidable cost of requiring a
- * window save (png_memcpy of up to 32768 output bytes) every ZLIB_IO_MAX
- * input bytes.
+ /* This is a bad internal error. The recovery assigns to the zstream msg
+ * pointer, which is not owned by the caller, but this is safe; it's only
+ * used on errors!
*/
- avail_in += png_ptr->zstream.avail_in; /* not consumed last time */
-
- avail = ZLIB_IO_MAX;
-
- if (avail_in < avail)
- avail = (uInt)avail_in; /* safe: < than ZLIB_IO_MAX */
-
- avail_in -= avail;
- png_ptr->zstream.avail_in = avail;
-
- /* zlib OUTPUT BUFFER */
- avail_out += png_ptr->zstream.avail_out; /* not written last time */
-
- avail = ZLIB_IO_MAX; /* maximum zlib can process */
-
- if (output == NULL)
- {
- /* Reset the output buffer each time round if output is NULL and make
- * available the full buffer, up to 'remaining_space'
- */
- png_ptr->zstream.next_out = png_ptr->zbuf;
- if (png_ptr->zbuf_size < avail)
- avail = png_ptr->zbuf_size;
- }
-
- if (avail_out < avail)
- avail = (uInt)avail_out; /* safe: < ZLIB_IO_MAX */
-
- png_ptr->zstream.avail_out = avail;
- avail_out -= avail;
-
- /* zlib inflate call */
- /* In fact 'avail_out' may be 0 at this point, that happens at the end of
- * the read when the final LZ end code was not passed at the end of the
- * previous chunk of input data. Tell zlib if we have reached the end of
- * the output buffer.
- *
- * TODO: this prevents multiple calls to inflate, but may help inflate
- * avoid overhead. Make using Z_FINISH an option for the caller.
- */
- ret = inflate(&png_ptr->zstream, avail_out > 0 ? Z_NO_FLUSH : Z_FINISH);
- } while (ret == Z_OK);
-
- /* Claw back the 'size' and 'remaining_space' byte counts. */
- avail_in += png_ptr->zstream.avail_in;
- avail_out += png_ptr->zstream.avail_out;
-
- /* Always reset the zstream, it must be left in inflateInit state.
- *
- * TODO: don't do this on Z_BUF_ERROR and no remaining space, then the
- * caller can use png_inflate incrementally. Also change to using the
- * same approach as in the write code - init and reset only on demand.
- */
- png_ptr->zstream.next_in = NULL;
- png_ptr->zstream.avail_in = 0;
- png_ptr->zstream.next_out = NULL;
- png_ptr->zstream.avail_out = 0;
- inflateReset(&png_ptr->zstream);
-
- /* Update the output size if the output was not filled.
- *
- * TODO: do this with 'size' as well, not currently a parameter.
- */
- if (avail_out > 0)
- *output_size -= avail_out;
-
- /* Error messages are stashed in png_ptr->zbuf, to avoid any errors in the
- * errors do this first:
- */
- png_ptr->zbuf[0] = 0;
-
- switch (ret)
- {
- case Z_STREAM_END:
- png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
- return PNG_INFLATE_OK;
-
- case Z_BUF_ERROR: /* no progress in zlib */
- if (avail_out > 0) /* input truncated */
- {
- stash_error(png_ptr, "LZ data truncated");
- return PNG_INFLATE_ERROR;
- }
-
- else if (avail_in > 0) /* output truncated */
- {
- /* TODO: currently restarting a partial read isn't supporte because
- * of the inflateReset above, change this to do the Reset/Init on
- * demand.
- */
- stash_error(png_ptr, "uncompressed data too large");
- return PNG_INFLATE_TRUNCATED;
- }
-
- default:
- /* Everything else is an error, expect 'Z_DATA_ERROR' and
- * zstream.msg to be non-NULL. In any case just copy something
- * into zbuf and return the error code.
- */
- if (png_ptr->zstream.msg != NULL)
- stash_error(png_ptr, png_ptr->zstream.msg);
-
- else
- stash_error(png_ptr, "LZ data damaged");
-
- return PNG_INFLATE_ERROR;
+ png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed");
+ return Z_STREAM_ERROR;
}
}
+#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED
/*
- * Decompress trailing data in a chunk. The assumption is that chunkdata
+ * Decompress trailing data in a chunk. The assumption is that read_buffer
* 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).
*/
-static png_const_charp
-png_decompress_chunk(png_structrp png_ptr, png_uint_32 chunklength,
- png_uint_32 prefix_size,
+static int
+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*/)
{
@@ -524,8 +553,6 @@
*/
png_alloc_size_t limit = PNG_SIZE_MAX;
- png_inflate_claim(png_ptr);
-
# ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED
if (png_ptr->user_chunk_malloc_max > 0 &&
png_ptr->user_chunk_malloc_max < limit)
@@ -537,17 +564,31 @@
if (limit >= prefix_size + (terminate != 0))
{
+ int ret;
+
limit -= prefix_size + (terminate != 0);
if (limit < *newlength)
*newlength = limit;
- switch (png_inflate(png_ptr,
- (png_bytep)png_ptr->chunkdata + prefix_size,
- chunklength - prefix_size, NULL /* output */, newlength))
+ /* Now try to claim the stream */
+ ret = png_inflate_claim(png_ptr, png_ptr->chunk_name);
+
+ if (ret == Z_OK)
{
- case PNG_INFLATE_OK:
- /* Success (maybe) - really uncompress the chunk. */
+ 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)
+ {
+#if 1
+ if (inflateReset(&png_ptr->zstream) == Z_OK)
+#else
+ if (inflateReset2(&png_ptr->zstream, 0/*from stream*/) == Z_OK)
+#endif
{
/* Because of the limit checks above we know that the new,
* expanded, size will fit in a size_t (let alone an
@@ -555,65 +596,101 @@
* extra OOM message.
*/
png_alloc_size_t new_size = *newlength;
- png_bytep text = png_voidcast(png_bytep, png_malloc_base(
- png_ptr, prefix_size + new_size + (terminate != 0)));
+ 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)
- break; /* out of memory */
-
- /* This should now work, although it is possible a logic
- * error might result.
- */
- switch (png_inflate(png_ptr, (png_bytep)png_ptr->chunkdata +
- prefix_size, chunklength - prefix_size, text + prefix_size,
- newlength))
+ if (text != NULL)
{
- case PNG_INFLATE_OK:
+ 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)
text[prefix_size + *newlength] = 0;
if (prefix_size > 0)
- png_memcpy(text, png_ptr->chunkdata, prefix_size);
+ png_memcpy(text, png_ptr->read_buffer, prefix_size);
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = (png_charp)text;
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
- return NULL;
+ {
+ 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 */
+ }
}
- /* This is an unexpected internal error. */
- /* FALL THROUGH */
+ 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;
+ }
+ }
- case PNG_INFLATE_TRUNCATED:
- png_free(png_ptr, text);
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
- return "libpng inflate error";
+ else if (ret == Z_OK)
+ ret = PNG_UNEXPECTED_ZLIB_RETURN; /* for safety */
- default: /* PNG_INFLATE_ERROR */
- png_free(png_ptr, text);
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
- return (png_const_charp)png_ptr->zbuf;
+ /* 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, Z_MEM_ERROR);
}
}
- case PNG_INFLATE_TRUNCATED:
- /* This means that there wasn't enough space to uncompress the
- * chunk.
- */
- break;
+ else
+ {
+ /* inflateReset failed, store the error message */
+ png_zstream_error(png_ptr, ret);
- default: /* PNG_INFLATE_ERROR */
- /* png_inflate puts the error message in zbuf. */
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
- return (png_const_charp)png_ptr->zbuf;
+ 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;
}
- /* out of memory returns. */
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
- return "insufficient memory";
+ else
+ {
+ /* Application/configuration limits exceeded */
+ png_zstream_error(png_ptr, Z_MEM_ERROR);
+ return Z_MEM_ERROR;
+ }
}
#endif /* PNG_READ_COMPRESSED_TEXT_SUPPORTED */
@@ -1336,6 +1413,7 @@
{
png_uint_32 keyword_length;
png_const_charp error_message = NULL;
+ png_bytep buffer;
png_debug(1, "in png_handle_iCCP");
@@ -1369,28 +1447,22 @@
png_ptr->mode |= PNG_HAVE_iCCP;
- png_free(png_ptr, png_ptr->chunkdata);
/* TODO: read the chunk in pieces, validating it as we go. */
- png_ptr->chunkdata = png_voidcast(png_charp, png_malloc(png_ptr, length));
- png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
+ buffer = png_read_buffer(png_ptr, length, 0/*!warn*/);
+
+ png_crc_read(png_ptr, buffer, length);
if (png_crc_finish(png_ptr, 0))
- {
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
return;
- }
/* TODO: also check that the keyword contents match the spec! */
for (keyword_length = 0;
- keyword_length < length && png_ptr->chunkdata[keyword_length] != 0;
+ keyword_length < length && buffer[keyword_length] != 0;
++keyword_length)
/* Empty loop to find end of name */ ;
if (keyword_length > 79 || keyword_length < 1)
{
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
png_chunk_benign_error(png_ptr, "Bad iCCP keyword");
return;
}
@@ -1401,8 +1473,6 @@
*/
if (keyword_length + 3 >= length)
{
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
png_chunk_benign_error(png_ptr, "iCCP chunk too short");
return;
}
@@ -1410,10 +1480,8 @@
/* We only understand '0' compression - deflate - so if we get a different
* value we can't safely decode the chunk.
*/
- if (png_ptr->chunkdata[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE)
+ if (buffer[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE)
{
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
png_chunk_benign_error(png_ptr, "Bad compression type in iCCP chunk");
return;
}
@@ -1436,17 +1504,15 @@
* level memory limit, this should be split to different values for iCCP
* and text chunks.
*/
- error_message = png_decompress_chunk(png_ptr, length, keyword_length+2,
- &uncompressed_length, 0/*do not terminate*/);
-
- if (error_message == NULL)
+ if (png_decompress_chunk(png_ptr, length, keyword_length+2,
+ &uncompressed_length, 0/*do not terminate*/) == Z_STREAM_END)
{
- /* It worked; png_ptr->chunkdata now contains the full uncompressed ICC
- * profile. The cast below is safe because of the checks above, the
- * final uncompressed length can be at most 2^32-1
+ /* It worked; png_ptr->read_buffer now contains the full uncompressed
+ * ICC profile. The cast below is safe because of the checks above,
+ * the final uncompressed length can be at most 2^32-1
*/
png_uint_32 profile_length = (png_uint_32)uncompressed_length;
- png_bytep profile = (png_bytep)png_ptr->chunkdata + (keyword_length+2);
+ png_bytep profile = png_ptr->read_buffer + (keyword_length+2);
/* Now perform some basic checks.
*
@@ -1454,16 +1520,16 @@
* check that the whole tag table is there.
*/
if (profile_length > 132 && png_get_uint_32(profile) == profile_length)
- png_set_iCCP(png_ptr, info_ptr, png_ptr->chunkdata,
- PNG_COMPRESSION_TYPE_BASE, profile, profile_length);
+ png_set_iCCP(png_ptr, info_ptr, (png_charp)png_ptr->read_buffer,
+ PNG_COMPRESSION_TYPE_BASE, profile, profile_length);
else
error_message = "Malformed ICC profile";
}
- }
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
+ else
+ error_message = png_ptr->zstream.msg;
+ }
if (error_message != NULL)
png_chunk_benign_error(png_ptr, error_message);
@@ -1475,13 +1541,12 @@
png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
/* Note: this does not properly handle chunks that are > 64K under DOS */
{
- png_bytep entry_start;
+ png_bytep entry_start, buffer;
png_sPLT_t new_palette;
png_sPLT_entryp pp;
png_uint_32 data_length;
int entry_size, i;
png_uint_32 skip = 0;
- png_size_t slength;
png_uint_32 dl;
png_size_t max_dl;
@@ -1525,36 +1590,27 @@
}
#endif
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = (png_charp)png_malloc(png_ptr, length + 1);
+ buffer = png_read_buffer(png_ptr, length+1, 0/*!warn*/);
/* WARNING: this may break if size_t is less than 32 bits; it is assumed
* that the PNG_MAX_MALLOC_64K test is enabled in this case, but this is a
* potential breakage point if the types in pngconf.h aren't exactly right.
*/
- png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
+ png_crc_read(png_ptr, buffer, length);
if (png_crc_finish(png_ptr, skip))
- {
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
return;
- }
- slength = length;
- png_ptr->chunkdata[slength] = 0x00;
+ buffer[length] = 0;
- for (entry_start = (png_bytep)png_ptr->chunkdata; *entry_start;
- entry_start++)
+ for (entry_start = buffer; *entry_start; entry_start++)
/* Empty loop to find end of name */ ;
++entry_start;
/* A sample depth should follow the separator, and we should be on it */
- if (entry_start > (png_bytep)png_ptr->chunkdata + slength - 2)
+ if (entry_start > buffer + length - 2)
{
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
png_warning(png_ptr, "malformed sPLT chunk");
return;
}
@@ -1562,17 +1618,13 @@
new_palette.depth = *entry_start++;
entry_size = (new_palette.depth == 8 ? 6 : 10);
/* This must fit in a png_uint_32 because it is derived from the original
- * chunk data length (and use 'length', not 'slength' here for clarity -
- * they are guaranteed to be the same, see the tests above.)
+ * chunk data length.
*/
- data_length = length - (png_uint_32)(entry_start -
- (png_bytep)png_ptr->chunkdata);
+ data_length = length - (png_uint_32)(entry_start - buffer);
/* Integrity-check the data length */
if (data_length % entry_size)
{
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
png_warning(png_ptr, "sPLT chunk has bad length");
return;
}
@@ -1647,12 +1699,10 @@
#endif
/* Discard all chunk data except the name and stash that */
- new_palette.name = png_ptr->chunkdata;
+ new_palette.name = (png_charp)buffer;
png_set_sPLT(png_ptr, info_ptr, &new_palette, 1);
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
png_free(png_ptr, new_palette.entries);
}
#endif /* PNG_READ_sPLT_SUPPORTED */
@@ -2024,7 +2074,7 @@
{
png_int_32 X0, X1;
png_byte type, nparams;
- png_charp buf, units, endptr;
+ png_bytep buffer, buf, units, endptr;
png_charpp params;
int i;
@@ -2049,31 +2099,27 @@
png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)",
length + 1);
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
- if (png_ptr->chunkdata == NULL)
+ buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
+
+ if (buffer == NULL)
{
png_warning(png_ptr, "No memory for pCAL purpose");
return;
}
- png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
+ png_crc_read(png_ptr, buffer, length);
if (png_crc_finish(png_ptr, 0))
- {
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
return;
- }
- png_ptr->chunkdata[length] = 0x00; /* Null terminate the last string */
+ buffer[length] = 0; /* Null terminate the last string */
png_debug(3, "Finding end of pCAL purpose string");
- for (buf = png_ptr->chunkdata; *buf; buf++)
+ for (buf = buffer; *buf; buf++)
/* Empty loop */ ;
- endptr = png_ptr->chunkdata + length;
+ endptr = buffer + length;
/* We need to have at least 12 bytes after the purpose string
* in order to get the parameter information.
@@ -2081,8 +2127,6 @@
if (endptr <= buf + 12)
{
png_warning(png_ptr, "Invalid pCAL data");
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
return;
}
@@ -2103,8 +2147,6 @@
(type == PNG_EQUATION_HYPERBOLIC && nparams != 4))
{
png_warning(png_ptr, "Invalid pCAL parameters for equation type");
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
return;
}
@@ -2118,43 +2160,37 @@
png_debug(3, "Allocating pCAL parameters array");
- params = (png_charpp)png_malloc_warn(png_ptr,
- (png_size_t)(nparams * png_sizeof(png_charp)));
+ params = png_voidcast(png_charpp, png_malloc_warn(png_ptr,
+ nparams * png_sizeof(png_charp)));
if (params == NULL)
{
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
png_warning(png_ptr, "No memory for pCAL params");
return;
}
/* Get pointers to the start of each parameter string. */
- for (i = 0; i < (int)nparams; i++)
+ for (i = 0; i < nparams; i++)
{
buf++; /* Skip the null string terminator from previous parameter. */
png_debug1(3, "Reading pCAL parameter %d", i);
- for (params[i] = buf; buf <= endptr && *buf != 0x00; buf++)
+ for (params[i] = (png_charp)buf; buf <= endptr && *buf != 0; buf++)
/* Empty loop to move past each parameter string */ ;
/* Make sure we haven't run out of data yet */
if (buf > endptr)
{
png_warning(png_ptr, "Invalid pCAL data");
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
png_free(png_ptr, params);
return;
}
}
- png_set_pCAL(png_ptr, info_ptr, png_ptr->chunkdata, X0, X1, type, nparams,
- units, params);
+ png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams,
+ (png_charp)units, params);
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
png_free(png_ptr, params);
}
#endif
@@ -2164,7 +2200,8 @@
void /* PRIVATE */
png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
{
- png_size_t slength, i;
+ png_bytep buffer;
+ png_size_t i;
int state;
png_debug(1, "in png_handle_sCAL");
@@ -2197,32 +2234,25 @@
png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)",
length + 1);
- png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
+ buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
- if (png_ptr->chunkdata == NULL)
+ if (buffer == NULL)
{
png_warning(png_ptr, "Out of memory while processing sCAL chunk");
png_crc_finish(png_ptr, length);
return;
}
- png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
- slength = length;
- png_ptr->chunkdata[slength] = 0x00; /* Null terminate the last string */
+ png_crc_read(png_ptr, buffer, length);
+ buffer[length] = 0; /* Null terminate the last string */
if (png_crc_finish(png_ptr, 0))
- {
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
return;
- }
/* Validate the unit. */
- if (png_ptr->chunkdata[0] != 1 && png_ptr->chunkdata[0] != 2)
+ if (buffer[0] != 1 && buffer[0] != 2)
{
png_warning(png_ptr, "Invalid sCAL ignored: invalid unit");
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
return;
}
@@ -2232,8 +2262,8 @@
i = 1;
state = 0;
- if (!png_check_fp_number(png_ptr->chunkdata, slength, &state, &i) ||
- i >= slength || png_ptr->chunkdata[i++] != 0)
+ if (!png_check_fp_number((png_const_charp)buffer, length, &state, &i) ||
+ i >= length || buffer[i++] != 0)
png_warning(png_ptr, "Invalid sCAL chunk ignored: bad width format");
else if (!PNG_FP_IS_POSITIVE(state))
@@ -2244,8 +2274,8 @@
png_size_t heighti = i;
state = 0;
- if (!png_check_fp_number(png_ptr->chunkdata, slength, &state, &i) ||
- i != slength)
+ if (!png_check_fp_number((png_const_charp)buffer, length, &state, &i) ||
+ i != length)
png_warning(png_ptr, "Invalid sCAL chunk ignored: bad height format");
else if (!PNG_FP_IS_POSITIVE(state))
@@ -2254,13 +2284,9 @@
else
/* This is the (only) success case. */
- png_set_sCAL_s(png_ptr, info_ptr, png_ptr->chunkdata[0],
- png_ptr->chunkdata+1, png_ptr->chunkdata+heighti);
+ png_set_sCAL_s(png_ptr, info_ptr, buffer[0],
+ (png_charp)buffer+1, (png_charp)buffer+heighti);
}
-
- /* Clean up - just free the temporarily allocated buffer. */
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
}
#endif
@@ -2314,11 +2340,11 @@
void /* PRIVATE */
png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
{
- png_textp text_ptr;
+ png_text text_info;
+ png_bytep buffer;
png_charp key;
png_charp text;
png_uint_32 skip = 0;
- int ret;
png_debug(1, "in png_handle_tEXt");
@@ -2355,28 +2381,21 @@
}
#endif
- png_free(png_ptr, png_ptr->chunkdata);
+ buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
- png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
-
- if (png_ptr->chunkdata == NULL)
+ if (buffer == NULL)
{
png_warning(png_ptr, "No memory to process text chunk");
return;
}
- png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
+ png_crc_read(png_ptr, buffer, length);
if (png_crc_finish(png_ptr, skip))
- {
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
return;
- }
- key = png_ptr->chunkdata;
-
- key[length] = 0x00;
+ key = (png_charp)buffer;
+ key[length] = 0;
for (text = key; *text; text++)
/* Empty loop to find end of key */ ;
@@ -2384,32 +2403,15 @@
if (text != key + length)
text++;
- text_ptr = (png_textp)png_malloc_warn(png_ptr,
- png_sizeof(png_text));
+ text_info.compression = PNG_TEXT_COMPRESSION_NONE;
+ text_info.key = key;
+ text_info.lang = NULL;
+ text_info.lang_key = NULL;
+ text_info.itxt_length = 0;
+ text_info.text = text;
+ text_info.text_length = png_strlen(text);
- if (text_ptr == NULL)
- {
- png_warning(png_ptr, "Not enough memory to process text chunk");
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
- return;
- }
-
- text_ptr->compression = PNG_TEXT_COMPRESSION_NONE;
- text_ptr->key = key;
- text_ptr->lang = NULL;
- text_ptr->lang_key = NULL;
- text_ptr->itxt_length = 0;
- text_ptr->text = text;
- text_ptr->text_length = png_strlen(text);
-
- ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
-
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
- png_free(png_ptr, text_ptr);
-
- if (ret)
+ if (png_set_text_2(png_ptr, info_ptr, &text_info, 1))
png_warning(png_ptr, "Insufficient memory to process text chunk");
}
#endif
@@ -2420,7 +2422,8 @@
png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
{
png_const_charp errmsg = NULL;
- png_uint_32 keyword_length;
+ png_bytep buffer;
+ png_uint_32 keyword_length;
png_debug(1, "in png_handle_zTXt");
@@ -2448,28 +2451,22 @@
if (png_ptr->mode & PNG_HAVE_IDAT)
png_ptr->mode |= PNG_AFTER_IDAT;
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = png_voidcast(png_charp, png_malloc_warn(png_ptr,
- length));
+ buffer = png_read_buffer(png_ptr, length, 1/*warn*/);
- if (png_ptr->chunkdata == NULL)
+ if (buffer == NULL)
{
png_chunk_benign_error(png_ptr, "insufficient memory");
return;
}
- png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
+ png_crc_read(png_ptr, buffer, length);
if (png_crc_finish(png_ptr, 0))
- {
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
return;
- }
/* TODO: also check that the keyword contents match the spec! */
for (keyword_length = 0;
- keyword_length < length && png_ptr->chunkdata[keyword_length] != 0;
+ keyword_length < length && buffer[keyword_length] != 0;
++keyword_length)
/* Empty loop to find end of name */ ;
@@ -2483,7 +2480,7 @@
else if (keyword_length + 3 > length)
errmsg = "truncated";
- else if (png_ptr->chunkdata[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE)
+ else if (buffer[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE)
errmsg = "unknown compression type";
else
@@ -2494,22 +2491,21 @@
* level memory limit, this should be split to different values for iCCP
* and text chunks.
*/
- errmsg = png_decompress_chunk(png_ptr, length, keyword_length+2,
- &uncompressed_length, 1/*terminate*/);
-
- if (errmsg == NULL)
+ if (png_decompress_chunk(png_ptr, length, keyword_length+2,
+ &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
{
png_text text;
- /* It worked; png_ptr->chunkdata now looks like a tEXt chunk except for
- * the extra compression type byte and the fact that it isn't
+ /* It worked; png_ptr->read_buffer now looks like a tEXt chunk except
+ * for the extra compression type byte and the fact that it isn't
* necessarily '\0' terminated.
*/
- png_ptr->chunkdata[uncompressed_length+(keyword_length+2)] = 0;
+ buffer = png_ptr->read_buffer;
+ buffer[uncompressed_length+(keyword_length+2)] = 0;
text.compression = PNG_TEXT_COMPRESSION_zTXt;
- text.key = png_ptr->chunkdata;
- text.text = png_ptr->chunkdata + keyword_length+2;
+ text.key = (png_charp)buffer;
+ text.text = (png_charp)(buffer + keyword_length+2);
text.text_length = uncompressed_length;
text.itxt_length = 0;
text.lang = NULL;
@@ -2518,10 +2514,10 @@
if (png_set_text_2(png_ptr, info_ptr, &text, 1))
errmsg = "insufficient memory to store zTXt chunk";
}
- }
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
+ else
+ errmsg = png_ptr->zstream.msg;
+ }
if (errmsg != NULL)
png_chunk_benign_error(png_ptr, errmsg);
@@ -2534,6 +2530,7 @@
png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
{
png_const_charp errmsg = NULL;
+ png_bytep buffer;
png_uint_32 prefix_length;
png_debug(1, "in png_handle_iTXt");
@@ -2562,28 +2559,22 @@
if (png_ptr->mode & PNG_HAVE_IDAT)
png_ptr->mode |= PNG_AFTER_IDAT;
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = png_voidcast(png_charp, png_malloc_warn(png_ptr,
- length+1));
+ buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
- if (png_ptr->chunkdata == NULL)
+ if (buffer == NULL)
{
png_chunk_benign_error(png_ptr, "insufficient memory");
return;
}
- png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
+ png_crc_read(png_ptr, buffer, length);
if (png_crc_finish(png_ptr, 0))
- {
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
return;
- }
/* First the keyword. */
for (prefix_length=0;
- prefix_length < length && png_ptr->chunkdata[prefix_length] != 0;
+ prefix_length < length && buffer[prefix_length] != 0;
++prefix_length)
/* Empty loop */ ;
@@ -2598,11 +2589,11 @@
else if (prefix_length + 5 > length)
errmsg = "truncated";
- else if (png_ptr->chunkdata[prefix_length+1] == 0 ||
- (png_ptr->chunkdata[prefix_length+1] == 1 &&
- png_ptr->chunkdata[prefix_length+2] == PNG_COMPRESSION_TYPE_BASE))
+ else if (buffer[prefix_length+1] == 0 ||
+ (buffer[prefix_length+1] == 1 &&
+ buffer[prefix_length+2] == PNG_COMPRESSION_TYPE_BASE))
{
- int compressed = png_ptr->chunkdata[prefix_length+1] != 0;
+ int compressed = buffer[prefix_length+1] != 0;
png_uint_32 language_offset, translated_keyword_offset;
png_alloc_size_t uncompressed_length = 0;
@@ -2610,14 +2601,14 @@
prefix_length += 3;
language_offset = prefix_length;
- for (; prefix_length < length && png_ptr->chunkdata[prefix_length] != 0;
+ for (; prefix_length < length && buffer[prefix_length] != 0;
++prefix_length)
/* Empty loop */ ;
/* WARNING: the length may be invalid here, this is checked below. */
translated_keyword_offset = ++prefix_length;
- for (; prefix_length < length && png_ptr->chunkdata[prefix_length] != 0;
+ for (; prefix_length < length && buffer[prefix_length] != 0;
++prefix_length)
/* Empty loop */ ;
@@ -2639,8 +2630,12 @@
* level memory limit, this should be split to different values for
* iCCP and text chunks.
*/
- errmsg = png_decompress_chunk(png_ptr, length, prefix_length,
- &uncompressed_length, 1/*terminate*/);
+ if (png_decompress_chunk(png_ptr, length, prefix_length,
+ &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
+ buffer = png_ptr->read_buffer;
+
+ else
+ errmsg = png_ptr->zstream.msg;
}
else
@@ -2650,7 +2645,7 @@
{
png_text text;
- png_ptr->chunkdata[uncompressed_length+prefix_length] = 0;
+ buffer[uncompressed_length+prefix_length] = 0;
if (compressed)
text.compression = PNG_ITXT_COMPRESSION_NONE;
@@ -2658,10 +2653,10 @@
else
text.compression = PNG_ITXT_COMPRESSION_zTXt;
- text.key = png_ptr->chunkdata;
- text.lang = png_ptr->chunkdata + language_offset;
- text.lang_key = png_ptr->chunkdata + translated_keyword_offset;
- text.text = png_ptr->chunkdata + prefix_length;
+ text.key = (png_charp)buffer;
+ text.lang = (png_charp)buffer + language_offset;
+ text.lang_key = (png_charp)buffer + translated_keyword_offset;
+ text.text = (png_charp)buffer + prefix_length;
text.text_length = 0;
text.itxt_length = uncompressed_length;
@@ -2673,9 +2668,6 @@
else
errmsg = "bad compression info";
- png_free(png_ptr, png_ptr->chunkdata);
- png_ptr->chunkdata = NULL;
-
if (errmsg != NULL)
png_chunk_benign_error(png_ptr, errmsg);
}
@@ -3822,6 +3814,176 @@
#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
void /* PRIVATE */
+png_read_IDAT_data(png_structrp png_ptr, png_bytep output,
+ png_alloc_size_t avail_out)
+{
+ /* Loop reading IDATs and decompressing the result into output[avail_out] */
+ png_ptr->zstream.next_out = output;
+ png_ptr->zstream.avail_out = 0; /* safety: set below */
+
+ if (output == NULL)
+ avail_out = 0;
+
+ do
+ {
+ int ret;
+ png_byte tmpbuf[PNG_INFLATE_BUF_SIZE];
+
+ if (png_ptr->zstream.avail_in == 0)
+ {
+ uInt avail_in;
+ png_bytep buffer;
+
+ while (png_ptr->idat_size == 0)
+ {
+ png_crc_finish(png_ptr, 0);
+
+ png_ptr->idat_size = png_read_chunk_header(png_ptr);
+ /* This is an error even in the 'check' case because the code just
+ * consumed a non-IDAT header.
+ */
+ if (png_ptr->chunk_name != png_IDAT)
+ png_error(png_ptr, "Not enough image data");
+ }
+
+ avail_in = png_ptr->IDAT_read_size;
+
+ if (avail_in > png_ptr->idat_size)
+ avail_in = (uInt)png_ptr->idat_size;
+
+ /* A PNG with a gradually increasing IDAT size will defeat this attempt
+ * to minimize memory usage by causing lots of re-allocs, but
+ * realistically doing IDAT_read_size re-allocs is not likely to be a
+ * big problem.
+ */
+ buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/);
+
+ png_crc_read(png_ptr, buffer, avail_in);
+ png_ptr->idat_size -= avail_in;
+
+ png_ptr->zstream.next_in = buffer;
+ png_ptr->zstream.avail_in = avail_in;
+ }
+
+ /* And set up the output side. */
+ if (output != NULL) /* standard read */
+ {
+ uInt out = ZLIB_IO_MAX;
+
+ if (out > avail_out)
+ out = (uInt)avail_out;
+
+ avail_out -= out;
+ png_ptr->zstream.avail_out = out;
+ }
+
+ else /* check for end */
+ {
+ png_ptr->zstream.next_out = tmpbuf;
+ png_ptr->zstream.avail_out = sizeof tmpbuf;
+ }
+
+ /* Use NO_FLUSH; this gives zlib the maximum opportunity to optimize the
+ * process.
+ */
+ ret = inflate(&png_ptr->zstream, Z_NO_FLUSH);
+
+ /* Take the unconsumed output back (so, in the 'check' case this just
+ * counts up).
+ */
+ avail_out += png_ptr->zstream.avail_out;
+ png_ptr->zstream.avail_out = 0;
+
+ if (ret == Z_STREAM_END)
+ {
+ /* Do this for safety; we won't read any more into this row. */
+ png_ptr->zstream.next_out = NULL;
+
+ png_ptr->mode |= PNG_AFTER_IDAT;
+ png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
+
+ if (png_ptr->zstream.avail_in > 0 || png_ptr->idat_size > 0)
+ png_chunk_benign_error(png_ptr, "Extra compressed data");
+ break;
+ }
+
+ if (ret != Z_OK)
+ {
+ png_zstream_error(png_ptr, ret);
+
+ if (output != NULL)
+ png_chunk_error(png_ptr, png_ptr->zstream.msg);
+
+ else /* checking */
+ {
+ png_chunk_benign_error(png_ptr, png_ptr->zstream.msg);
+ return;
+ }
+ }
+ } while (avail_out > 0);
+
+ if (avail_out > 0)
+ {
+ /* The stream ended before the image; this is the same as too few IDATs so
+ * should be handled the same way.
+ */
+ if (output != NULL)
+ png_error(png_ptr, "Not enough image data");
+
+ else /* checking */
+ png_chunk_benign_error(png_ptr, "Too much image data");
+ }
+}
+
+void /* PRIVATE */
+png_read_finish_IDAT(png_structrp png_ptr)
+{
+ /* We don't need any more data and the stream should have ended, however the
+ * LZ end code may actually not have been processed. In this case we must
+ * read it otherwise stray unread IDAT data or, more likely, an IDAT chunk
+ * may still remain to be consumed.
+ */
+ if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED))
+ {
+ /* The NULL causes png_read_IDAT_data to swallow any remaining bytes in
+ * the compressed stream, but the stream may be damaged too, so even after
+ * this call we may need to terminate the zstream ownership.
+ */
+ png_read_IDAT_data(png_ptr, NULL, 0);
+ png_ptr->zstream.next_out = NULL; /* safety */
+
+ /* Now clear everything out for safety; the following may not have been
+ * done.
+ */
+ if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED))
+ {
+ png_ptr->mode |= PNG_AFTER_IDAT;
+ png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
+ }
+ }
+
+ /* If the zstream has not been released do it now *and* terminate the reading
+ * of the final IDAT chunk.
+ */
+ if (png_ptr->zowner == png_IDAT)
+ {
+ /* Always do this; the pointers otherwise point into the read buffer. */
+ png_ptr->zstream.next_in = NULL;
+ png_ptr->zstream.avail_in = 0;
+
+ /* Now we no longer own the zstream. */
+ png_ptr->zowner = 0;
+
+ /* The slightly weird semantics of the sequential IDAT reading is that we
+ * are always in or at the end of an IDAT chunk, so we always need to do a
+ * crc_finish here. If idat_size is non-zero we also need to read the
+ * spurious bytes at the end of the chunk now.
+ */
+ (void)png_crc_finish(png_ptr, png_ptr->idat_size);
+ }
+}
+
+void /* PRIVATE */
png_read_finish_row(png_structrp png_ptr)
{
#ifdef PNG_READ_INTERLACING_SUPPORTED
@@ -3886,66 +4048,7 @@
#endif /* PNG_READ_INTERLACING_SUPPORTED */
/* Here after at the end of the last row of the last pass. */
- if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED))
- {
- Byte extra;
- int ret;
-
- png_ptr->zstream.next_out = &extra;
- png_ptr->zstream.avail_out = 1;
-
- for (;;)
- {
- if (!(png_ptr->zstream.avail_in))
- {
- while (png_ptr->idat_size == 0)
- {
- png_crc_finish(png_ptr, 0);
- png_ptr->idat_size = png_read_chunk_header(png_ptr);
- if (png_ptr->chunk_name != png_IDAT)
- png_error(png_ptr, "Not enough image data");
- }
-
- png_ptr->zstream.avail_in = png_ptr->zbuf_size;
- png_ptr->zstream.next_in = png_ptr->zbuf;
-
- if (png_ptr->zbuf_size > png_ptr->idat_size)
- png_ptr->zstream.avail_in = png_ptr->idat_size;
-
- png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in);
- png_ptr->idat_size -= png_ptr->zstream.avail_in;
- }
-
- ret = inflate(&png_ptr->zstream, Z_NO_FLUSH);
-
- if (ret == Z_STREAM_END)
- {
- if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in ||
- png_ptr->idat_size)
- png_warning(png_ptr, "Extra compressed data");
-
- png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
- break;
- }
-
- if (ret != Z_OK)
- png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg :
- "Decompression Error");
-
- if (!(png_ptr->zstream.avail_out))
- {
- png_warning(png_ptr, "Extra compressed data");
- break;
- }
- }
- png_ptr->zstream.avail_out = 0;
- }
-
- if (png_ptr->idat_size || png_ptr->zstream.avail_in)
- png_warning(png_ptr, "Extra compression data");
-
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
- png_ptr->mode |= PNG_AFTER_IDAT;
+ png_read_finish_IDAT(png_ptr);
}
#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
@@ -4220,8 +4323,22 @@
png_debug1(3, "irowbytes = %lu",
(unsigned long)PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1);
+ /* The sequential reader needs a buffer for IDAT, but the progressive reader
+ * does not, so free the read buffer now regardless; the sequential reader
+ * reallocates it on demand.
+ */
+ if (png_ptr->read_buffer)
+ {
+ png_bytep buffer = png_ptr->read_buffer;
+
+ png_ptr->read_buffer_size = 0;
+ png_ptr->read_buffer = NULL;
+ png_free(png_ptr, buffer);
+ }
+
/* Finally claim the zstream for the inflate of the IDAT data. */
- png_inflate_claim(png_ptr);
+ if (png_inflate_claim(png_ptr, png_IDAT) != Z_OK)
+ png_error(png_ptr, png_ptr->zstream.msg);
png_ptr->flags |= PNG_FLAG_ROW_INIT;
}
diff --git a/pngset.c b/pngset.c
index 4fbac40..882be97 100644
--- a/pngset.c
+++ b/pngset.c
@@ -1231,26 +1231,51 @@
if (png_ptr == NULL)
return;
- png_free(png_ptr, png_ptr->zbuf);
+ if (size == 0 || size > PNG_UINT_31_MAX)
+ png_error(png_ptr, "invalid compression buffer size");
- if (size > ZLIB_IO_MAX)
- {
- png_warning(png_ptr, "Attempt to set buffer size beyond max ignored");
- png_ptr->zbuf_size = ZLIB_IO_MAX;
- size = ZLIB_IO_MAX; /* must fit */
- }
+# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+ if (png_ptr->mode & PNG_IS_READ_STRUCT)
+ {
+ png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */
+ return;
+ }
+# endif
- else
- png_ptr->zbuf_size = (uInt)size;
-
- png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size);
-
- /* The following ensures a relatively safe failure if this gets called while
- * the buffer is actually in use.
- */
- png_ptr->zstream.next_out = png_ptr->zbuf;
- png_ptr->zstream.avail_out = 0;
- png_ptr->zstream.avail_in = 0;
+# ifdef PNG_WRITE_SUPPORTED
+ if (!(png_ptr->mode & PNG_IS_READ_STRUCT))
+ {
+ if (png_ptr->zowner != 0)
+ {
+ png_warning(png_ptr,
+ "Compression buffer size cannot be changed because it is in use");
+ return;
+ }
+
+ if (size > ZLIB_IO_MAX)
+ {
+ png_warning(png_ptr,
+ "Compression buffer size limited to system maximum");
+ size = ZLIB_IO_MAX; /* must fit */
+ }
+
+ else if (size < 6)
+ {
+ /* Deflate will potentially go into an infinite loop on a SYNC_FLUSH
+ * if this is permitted.
+ */
+ png_warning(png_ptr,
+ "Compression buffer size cannot be reduced below 6");
+ return;
+ }
+
+ if (png_ptr->zbuffer_size != size)
+ {
+ png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
+ png_ptr->zbuffer_size = (uInt)size;
+ }
+ }
+# endif
}
void PNGAPI
diff --git a/pngstruct.h b/pngstruct.h
index fb29b54..88a5dda 100644
--- a/pngstruct.h
+++ b/pngstruct.h
@@ -24,7 +24,50 @@
* in this structure and is required for decompressing the LZ compressed
* data in PNG files.
*/
+#ifndef ZLIB_CONST
+ /* We must ensure that zlib uses 'const' in declarations. */
+# define ZLIB_CONST
+#endif
#include "zlib.h"
+#ifdef const
+ /* zlib.h sometimes #defines const to nothing, undo this. */
+# undef const
+#endif
+
+/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility
+ * with older builds.
+ */
+#if ZLIB_VERNUM < 0x1260
+# define PNGZ_MSG_CAST(s) png_constcast(char*,s)
+# define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b)
+#else
+# define PNGZ_MSG_CAST(s) (s)
+# define PNGZ_INPUT_CAST(b) (b)
+#endif
+
+/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib
+ * can handle at once. This type need be no larger than 16 bits (so maximum of
+ * 65535), this define allows us to discover how big it is, but limited by the
+ * maximuum for png_size_t. The value can be overriden in a library build
+ * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably
+ * lower value (e.g. 255 works). A lower value may help memory usage (slightly)
+ * and may even improve performance on some systems (and degrade it on others.)
+ */
+#ifndef ZLIB_IO_MAX
+# define ZLIB_IO_MAX ((uInt)-1)
+#endif
+
+#ifdef PNG_WRITE_SUPPORTED
+/* The type of a compression buffer list used by the write code. */
+typedef struct png_compression_buffer
+{
+ struct png_compression_buffer *next;
+ png_byte output[1]; /* actually zbuf_size */
+} png_compression_buffer, *png_compression_bufferp;
+
+#define PNG_COMPRESSION_BUFFER_SIZE(pp)\
+ (offsetof(png_compression_buffer, output) + (pp)->zbuffer_size)
+#endif
struct png_struct_def
{
@@ -65,11 +108,13 @@
png_uint_32 flags; /* flags indicating various things to libpng */
png_uint_32 transformations; /* which transformations to perform */
- z_stream zstream; /* decompression structure */
- png_bytep zbuf; /* buffer for zlib */
- uInt zbuf_size; /* size of zbuf */
+ png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */
+ z_stream zstream; /* decompression structure */
#ifdef PNG_WRITE_SUPPORTED
+ png_compression_bufferp zbuffer_list; /* Created on demand during write */
+ uInt zbuffer_size; /* size of the actual buffer */
+
int zlib_level; /* holds zlib compression level */
int zlib_method; /* holds zlib compression method */
int zlib_window_bits; /* holds zlib compression window bits */
@@ -341,8 +386,14 @@
/* New member added in libpng-1.2.26 */
png_size_t old_big_row_buf_size;
+#ifdef PNG_READ_SUPPORTED
/* New member added in libpng-1.2.30 */
- png_charp chunkdata; /* buffer for reading chunk data */
+ png_bytep read_buffer; /* buffer for reading chunk data */
+ png_alloc_size_t read_buffer_size; /* current size of the buffer */
+#endif
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+ uInt IDAT_read_size; /* limit on read buffer size for IDAT */
+#endif
#ifdef PNG_IO_STATE_SUPPORTED
/* New member added in libpng-1.4.0 */
diff --git a/pngwrite.c b/pngwrite.c
index 7fb8577..4b10b04 100644
--- a/pngwrite.c
+++ b/pngwrite.c
@@ -475,6 +475,8 @@
/* Set the zlib control values to defaults; they can be overridden by the
* application after the struct has been created.
*/
+ png_ptr->zbuffer_size = PNG_ZBUF_SIZE;
+
png_ptr->zlib_strategy = Z_FILTERED; /* may be overridden if no filters */
png_ptr->zlib_level = Z_DEFAULT_COMPRESSION;
png_ptr->zlib_mem_level = 8;
@@ -784,8 +786,6 @@
void PNGAPI
png_write_flush(png_structrp png_ptr)
{
- int wrote_IDAT;
-
png_debug(1, "in png_write_flush");
if (png_ptr == NULL)
@@ -795,39 +795,7 @@
if (png_ptr->row_number >= png_ptr->num_rows)
return;
- do
- {
- int ret;
-
- /* Compress the data */
- ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
- wrote_IDAT = 0;
-
- /* Check for compression errors */
- if (ret != Z_OK)
- {
- if (png_ptr->zstream.msg != NULL)
- png_error(png_ptr, png_ptr->zstream.msg);
-
- else
- png_error(png_ptr, "zlib error");
- }
-
- if (!(png_ptr->zstream.avail_out))
- {
- /* Write the IDAT and reset the zlib output buffer */
- png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
- wrote_IDAT = 1;
- }
- } while (wrote_IDAT == 1);
-
- /* If there is any data left to be output, write it into a new IDAT */
- if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
- {
- /* Write the IDAT and reset the zlib output buffer */
- png_write_IDAT(png_ptr, png_ptr->zbuf,
- png_ptr->zbuf_size - png_ptr->zstream.avail_out);
- }
+ png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH);
png_ptr->flush_rows = 0;
png_flush(png_ptr);
}
@@ -848,7 +816,7 @@
deflateEnd(&png_ptr->zstream);
/* Free our memory. png_free checks NULL for us. */
- png_free(png_ptr, png_ptr->zbuf);
+ png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
png_free(png_ptr, png_ptr->row_buf);
#ifdef PNG_WRITE_FILTER_SUPPORTED
png_free(png_ptr, png_ptr->prev_row);
diff --git a/pngwutil.c b/pngwutil.c
index d4a50f6..44a8413 100644
--- a/pngwutil.c
+++ b/pngwutil.c
@@ -197,7 +197,7 @@
return;
/* On 64 bit architectures 'length' may not fit in a png_uint_32. */
- if (length > PNG_UINT_32_MAX)
+ if (length > PNG_UINT_31_MAX)
png_error(png_ptr, "length exceeds PNG maxima");
png_write_chunk_header(png_ptr, chunk_name, (png_uint_32)length);
@@ -257,12 +257,90 @@
return 0xffffffffU;
}
-/* Initialize the compressor for the appropriate type of compression. */
+#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+ /* This is the code to hack the first two bytes of the deflate stream (the
+ * deflate header) to correct the windowBits value to match the actual data
+ * size. Note that the second argument is the *uncompressed* size but the
+ * first argument is the *compressed* data (and it must be deflate
+ * compressed.)
+ */
static void
-png_deflate_claim(png_structrp png_ptr, int for_IDAT,
+optimize_cmf(png_bytep data, png_alloc_size_t data_size)
+{
+ /* Optimize the CMF field in the zlib stream. The resultant zlib stream is
+ * still compliant to the stream specification.
+ */
+ if (data_size <= 16384) /* else windowBits must be 15 */
+ {
+ unsigned int z_cmf = data[0]; /* zlib compression method and flags */
+
+ if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
+ {
+ unsigned int z_cinfo;
+ unsigned int half_z_window_size;
+
+ z_cinfo = z_cmf >> 4;
+ half_z_window_size = 1U << (z_cinfo + 7);
+
+ if (data_size <= half_z_window_size) /* else no change */
+ {
+ unsigned int tmp;
+
+ do
+ {
+ half_z_window_size >>= 1;
+ --z_cinfo;
+ }
+ while (z_cinfo > 0 && data_size <= half_z_window_size);
+
+ z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
+
+ data[0] = (png_byte)z_cmf;
+ tmp = data[1] & 0xe0;
+ tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
+ data[1] = (png_byte)tmp;
+ }
+ }
+ }
+}
+#else
+# define optimize_cmf(dp,dl) ((void)0)
+#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */
+
+/* Initialize the compressor for the appropriate type of compression. */
+static int
+png_deflate_claim(png_structrp png_ptr, png_uint_32 owner,
png_alloc_size_t data_size)
{
- if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_IN_USE))
+ if (png_ptr->zowner != 0)
+ {
+ char msg[64];
+
+ PNG_STRING_FROM_CHUNK(msg, owner);
+ msg[4] = ':';
+ msg[5] = ' ';
+ PNG_STRING_FROM_CHUNK(msg+6, png_ptr->zowner);
+ /* So the message that results is "<chunk> using zstream"; this is an
+ * internal error, but is very useful for debugging. i18n requirements
+ * are minimal.
+ */
+ (void)png_safecat(msg, sizeof msg, 10, " using zstream");
+# if PNG_LIBPNG_BUILD_BASE_TYPE == PNG_LIBPNG_BUILD_STABLE
+ png_warning(png_ptr, msg);
+
+ /* Attempt sane error recovery */
+ if (png_ptr->zowner == png_IDAT) /* don't steal from IDAT */
+ {
+ png_ptr->zstream.msg = PNGZ_MSG_CAST("in use by IDAT");
+ return Z_STREAM_ERROR;
+ }
+
+ png_ptr->zowner = 0;
+# else
+ png_error(png_ptr, msg);
+# endif
+ }
+
{
int level = png_ptr->zlib_level;
int method = png_ptr->zlib_method;
@@ -271,10 +349,7 @@
int strategy; /* set below */
int ret; /* zlib return code */
- /* Do this for safety now. */
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_ENDED;
-
- if (for_IDAT)
+ if (owner == png_IDAT)
{
if (png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)
strategy = png_ptr->zlib_strategy;
@@ -311,29 +386,20 @@
* next windowBits size we need to fix up the value later. (Because even
* though deflate needs the extra window, inflate does not!)
*/
- if (data_size <= 256)
- windowBits = 8;
-
- else if (data_size <= 16384) /* Else 15 bits required */ for (;;)
+ if (data_size <= 16384)
{
- png_uint_32 test = 1U << (windowBits-1);
- if (data_size > test)
- break;
- --windowBits;
- }
+ /* IMPLEMENTATION NOTE: this 'half_window_size' stuff is only here to
+ * work round a Microsoft Visual C misbehavior which, contrary to C-90,
+ * widens the result of the following shift to 64-bits if (and,
+ * apparently, only if) it is used in a test.
+ */
+ unsigned int half_window_size = 1U << (windowBits-1);
- if (windowBits < 15)
- {
- png_uint_32 test = 1U << windowBits;
-
- if (data_size + 262U > test)
+ while (data_size + 262 <= half_window_size)
{
- ++windowBits;
- png_ptr->flags |= PNG_FLAG_ZSTREAM_CMF_FIXUP;
+ half_window_size >>= 1;
+ --windowBits;
}
-
- else
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_CMF_FIXUP;
}
/* Check against the previous initialized values, if any. */
@@ -350,6 +416,14 @@
png_ptr->flags &= ~PNG_FLAG_ZSTREAM_INITIALIZED;
}
+ /* For safety clear out the input and output pointers (currently zlib
+ * doesn't use them on Init, but it might in the future).
+ */
+ png_ptr->zstream.next_in = NULL;
+ png_ptr->zstream.avail_in = 0;
+ png_ptr->zstream.next_out = NULL;
+ png_ptr->zstream.avail_out = 0;
+
/* Now initialize if required, setting the new parameters, otherwise just
* to a simple reset to the previous parameters.
*/
@@ -369,357 +443,325 @@
* pretty much the same set of error codes.
*/
if (ret == Z_OK)
- {
- /* No problem, so the stream is now 'in use'. */
- png_ptr->flags |= PNG_FLAG_ZSTREAM_IN_USE;
- }
+ png_ptr->zowner = owner;
else
- {
- /* A problem; the flags are set ok, but we need a credible error
- * message.
- */
- if (png_ptr->zstream.msg != NULL)
- png_error(png_ptr, png_ptr->zstream.msg);
+ png_zstream_error(png_ptr, ret);
- else
- png_error(png_ptr, "zlib initialization error");
- }
+ return ret;
}
-
- else
- png_error(png_ptr, "zstream already in use (internal error)");
-}
-
-/* The opposite: release the stream. It is also reset, this API will warn on
- * error but will not fail.
- */
-static void
-png_deflate_release(png_structrp png_ptr)
-{
- if (png_ptr->flags & PNG_FLAG_ZSTREAM_IN_USE)
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
-
- else
- png_warning(png_ptr, "zstream not in use (internal error)");
}
#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
/* This pair of functions encapsulates the operation of (a) compressing a
* text string, and (b) issuing it later as a series of chunk data writes.
* The compression_state structure is shared context for these functions
- * set up by the caller in order to make the whole mess thread-safe.
+ * set up by the caller to allow access to the relevant local variables.
+ *
+ * compression_buffer (new in 1.6.0) is just a linked list of zbuffer_size
+ * temporary buffers. From 1.6.0 it is retained in png_struct so that it will
+ * be correctly freed in the event of a write error (previous implementations
+ * just leaked memory.)
*/
-
typedef struct
{
- png_const_bytep input; /* The uncompressed input data */
- png_size_t input_len; /* Its length */
- int num_output_ptr; /* Number of output pointers used */
- int max_output_ptr; /* Size of output_ptr */
- png_bytep *output_ptr; /* Array of pointers to output */
+ png_const_bytep input; /* The uncompressed input data */
+ png_alloc_size_t input_len; /* Its length */
+ png_uint_32 output_len; /* Final compressed length */
+ png_byte output[1024]; /* First block of output */
} compression_state;
-/* Compress given text into storage in the png_ptr structure */
-static png_size_t /* PRIVATE */
-png_text_compress(png_structrp png_ptr,
- png_const_charp text, png_size_t text_len, int compression,
- compression_state *comp)
+static void
+png_text_compress_init(compression_state *comp, png_const_bytep input,
+ png_alloc_size_t input_len)
+{
+ comp->input = input;
+ comp->input_len = input_len;
+ comp->output_len = 0;
+}
+
+void /* PRIVATE */
+png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp)
+{
+ png_compression_bufferp list = *listp;
+
+ if (list != NULL)
+ {
+ *listp = NULL;
+
+ do
+ {
+ png_compression_bufferp next = list->next;
+
+ png_free(png_ptr, list);
+ list = next;
+ }
+ while (list != NULL);
+ }
+}
+
+/* Compress the data in the compression state input */
+static int
+png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name,
+ compression_state *comp, png_uint_32 prefix_len)
{
int ret;
- comp->num_output_ptr = 0;
- comp->max_output_ptr = 0;
- comp->output_ptr = NULL;
- comp->input = NULL;
- comp->input_len = text_len;
-
- /* We may just want to pass the text right through */
- if (compression == PNG_TEXT_COMPRESSION_NONE)
- {
- comp->input = (png_const_bytep)text;
- return text_len;
- }
-
- if (compression >= PNG_TEXT_COMPRESSION_LAST)
- {
- PNG_WARNING_PARAMETERS(p)
-
- png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_d,
- compression);
- png_formatted_warning(png_ptr, p, "Unknown compression type @1");
- }
-
- /* We can't write the chunk until we find out how much data we have,
- * which means we need to run the compressor first and save the
- * output. This shouldn't be a problem, as the vast majority of
- * comments should be reasonable, but we will set up an array of
- * malloc'd pointers to be sure.
+ /* To find the length of the output it is necessary to first compress the
+ * input, the result is buffered rather than using the two-pass algorithm
+ * that is used on the inflate side; deflate is assumed to be slower and a
+ * PNG writer is assumed to have more memory available than a PNG reader.
*
- * If we knew the application was well behaved, we could simplify this
- * greatly by assuming we can always malloc an output buffer large
- * enough to hold the compressed text ((1001 * text_len / 1000) + 12)
- * and malloc this directly. The only time this would be a bad idea is
- * if we can't malloc more than 64K and we have 64K of random input
- * data, or if the input string is incredibly large (although this
- * wouldn't cause a failure, just a slowdown due to swapping).
+ * IMPLEMENTATION NOTE: the zlib API deflateBound() can be used to find an
+ * upper limit on the output size, but it is always bigger than the input
+ * size so it is likely to be more efficient to use this linked-list
+ * approach.
*/
- png_deflate_claim(png_ptr, 0/*!for IDAT*/, text_len);
+ ret = png_deflate_claim(png_ptr, chunk_name, comp->input_len);
- /* Set up the compression buffers */
- /* TODO: the following cast hides a ****CERTAIN**** overflow problem. */
- png_ptr->zstream.avail_in = (uInt)text_len;
+ if (ret != Z_OK)
+ return ret;
- /* NOTE: assume zlib doesn't overwrite the input */
- png_ptr->zstream.next_in = (Bytef *)text;
- png_ptr->zstream.avail_out = png_ptr->zbuf_size;
- png_ptr->zstream.next_out = png_ptr->zbuf;
-
- /* This is the same compression loop as in png_write_row() */
- do
+ /* Set up the compression buffers, we need a loop here to avoid overflowing a
+ * uInt. Use ZLIB_IO_MAX to limit the input. The output is always limited
+ * by the output buffer size, so there is no need to check that. Since this
+ * is ANSI-C we know that an 'int', hence a uInt, is always at least 16 bits
+ * in size.
+ */
{
- /* Compress the data */
- ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
+ png_compression_bufferp *end = &png_ptr->zbuffer_list;
+ png_alloc_size_t input_len = comp->input_len; /* may be zero! */
+ png_uint_32 output_len;
- if (ret != Z_OK)
+ /* zlib updates these for us: */
+ png_ptr->zstream.next_in = PNGZ_INPUT_CAST(comp->input);
+ png_ptr->zstream.avail_in = 0; /* Set below */
+ png_ptr->zstream.next_out = comp->output;
+ png_ptr->zstream.avail_out = sizeof comp->output;
+
+ output_len = png_ptr->zstream.avail_out;
+
+ do
{
- /* Error */
- if (png_ptr->zstream.msg != NULL)
- png_error(png_ptr, png_ptr->zstream.msg);
+ uInt avail_in = ZLIB_IO_MAX;
- else
- png_error(png_ptr, "zlib error");
- }
+ if (avail_in > input_len)
+ avail_in = (uInt)input_len;
- /* Check to see if we need more room */
- if (!(png_ptr->zstream.avail_out))
- {
- /* Make sure the output array has room */
- if (comp->num_output_ptr >= comp->max_output_ptr)
+ input_len -= avail_in;
+
+ png_ptr->zstream.avail_in = avail_in;
+
+ if (png_ptr->zstream.avail_out == 0)
{
- int old_max;
+ png_compression_buffer *next;
- old_max = comp->max_output_ptr;
- comp->max_output_ptr = comp->num_output_ptr + 4;
- if (comp->output_ptr != NULL)
+ /* Chunk data is limited to 2^31 bytes in length, so the prefix
+ * length must be counted here.
+ */
+ if (output_len + prefix_len > PNG_UINT_31_MAX)
{
- png_bytepp old_ptr;
-
- old_ptr = comp->output_ptr;
-
- comp->output_ptr = (png_bytepp)png_malloc(png_ptr,
- (png_alloc_size_t)
- (comp->max_output_ptr * png_sizeof(png_charpp)));
-
- png_memcpy(comp->output_ptr, old_ptr, old_max
- * png_sizeof(png_charp));
-
- png_free(png_ptr, old_ptr);
+ ret = Z_MEM_ERROR;
+ break;
}
- else
- comp->output_ptr = (png_bytepp)png_malloc(png_ptr,
- (png_alloc_size_t)
- (comp->max_output_ptr * png_sizeof(png_charp)));
- }
- /* Save the data */
- comp->output_ptr[comp->num_output_ptr] =
- (png_bytep)png_malloc(png_ptr,
- (png_alloc_size_t)png_ptr->zbuf_size);
-
- png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf,
- png_ptr->zbuf_size);
-
- comp->num_output_ptr++;
-
- /* and reset the buffer */
- png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
- png_ptr->zstream.next_out = png_ptr->zbuf;
- }
- /* Continue until we don't have any more to compress */
- } while (png_ptr->zstream.avail_in);
-
- /* Finish the compression */
- do
- {
- /* Tell zlib we are finished */
- ret = deflate(&png_ptr->zstream, Z_FINISH);
-
- if (ret == Z_OK)
- {
- /* Check to see if we need more room */
- if (!(png_ptr->zstream.avail_out))
- {
- /* Check to make sure our output array has room */
- if (comp->num_output_ptr >= comp->max_output_ptr)
+ /* Need a new (malloc'ed) buffer, but there may be one present
+ * already.
+ */
+ next = *end;
+ if (next == NULL)
{
- int old_max;
+ next = png_voidcast(png_compression_bufferp, png_malloc_base
+ (png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
- old_max = comp->max_output_ptr;
- comp->max_output_ptr = comp->num_output_ptr + 4;
- if (comp->output_ptr != NULL)
+ if (next == NULL)
{
- png_bytepp old_ptr;
-
- old_ptr = comp->output_ptr;
-
- /* This could be optimized to realloc() */
- comp->output_ptr = png_voidcast(png_bytepp,png_malloc(png_ptr,
- comp->max_output_ptr * png_sizeof(png_charp)));
-
- png_memcpy(comp->output_ptr, old_ptr,
- old_max * png_sizeof(png_charp));
-
- png_free(png_ptr, old_ptr);
+ ret = Z_MEM_ERROR;
+ break;
}
- else
- comp->output_ptr = png_voidcast(png_bytepp,png_malloc(png_ptr,
- comp->max_output_ptr * png_sizeof(png_charp)));
+ /* Link in this buffer (so that it will be freed later) */
+ next->next = NULL;
+ *end = next;
}
- /* Save the data */
- comp->output_ptr[comp->num_output_ptr] = png_voidcast(png_bytep,
- png_malloc(png_ptr, png_ptr->zbuf_size));
+ png_ptr->zstream.next_out = next->output;
+ png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
+ output_len += png_ptr->zstream.avail_out;
- png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf,
- png_ptr->zbuf_size);
-
- comp->num_output_ptr++;
-
- /* and reset the buffer pointers */
- png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
- png_ptr->zstream.next_out = png_ptr->zbuf;
+ /* Move 'end' to the next buffer pointer. */
+ end = &next->next;
}
+
+ /* Compress the data */
+ ret = deflate(&png_ptr->zstream,
+ input_len > 0 ? Z_NO_FLUSH : Z_FINISH);
+
+ /* Claw back input data that was not consumed (because avail_in is
+ * reset above every time round the loop).
+ */
+ input_len += png_ptr->zstream.avail_in;
+ png_ptr->zstream.avail_in = 0; /* safety */
}
- else if (ret != Z_STREAM_END)
+ while (ret == Z_OK);
+
+ /* There may be some space left in the last output buffer, this needs to
+ * be subtracted from output_len.
+ */
+ output_len -= png_ptr->zstream.avail_out;
+ png_ptr->zstream.avail_out = 0; /* safety */
+ comp->output_len = output_len;
+
+ /* Now double check the output length, put in a custom message if it is
+ * too long. Otherwise ensure the z_stream::msg pointer is set to
+ * something.
+ */
+ if (output_len + prefix_len >= PNG_UINT_31_MAX)
{
- /* We got an error */
- if (png_ptr->zstream.msg != NULL)
- png_error(png_ptr, png_ptr->zstream.msg);
-
- else
- png_error(png_ptr, "zlib error");
+ png_ptr->zstream.msg = PNGZ_MSG_CAST("compressed data too long");
+ ret = Z_MEM_ERROR;
}
- } while (ret != Z_STREAM_END);
- /* Text length is number of buffers plus last buffer */
- /* TODO: this ****WILL**** overflow */
- text_len = png_ptr->zbuf_size * comp->num_output_ptr;
+ else
+ png_zstream_error(png_ptr, ret);
- if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
- text_len += png_ptr->zbuf_size - png_ptr->zstream.avail_out;
+ /* Reset zlib for another zTXt/iTXt or image data */
+ png_ptr->zowner = 0;
- return text_len;
+ /* The only success case is Z_STREAM_END, input_len must be 0, if not this
+ * is an internal error.
+ */
+ if (ret == Z_STREAM_END && input_len == 0)
+ {
+ /* Fix up the deflate header, if required */
+ optimize_cmf(comp->output, comp->input_len);
+
+ /* But Z_OK is returned, not Z_STREAM_END; this allows the claim
+ * function above to return Z_STREAM_END on an error (though it never
+ * does in the current versions of zlib.)
+ */
+ return Z_OK;
+ }
+
+ else
+ return ret;
+ }
}
/* Ship the compressed text out via chunk writes */
-static void /* PRIVATE */
+static void
png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp)
{
- int i;
+ png_uint_32 output_len = comp->output_len;
+ png_const_bytep output = comp->output;
+ png_uint_32 avail = sizeof comp->output;
+ png_compression_buffer *next = png_ptr->zbuffer_list;
- /* Handle the no-compression case */
- if (comp->input)
+ for (;;)
{
- png_write_chunk_data(png_ptr, comp->input, comp->input_len);
+ if (avail > output_len)
+ avail = output_len;
- return;
+ png_write_chunk_data(png_ptr, output, avail);
+
+ output_len -= avail;
+
+ if (output_len == 0 || next == NULL)
+ break;
+
+ avail = png_ptr->zbuffer_size;
+ output = next->output;
+ next = next->next;
}
-#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
- /* The zbuf_size test is because the code below doesn't work if zbuf_size is
- * '1'; simply skip it to avoid memory overwrite.
- */
- if (comp->input_len >= 2 && comp->input_len < 16384 && png_ptr->zbuf_size > 1)
- {
- unsigned int z_cmf; /* zlib compression method and flags */
-
- /* Optimize the CMF field in the zlib stream. This hack of the zlib
- * stream is compliant to the stream specification.
- */
-
- if (comp->num_output_ptr)
- z_cmf = comp->output_ptr[0][0];
- else
- z_cmf = png_ptr->zbuf[0];
-
- if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
- {
- unsigned int z_cinfo;
- unsigned int half_z_window_size;
- int reduced = 0;
- png_size_t uncompressed_text_size = comp->input_len;
-
- z_cinfo = z_cmf >> 4;
- half_z_window_size = 1 << (z_cinfo + 7);
-
- while (uncompressed_text_size <= half_z_window_size &&
- half_z_window_size >= 256)
- {
- z_cinfo--;
- half_z_window_size >>= 1;
- reduced = 1;
- }
-
- if (reduced && !(png_ptr->flags & PNG_FLAG_ZSTREAM_CMF_FIXUP))
- png_warning(png_ptr, "internal data size error (text)");
-
- z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
-
- if (comp->num_output_ptr)
- {
-
- if (comp->output_ptr[0][0] != z_cmf)
- {
- int tmp;
-
- comp->output_ptr[0][0] = (png_byte)z_cmf;
- tmp = comp->output_ptr[0][1] & 0xe0;
- tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
- comp->output_ptr[0][1] = (png_byte)tmp;
- }
- }
- else
- {
- int tmp;
-
- png_ptr->zbuf[0] = (png_byte)z_cmf;
- tmp = png_ptr->zbuf[1] & 0xe0;
- tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
- png_ptr->zbuf[1] = (png_byte)tmp;
- }
- }
-
- else
- png_warning(png_ptr, /* must be a warning or the data isn't freed */
- "Invalid zlib compression method or flags in non-IDAT chunk");
- }
-#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */
-
- /* Write saved output buffers, if any */
- /* WARNING: SIDE EFFECT; the code below is what actually frees the data */
- for (i = 0; i < comp->num_output_ptr; i++)
- {
- png_write_chunk_data(png_ptr, comp->output_ptr[i],
- (png_size_t)png_ptr->zbuf_size);
-
- png_free(png_ptr, comp->output_ptr[i]);
- }
-
- if (comp->max_output_ptr != 0)
- png_free(png_ptr, comp->output_ptr);
-
- /* Write anything left in zbuf */
- if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size)
- png_write_chunk_data(png_ptr, png_ptr->zbuf,
- (png_size_t)(png_ptr->zbuf_size - png_ptr->zstream.avail_out));
-
- /* Reset zlib for another zTXt/iTXt or image data */
- png_deflate_release(png_ptr);
+ /* This is an internal error; 'next' must have been NULL! */
+ if (output_len > 0)
+ png_error(png_ptr, "error writing ancilliary chunked compressed data");
}
#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
+ defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
+/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification,
+ * and if invalid, correct the keyword rather than discarding the entire
+ * chunk. The PNG 1.0 specification requires keywords 1-79 characters in
+ * length, forbids leading or trailing whitespace, multiple internal spaces,
+ * and the non-break space (0x80) from ISO 8859-1. Returns keyword length.
+ *
+ * The 'new_key' buffer must be 80 characters in size (for the keyword plus a
+ * trailing '\0'). If this routine returns 0 then there was no keyword, or a
+ * valid one could not be generated, and the caller must png_error.
+ */
+static png_uint_32
+png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key)
+{
+ png_const_charp orig_key = key;
+ png_uint_32 key_len = 0;
+ int bad_character = 0;
+ int space = 1;
+
+ png_debug(1, "in png_check_keyword");
+
+ if (key == NULL)
+ {
+ *new_key = 0;
+ return 0;
+ }
+
+ while (*key && key_len < 79)
+ {
+ png_byte ch = (png_byte)(0xff & *key++);
+
+ if ((ch > 32 && ch <= 126) || (ch >= 161 /*&& ch <= 255*/))
+ *new_key++ = ch, ++key_len, space = 0;
+
+ else if (!space)
+ {
+ /* A space or an invalid character when one wasn't seen immediately
+ * before; output just a space.
+ */
+ *new_key++ = 32, ++key_len, space = 1;
+
+ /* If the character was not a space then it is inalid. */
+ if (ch != 32)
+ bad_character = ch;
+ }
+
+ else if (!bad_character)
+ bad_character = ch; /* just skip it, record the first error */
+ }
+
+ if (key_len > 0 && space) /* trailing space */
+ {
+ --key_len, --new_key;
+ if (!bad_character)
+ bad_character = 32;
+ }
+
+ /* Terminate the keyword */
+ *new_key = 0;
+
+ if (key_len == 0)
+ return 0;
+
+ /* Try to only output one warning per keyword: */
+ if (*key) /* keyword too long */
+ png_warning(png_ptr, "keyword truncated");
+
+ else if (bad_character)
+ {
+ PNG_WARNING_PARAMETERS(p)
+
+ png_warning_parameter(p, 1, orig_key);
+ png_warning_parameter_signed(p, 2, PNG_NUMBER_FORMAT_02x, bad_character);
+
+ png_formatted_warning(png_ptr, p, "keyword \"@1\": bad character '0x@2'");
+ }
+
+ return key_len;
+}
+#endif
+
/* Write the IHDR chunk, and update the png_struct with the necessary
* information. Note that the rest of this code depends upon this
* information being correct.
@@ -958,78 +1000,158 @@
png_ptr->mode |= PNG_HAVE_PLTE;
}
-/* Write an IDAT chunk */
+/* This is similar to png_text_compress, above, except that it does not require
+ * all of the data at once and, instead of buffering the compressed result,
+ * writes it as IDAT chunks. Unlike png_text_compress it *can* png_error out
+ * because it calls the write interface. As a result it does its own error
+ * reporting and does not return an error code. In the event of error it will
+ * just call png_error. The input data length may exceed 32-bits. The 'flush'
+ * parameter is exactly the same as that to deflate, with the following
+ * meanings:
+ *
+ * Z_NO_FLUSH: normal incremental output of compressed data
+ * Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush
+ * Z_FINISH: this is the end of the input, do a Z_FINISH and clean up
+ *
+ * The routine manages the acquire and release of the png_ptr->zstream by
+ * checking and (at the end) clearing png_ptr->zowner, it does some sanity
+ * checks on the 'mode' flags while doing this.
+ */
void /* PRIVATE */
-png_write_IDAT(png_structrp png_ptr, png_bytep data, png_size_t length)
+png_compress_IDAT(png_structrp png_ptr, png_const_bytep input,
+ png_alloc_size_t input_len, int flush)
{
- png_debug(1, "in png_write_IDAT");
-
-#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
- if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
- png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
+ if (png_ptr->zowner != png_IDAT)
{
- /* Optimize the CMF field in the zlib stream. This hack of the zlib
- * stream is compliant to the stream specification.
+ /* First time. Ensure we have a temporary buffer for compression and
+ * trim the buffer list if it has more than one entry to free memory.
*/
- unsigned int z_cmf = data[0]; /* zlib compression method and flags */
-
- if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
+ if (png_ptr->zbuffer_list == NULL)
{
- /* Avoid memory underflows and multiplication overflows.
- *
- * The conditions below are practically always satisfied;
- * however, they still must be checked.
- */
- if (length >= 2 &&
- png_ptr->height < 16384 && png_ptr->width < 16384)
- {
- unsigned int z_cinfo;
- unsigned int half_z_window_size;
- int reduced = 0;
- png_alloc_size_t uncompressed_idat_size = png_image_size(png_ptr);
-
- z_cinfo = z_cmf >> 4;
- half_z_window_size = 1 << (z_cinfo + 7);
-
- while (uncompressed_idat_size <= half_z_window_size &&
- half_z_window_size >= 256)
- {
- z_cinfo--;
- half_z_window_size >>= 1;
- reduced = 1;
- }
-
- if (reduced && !(png_ptr->flags & PNG_FLAG_ZSTREAM_CMF_FIXUP))
- png_warning(png_ptr, "internal data size error (IDAT)");
-
- z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
-
- if (data[0] != z_cmf)
- {
- int tmp;
- data[0] = (png_byte)z_cmf;
- tmp = data[1] & 0xe0;
- tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
- data[1] = (png_byte)tmp;
- }
- }
+ png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp,
+ png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
+ png_ptr->zbuffer_list->next = NULL;
}
else
- png_error(png_ptr,
- "Invalid zlib compression method or flags in IDAT");
+ png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list->next);
+
+ /* It is a terminal error if we can't claim the zstream. */
+ if (png_deflate_claim(png_ptr, png_IDAT, png_image_size(png_ptr)) != Z_OK)
+ png_error(png_ptr, png_ptr->zstream.msg);
+
+ /* The output state is maintained in png_ptr->zstream, so it must be
+ * initialized here after the claim.
+ */
+ png_ptr->zstream.next_out = png_ptr->zbuffer_list->output;
+ png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
}
-#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */
- png_write_complete_chunk(png_ptr, png_IDAT, data, length);
- png_ptr->mode |= PNG_HAVE_IDAT;
-
- /* Prior to 1.5.4 this code was replicated in every caller (except at the
- * end, where it isn't technically necessary). Since this function has
- * flushed the data we can safely reset the zlib output buffer here.
+ /* Now loop reading and writing until all the input is consumed or an error
+ * terminates the operation. The _out values are maintained across calls to
+ * this function, but the input must be reset each time.
*/
- png_ptr->zstream.next_out = png_ptr->zbuf;
- png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+ png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input);
+ png_ptr->zstream.avail_in = 0; /* set below */
+ for (;;)
+ {
+ int ret;
+
+ /* INPUT: from the row data */
+ uInt avail = ZLIB_IO_MAX;
+
+ if (avail > input_len)
+ avail = (uInt)input_len; /* safe because of the check */
+
+ png_ptr->zstream.avail_in = avail;
+ input_len -= avail;
+
+ ret = deflate(&png_ptr->zstream, input_len > 0 ? Z_NO_FLUSH : flush);
+
+ /* Include as-yet unconsumed input */
+ input_len += png_ptr->zstream.avail_in;
+ png_ptr->zstream.avail_in = 0;
+
+ /* OUTPUT: write complete IDAT chunks when avail_out drops to zero, note
+ * that these two zstream fields are preserved across the calls, therefore
+ * there is no need to set these up on entry to the loop.
+ */
+ if (png_ptr->zstream.avail_out == 0)
+ {
+ png_bytep data = png_ptr->zbuffer_list->output;
+ uInt size = png_ptr->zbuffer_size;
+
+ /* Write an IDAT containing the data then reset the buffer. The
+ * first IDAT may need deflate header optimization.
+ */
+# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+ if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
+ png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
+ optimize_cmf(data, png_image_size(png_ptr));
+# endif
+
+ png_write_complete_chunk(png_ptr, png_IDAT, data, size);
+ png_ptr->mode |= PNG_HAVE_IDAT;
+
+ png_ptr->zstream.next_out = data;
+ png_ptr->zstream.avail_out = size;
+
+ /* For SYNC_FLUSH or FINISH it is essential to keep calling zlib with
+ * the same flush parameter until it has finished output, for NO_FLUSH
+ * it doesn't matter.
+ */
+ if (ret == Z_OK && flush != Z_NO_FLUSH)
+ continue;
+ }
+
+ /* The order of these checks doesn't matter much; it just effect which
+ * possible error might be detected if multiple things go wrong at once.
+ */
+ if (ret == Z_OK) /* most likely return code! */
+ {
+ /* If all the input has been consumed then just return. If Z_FINISH
+ * was used as the flush parameter something has gone wrong if we get
+ * here.
+ */
+ if (input_len == 0)
+ {
+ if (flush == Z_FINISH)
+ png_error(png_ptr, "Z_OK on Z_FINISH with output space");
+
+ return;
+ }
+ }
+
+ else if (ret == Z_STREAM_END && flush == Z_FINISH)
+ {
+ /* This is the end of the IDAT data; any pending output must be
+ * flushed. For small PNG files we may still be at the beginning.
+ */
+ png_bytep data = png_ptr->zbuffer_list->output;
+ uInt size = png_ptr->zbuffer_size - png_ptr->zstream.avail_out;
+
+# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+ if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
+ png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
+ optimize_cmf(data, png_image_size(png_ptr));
+# endif
+
+ png_write_complete_chunk(png_ptr, png_IDAT, data, size);
+ png_ptr->zstream.avail_out = 0;
+ png_ptr->zstream.next_out = NULL;
+ png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT;
+
+ png_ptr->zowner = 0; /* Release the stream */
+ return;
+ }
+
+ else
+ {
+ /* This is an error condition. */
+ png_zstream_error(png_ptr, ret);
+ png_error(png_ptr, png_ptr->zstream.msg);
+ }
+ }
}
/* Write an IEND chunk */
@@ -1081,10 +1203,9 @@
png_write_iCCP(png_structrp png_ptr, png_const_charp name, int compression_type,
png_const_bytep profile, png_uint_32 profile_len)
{
- png_size_t name_len, compressed_profile_len;
- png_charp new_name;
+ png_uint_32 name_len;
+ png_byte new_name[81]; /* 1 byte for the compression byte */
compression_state comp;
- png_uint_32 embedded_profile_len = 0;
png_debug(1, "in png_write_iCCP");
@@ -1100,64 +1221,36 @@
if (profile_len & 0x03)
png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)");
- embedded_profile_len = png_get_uint_32(profile);
-
- if (profile_len != embedded_profile_len)
- png_error(png_ptr, "Profile length does not match profile");
-
- if ((png_size_t)profile_len != profile_len)
{
- /* This isn't an application error, technically, but all the same do
- * not short-change the app by writing a PNG without the profile!
- */
- png_error(png_ptr, "Profile length exceeds system limits");
+ png_uint_32 embedded_profile_len = png_get_uint_32(profile);
+
+ if (profile_len != embedded_profile_len)
+ png_error(png_ptr, "Profile length does not match profile");
}
- if ((name_len = png_check_keyword(png_ptr, name, &new_name)) == 0)
- return;
+ name_len = png_check_keyword(png_ptr, name, new_name);
- comp.num_output_ptr = 0;
- comp.max_output_ptr = 0;
- comp.output_ptr = NULL;
- comp.input = NULL;
- comp.input_len = 0;
+ if (name_len == 0)
+ png_error(png_ptr, "iCCP: invalid keyword");
- compressed_profile_len = png_text_compress(png_ptr, (png_const_charp)profile,
- (png_size_t)/*ok*/profile_len, PNG_COMPRESSION_TYPE_BASE, &comp);
-
- /* 'name_len' is 1..79, so the following is safe: */
- if (compressed_profile_len > PNG_UINT_31_MAX - name_len - 2)
- {
- png_free(png_ptr, new_name);
-
- /* TODO: there is no 'compression_free' function */
- if (comp.output_ptr)
- {
- int i;
- for (i = 0; i < comp.num_output_ptr; i++)
- png_free(png_ptr, comp.output_ptr[i]);
- png_free(png_ptr, comp.output_ptr);
- }
-
- png_error(png_ptr, "Compressed profile exceeds PNG size limits");
- }
+ new_name[++name_len] = PNG_COMPRESSION_TYPE_BASE;
/* Make sure we include the NULL after the name and the compression type */
- png_write_chunk_header(png_ptr, png_iCCP,
- (png_uint_32)/*ok*/(name_len + compressed_profile_len + 2));
+ ++name_len;
- new_name[name_len + 1] = 0x00;
+ png_text_compress_init(&comp, profile, profile_len);
- png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 2);
+ /* Allow for keyword terminator and compression byte */
+ if (png_text_compress(png_ptr, png_iCCP, &comp, name_len) != Z_OK)
+ png_error(png_ptr, png_ptr->zstream.msg);
- if (compressed_profile_len)
- {
- comp.input_len = compressed_profile_len;
- png_write_compressed_data_out(png_ptr, &comp);
- }
+ png_write_chunk_header(png_ptr, png_iCCP, name_len + comp.output_len);
+
+ png_write_chunk_data(png_ptr, new_name, name_len);
+
+ png_write_compressed_data_out(png_ptr, &comp);
png_write_chunk_end(png_ptr);
- png_free(png_ptr, new_name);
}
#endif
@@ -1166,8 +1259,8 @@
void /* PRIVATE */
png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette)
{
- png_size_t name_len;
- png_charp new_name;
+ png_uint_32 name_len;
+ png_byte new_name[80];
png_byte entrybuf[10];
png_size_t entry_size = (spalette->depth == 8 ? 6 : 10);
png_size_t palette_size = entry_size * spalette->nentries;
@@ -1178,8 +1271,10 @@
png_debug(1, "in png_write_sPLT");
- if ((name_len = png_check_keyword(png_ptr,spalette->name, &new_name))==0)
- return;
+ name_len = png_check_keyword(png_ptr, spalette->name, new_name);
+
+ if (name_len == 0)
+ png_error(png_ptr, "sPLT: invalid keyword");
/* Make sure we include the NULL after the name */
png_write_chunk_header(png_ptr, png_sPLT,
@@ -1212,7 +1307,7 @@
png_save_uint_16(entrybuf + 8, ep->frequency);
}
- png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size);
+ png_write_chunk_data(png_ptr, entrybuf, entry_size);
}
#else
ep=spalette->entries;
@@ -1236,12 +1331,11 @@
png_save_uint_16(entrybuf + 8, ep[i].frequency);
}
- png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size);
+ png_write_chunk_data(png_ptr, entrybuf, entry_size);
}
#endif
png_write_chunk_end(png_ptr);
- png_free(png_ptr, new_name);
}
#endif
@@ -1496,151 +1590,21 @@
}
#endif
-#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
- defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
-/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification,
- * and if invalid, correct the keyword rather than discarding the entire
- * chunk. The PNG 1.0 specification requires keywords 1-79 characters in
- * length, forbids leading or trailing whitespace, multiple internal spaces,
- * and the non-break space (0x80) from ISO 8859-1. Returns keyword length.
- *
- * The new_key is allocated to hold the corrected keyword and must be freed
- * by the calling routine. This avoids problems with trying to write to
- * static keywords without having to have duplicate copies of the strings.
- */
-png_size_t /* PRIVATE */
-png_check_keyword(png_structrp png_ptr, png_const_charp key, png_charpp new_key)
-{
- png_size_t key_len;
- png_const_charp ikp;
- png_charp kp, dp;
- int kflag;
- int kwarn=0;
-
- png_debug(1, "in png_check_keyword");
-
- *new_key = NULL;
-
- if (key == NULL || (key_len = png_strlen(key)) == 0)
- {
- png_warning(png_ptr, "zero length keyword");
- return ((png_size_t)0);
- }
-
- png_debug1(2, "Keyword to be checked is '%s'", key);
-
- *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2));
-
- if (*new_key == NULL)
- {
- png_warning(png_ptr, "Out of memory while procesing keyword");
- return ((png_size_t)0);
- }
-
- /* Replace non-printing characters with a blank and print a warning */
- for (ikp = key, dp = *new_key; *ikp != '\0'; ikp++, dp++)
- {
- if ((png_byte)*ikp < 0x20 ||
- ((png_byte)*ikp > 0x7E && (png_byte)*ikp < 0xA1))
- {
- PNG_WARNING_PARAMETERS(p)
-
- png_warning_parameter_unsigned(p, 1, PNG_NUMBER_FORMAT_02x,
- (png_byte)*ikp);
- png_formatted_warning(png_ptr, p, "invalid keyword character 0x@1");
- *dp = ' ';
- }
-
- else
- {
- *dp = *ikp;
- }
- }
- *dp = '\0';
-
- /* Remove any trailing white space. */
- kp = *new_key + key_len - 1;
- if (*kp == ' ')
- {
- png_warning(png_ptr, "trailing spaces removed from keyword");
-
- while (*kp == ' ')
- {
- *(kp--) = '\0';
- key_len--;
- }
- }
-
- /* Remove any leading white space. */
- kp = *new_key;
- if (*kp == ' ')
- {
- png_warning(png_ptr, "leading spaces removed from keyword");
-
- while (*kp == ' ')
- {
- kp++;
- key_len--;
- }
- }
-
- png_debug1(2, "Checking for multiple internal spaces in '%s'", kp);
-
- /* Remove multiple internal spaces. */
- for (kflag = 0, dp = *new_key; *kp != '\0'; kp++)
- {
- if (*kp == ' ' && kflag == 0)
- {
- *(dp++) = *kp;
- kflag = 1;
- }
-
- else if (*kp == ' ')
- {
- key_len--;
- kwarn = 1;
- }
-
- else
- {
- *(dp++) = *kp;
- kflag = 0;
- }
- }
- *dp = '\0';
- if (kwarn)
- png_warning(png_ptr, "extra interior spaces removed from keyword");
-
- if (key_len == 0)
- {
- png_free(png_ptr, *new_key);
- png_warning(png_ptr, "Zero length keyword");
- }
-
- if (key_len > 79)
- {
- png_warning(png_ptr, "keyword length must be 1 - 79 characters");
- (*new_key)[79] = '\0';
- key_len = 79;
- }
-
- return (key_len);
-}
-#endif
-
#ifdef PNG_WRITE_tEXt_SUPPORTED
/* Write a tEXt chunk */
void /* PRIVATE */
png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
png_size_t text_len)
{
- png_size_t key_len;
- png_charp new_key;
+ png_uint_32 key_len;
+ png_byte new_key[80];
png_debug(1, "in png_write_tEXt");
- if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0)
- return;
+ key_len = png_check_keyword(png_ptr, key, new_key);
+
+ if (key_len == 0)
+ png_error(png_ptr, "tEXt: invalid keyword");
if (text == NULL || *text == '\0')
text_len = 0;
@@ -1648,24 +1612,24 @@
else
text_len = png_strlen(text);
+ if (text_len > PNG_UINT_31_MAX - (key_len+1))
+ png_error(png_ptr, "tEXt: text too long");
+
/* Make sure we include the 0 after the key */
png_write_chunk_header(png_ptr, png_tEXt,
- (png_uint_32)(key_len + text_len + 1));
+ (png_uint_32)/*checked above*/(key_len + text_len + 1));
/*
* We leave it to the application to meet PNG-1.0 requirements on the
* contents of the text. PNG-1.0 through PNG-1.2 discourage the use of
* any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them.
* The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
*/
- png_write_chunk_data(png_ptr, (png_bytep)new_key,
- (png_size_t)(key_len + 1));
+ png_write_chunk_data(png_ptr, new_key, key_len + 1);
if (text_len)
- png_write_chunk_data(png_ptr, (png_const_bytep)text,
- (png_size_t)text_len);
+ png_write_chunk_data(png_ptr, (png_const_bytep)text, text_len);
png_write_chunk_end(png_ptr);
- png_free(png_ptr, new_key);
}
#endif
@@ -1675,55 +1639,45 @@
png_write_zTXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
png_size_t text_len, int compression)
{
- png_size_t key_len;
- png_byte buf;
- png_charp new_key;
+ png_uint_32 key_len;
+ png_byte new_key[81];
compression_state comp;
png_debug(1, "in png_write_zTXt");
+ PNG_UNUSED(text_len); /* Always use strlen */
- comp.num_output_ptr = 0;
- comp.max_output_ptr = 0;
- comp.output_ptr = NULL;
- comp.input = NULL;
- comp.input_len = 0;
-
- if ((key_len = png_check_keyword(png_ptr, key, &new_key)) == 0)
+ if (compression == PNG_TEXT_COMPRESSION_NONE)
{
- png_free(png_ptr, new_key);
+ png_write_tEXt(png_ptr, key, text, 0);
return;
}
- if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE)
- {
- png_write_tEXt(png_ptr, new_key, text, (png_size_t)0);
- png_free(png_ptr, new_key);
- return;
- }
+ if (compression != PNG_TEXT_COMPRESSION_zTXt)
+ png_error(png_ptr, "zTXt: invalid compression type");
- text_len = png_strlen(text);
+ key_len = png_check_keyword(png_ptr, key, new_key);
+
+ if (key_len == 0)
+ png_error(png_ptr, "zTXt: invalid keyword");
+
+ /* Add the compression method and 1 for the keyword separator. */
+ new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
+ ++key_len;
/* Compute the compressed data; do it now for the length */
- text_len = png_text_compress(png_ptr, text, text_len, compression,
- &comp);
+ png_text_compress_init(&comp, (png_const_bytep)text,
+ text == NULL ? 0 : strlen(text));
+
+ if (png_text_compress(png_ptr, png_zTXt, &comp, key_len) != Z_OK)
+ png_error(png_ptr, png_ptr->zstream.msg);
/* Write start of chunk */
- png_write_chunk_header(png_ptr, png_zTXt,
- (png_uint_32)(key_len+text_len + 2));
+ png_write_chunk_header(png_ptr, png_zTXt, key_len + comp.output_len);
/* Write key */
- png_write_chunk_data(png_ptr, (png_bytep)new_key,
- (png_size_t)(key_len + 1));
-
- png_free(png_ptr, new_key);
-
- buf = (png_byte)compression;
-
- /* Write compression */
- png_write_chunk_data(png_ptr, &buf, (png_size_t)1);
+ png_write_chunk_data(png_ptr, new_key, key_len);
/* Write the compressed data */
- comp.input_len = text_len;
png_write_compressed_data_out(png_ptr, &comp);
/* Close the chunk */
@@ -1737,90 +1691,94 @@
png_write_iTXt(png_structrp png_ptr, int compression, png_const_charp key,
png_const_charp lang, png_const_charp lang_key, png_const_charp text)
{
- png_size_t lang_len, key_len, lang_key_len, text_len;
- png_charp new_lang;
- png_charp new_key = NULL;
- png_byte cbuf[2];
+ png_uint_32 key_len, prefix_len;
+ png_size_t lang_len, lang_key_len;
+ png_byte new_key[82];
compression_state comp;
png_debug(1, "in png_write_iTXt");
- comp.num_output_ptr = 0;
- comp.max_output_ptr = 0;
- comp.output_ptr = NULL;
- comp.input = NULL;
+ key_len = png_check_keyword(png_ptr, key, new_key);
+
+ if (key_len == 0)
+ png_error(png_ptr, "iTXt: invalid keyword");
- if ((key_len = png_check_keyword(png_ptr, key, &new_key)) == 0)
- return;
-
- if ((lang_len = png_check_keyword(png_ptr, lang, &new_lang)) == 0)
+ /* Set the compression flag */
+ switch (compression)
{
- png_warning(png_ptr, "Empty language field in iTXt chunk");
- new_lang = NULL;
- lang_len = 0;
+ case PNG_ITXT_COMPRESSION_NONE:
+ case PNG_TEXT_COMPRESSION_NONE:
+ compression = new_key[++key_len] = 0; /* no compression */
+ break;
+
+ case PNG_TEXT_COMPRESSION_zTXt:
+ case PNG_ITXT_COMPRESSION_zTXt:
+ compression = new_key[++key_len] = 1; /* compressed */
+ break;
+
+ default:
+ png_error(png_ptr, "iTXt: invalid compression");
}
- if (lang_key == NULL)
- lang_key_len = 0;
-
- else
- lang_key_len = png_strlen(lang_key);
-
- if (text == NULL)
- text_len = 0;
-
- else
- text_len = png_strlen(text);
-
- /* Compute the compressed data; do it now for the length */
- text_len = png_text_compress(png_ptr, text, text_len, compression - 2,
- &comp);
-
-
- /* Make sure we include the compression flag, the compression byte,
- * and the NULs after the key, lang, and lang_key parts
- */
-
- png_write_chunk_header(png_ptr, png_iTXt, (png_uint_32)(
- 5 /* comp byte, comp flag, terminators for key, lang and lang_key */
- + key_len
- + lang_len
- + lang_key_len
- + text_len));
+ new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
+ ++key_len; /* for the keywod separator */
/* We leave it to the application to meet PNG-1.0 requirements on the
* contents of the text. PNG-1.0 through PNG-1.2 discourage the use of
- * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them.
+ * any non-Latin-1 characters except for NEWLINE. ISO PNG, however,
+ * specifies that the text is UTF-8 and this really doesn't require any
+ * checking.
+ *
* The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
+ *
+ * TODO: validate the language tag correctly (see the spec.)
*/
- png_write_chunk_data(png_ptr, (png_bytep)new_key, (png_size_t)(key_len + 1));
+ if (lang == NULL) lang = ""; /* empty language is valid */
+ lang_len = strlen(lang)+1;
+ if (lang_key == NULL) lang_key = ""; /* may be empty */
+ lang_key_len = strlen(lang_key)+1;
+ if (text == NULL) text = ""; /* may be empty */
- /* Set the compression flag */
- if (compression == PNG_ITXT_COMPRESSION_NONE ||
- compression == PNG_TEXT_COMPRESSION_NONE)
- cbuf[0] = 0;
+ prefix_len = key_len;
+ if (lang_len > PNG_UINT_31_MAX-prefix_len)
+ prefix_len = PNG_UINT_31_MAX;
+ else
+ prefix_len = (png_uint_32)(prefix_len + lang_len);
- else /* compression == PNG_ITXT_COMPRESSION_zTXt */
- cbuf[0] = 1;
+ if (lang_key_len > PNG_UINT_31_MAX-prefix_len)
+ prefix_len = PNG_UINT_31_MAX;
+ else
+ prefix_len = (png_uint_32)(prefix_len + lang_key_len);
- /* Set the compression method */
- cbuf[1] = 0;
+ png_text_compress_init(&comp, (png_const_bytep)text, strlen(text));
- png_write_chunk_data(png_ptr, cbuf, (png_size_t)2);
+ if (compression)
+ {
+ if (png_text_compress(png_ptr, png_iTXt, &comp, prefix_len) != Z_OK)
+ png_error(png_ptr, png_ptr->zstream.msg);
+ }
- cbuf[0] = 0;
- png_write_chunk_data(png_ptr, (new_lang ? (png_const_bytep)new_lang : cbuf),
- (png_size_t)(lang_len + 1));
+ else
+ {
+ if (comp.input_len > PNG_UINT_31_MAX-prefix_len)
+ png_error(png_ptr, "iTXt: uncompressed text too long");
+ }
- png_write_chunk_data(png_ptr, (lang_key ? (png_const_bytep)lang_key : cbuf),
- (png_size_t)(lang_key_len + 1));
+ png_write_chunk_header(png_ptr, png_iTXt, comp.output_len + prefix_len);
- png_write_compressed_data_out(png_ptr, &comp);
+ png_write_chunk_data(png_ptr, new_key, key_len);
+
+ png_write_chunk_data(png_ptr, (png_const_bytep)lang, lang_len);
+
+ png_write_chunk_data(png_ptr, (png_const_bytep)lang_key, lang_key_len);
+
+ if (compression)
+ png_write_compressed_data_out(png_ptr, &comp);
+
+ else
+ png_write_chunk_data(png_ptr, (png_const_bytep)text, comp.input_len);
png_write_chunk_end(png_ptr);
-
- png_free(png_ptr, new_key);
- png_free(png_ptr, new_lang);
}
#endif
@@ -1851,18 +1809,25 @@
png_int_32 X1, int type, int nparams, png_const_charp units,
png_charpp params)
{
- png_size_t purpose_len, units_len, total_len;
+ png_uint_32 purpose_len;
+ png_size_t units_len, total_len;
png_size_tp params_len;
png_byte buf[10];
- png_charp new_purpose;
+ png_byte new_purpose[80];
int i;
png_debug1(1, "in png_write_pCAL (%d parameters)", nparams);
if (type >= PNG_EQUATION_LAST)
- png_warning(png_ptr, "Unrecognized equation type for pCAL chunk");
+ png_error(png_ptr, "Unrecognized equation type for pCAL chunk");
- purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1;
+ purpose_len = png_check_keyword(png_ptr, purpose, new_purpose);
+
+ if (purpose_len == 0)
+ png_error(png_ptr, "pCAL: invalid keyword");
+
+ ++purpose_len; /* terminator */
+
png_debug1(3, "pCAL purpose length = %d", (int)purpose_len);
units_len = png_strlen(units) + (nparams == 0 ? 0 : 1);
png_debug1(3, "pCAL units length = %d", (int)units_len);
@@ -1884,7 +1849,7 @@
png_debug1(3, "pCAL total length = %d", (int)total_len);
png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len);
- png_write_chunk_data(png_ptr, (png_const_bytep)new_purpose, purpose_len);
+ png_write_chunk_data(png_ptr, new_purpose, purpose_len);
png_save_int_32(buf, X0);
png_save_int_32(buf + 4, X1);
buf[8] = (png_byte)type;
@@ -1892,8 +1857,6 @@
png_write_chunk_data(png_ptr, buf, (png_size_t)10);
png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len);
- png_free(png_ptr, new_purpose);
-
for (i = 0; i < nparams; i++)
{
png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]);
@@ -2090,10 +2053,6 @@
png_ptr->num_rows = png_ptr->height;
png_ptr->usr_width = png_ptr->width;
}
-
- png_deflate_claim(png_ptr, 1/*for IDAT*/, png_image_size(png_ptr));
- png_ptr->zstream.avail_out = png_ptr->zbuf_size;
- png_ptr->zstream.next_out = png_ptr->zbuf;
}
/* Internal use only. Called when finished processing a row of data. */
@@ -2116,8 +2075,6 @@
static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
#endif
- int ret;
-
png_debug(1, "in png_write_finish_row");
/* Next row */
@@ -2179,41 +2136,7 @@
/* If we get here, we've just written the last row, so we need
to flush the compressor */
- do
- {
- /* Tell the compressor we are done */
- ret = deflate(&png_ptr->zstream, Z_FINISH);
-
- /* Check for an error */
- if (ret == Z_OK)
- {
- /* Check to see if we need more room */
- if (!(png_ptr->zstream.avail_out))
- {
- png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
- png_ptr->zstream.next_out = png_ptr->zbuf;
- png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
- }
- }
-
- else if (ret != Z_STREAM_END)
- {
- if (png_ptr->zstream.msg != NULL)
- png_error(png_ptr, png_ptr->zstream.msg);
-
- else
- png_error(png_ptr, "zlib error");
- }
- } while (ret != Z_STREAM_END);
-
- /* Write any extra space */
- if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
- {
- png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size -
- png_ptr->zstream.avail_out);
- }
-
- png_deflate_release(png_ptr);
+ png_compress_IDAT(png_ptr, NULL, 0, Z_FINISH);
}
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
@@ -3081,65 +3004,13 @@
/* Do the actual writing of a previously filtered row. */
static void
png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,
- png_size_t avail/*includes filter byte*/)
+ png_size_t full_row_length/*includes filter byte*/)
{
png_debug(1, "in png_write_filtered_row");
png_debug1(2, "filter = %d", filtered_row[0]);
- /* Set up the zlib input buffer */
- png_ptr->zstream.next_in = filtered_row;
- png_ptr->zstream.avail_in = 0;
- /* Repeat until we have compressed all the data */
- do
- {
- int ret; /* Return of zlib */
-
- /* Record the number of bytes available - zlib supports at least 65535
- * bytes at one step, depending on the size of the zlib type 'uInt', the
- * maximum size zlib can write at once is ZLIB_IO_MAX (from pngpriv.h).
- * Use this because on 16 bit systems 'rowbytes' can be up to 65536 (i.e.
- * one more than 16 bits) and, in this case 'rowbytes+1' can overflow a
- * uInt. ZLIB_IO_MAX can be safely reduced to cause zlib to be called
- * with smaller chunks of data.
- */
- if (png_ptr->zstream.avail_in == 0)
- {
- if (avail > ZLIB_IO_MAX)
- {
- png_ptr->zstream.avail_in = ZLIB_IO_MAX;
- avail -= ZLIB_IO_MAX;
- }
-
- else
- {
- /* So this will fit in the available uInt space: */
- png_ptr->zstream.avail_in = (uInt)avail;
- avail = 0;
- }
- }
-
- /* Compress the data */
- ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
-
- /* Check for compression errors */
- if (ret != Z_OK)
- {
- if (png_ptr->zstream.msg != NULL)
- png_error(png_ptr, png_ptr->zstream.msg);
-
- else
- png_error(png_ptr, "zlib error");
- }
-
- /* See if it is time to write another IDAT */
- if (!(png_ptr->zstream.avail_out))
- {
- /* Write the IDAT and reset the zlib output buffer */
- png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
- }
- /* Repeat until all data has been compressed */
- } while (avail > 0 || png_ptr->zstream.avail_in > 0);
+ png_compress_IDAT(png_ptr, filtered_row, full_row_length, Z_NO_FLUSH);
/* Swap the current and previous rows */
if (png_ptr->prev_row != NULL)
diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa
index 8eba0e2..cc8e786 100644
--- a/scripts/pnglibconf.dfa
+++ b/scripts/pnglibconf.dfa
@@ -516,6 +516,30 @@
setting ZBUF_SIZE default 8192
+# This is the size of the decompression buffer used when counting or checking
+# the decompressed size of an LZ stream from a compressed ancilliary chunk; the
+# decompressed data is never used so a different size may be optimal. This size
+# was determined using contrib/libtests/timepng.c with compressed zTXt data
+# around 11MByte in size. Slight speed improvements (up to about 14% in
+# timepng) can be achieved by very large increases (to 32kbyte) on regular data,
+# but highly compressible data shows only around 2% improvement. The size is
+# chosen to minimize the effects of DoS attacks based on using very large
+# amounts of highly compressible data.
+
+setting INFLATE_BUF_SIZE default 1024
+
+# This is the maximum amount of IDAT data that the sequential reader will
+# process at one time. The setting does not affect the size of IDAT chunks
+# read, just the amount read at once. Neither does it affect the progressive
+# reader, which processes just the amount of data the application gives it.
+# The sequential reader is currently unable to process more than one IDAT at
+# once - it has to read and process each one in turn. There is no point setting
+# this to a value larger than the IDAT chunks typically encountered (it would
+# just waste memory) but there may be some point in reducing it below the value
+# of ZBUF_SIZE (the size of IDAT chunks written by libpng.)
+
+setting IDAT_READ_SIZE default PNG_ZBUF_SIZE
+
# Ancillary chunks
chunk bKGD
chunk cHRM
diff --git a/scripts/pnglibconf.h.prebuilt b/scripts/pnglibconf.h.prebuilt
index 40e547c..417b1c8 100644
--- a/scripts/pnglibconf.h.prebuilt
+++ b/scripts/pnglibconf.h.prebuilt
@@ -3,7 +3,7 @@
/* pnglibconf.h - library build configuration */
-/* Libpng 1.6.0beta17 - March 6, 2012 */
+/* Libpng 1.6.0beta17 - March 9, 2012 */
/* Copyright (c) 1998-2012 Glenn Randers-Pehrson */
@@ -26,6 +26,8 @@
#define PNG_COST_SHIFT 3
#define PNG_DEFAULT_READ_MACROS 1
#define PNG_GAMMA_THRESHOLD_FIXED 5000
+#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE
+#define PNG_INFLATE_BUF_SIZE 1024
#define PNG_MAX_GAMMA_8 11
#define PNG_QUANTIZE_BLUE_BITS 5
#define PNG_QUANTIZE_GREEN_BITS 5