diff --git a/ANNOUNCE b/ANNOUNCE index 4a0fdde4e..f91b5e461 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 @@ Version 1.6.0beta16 [March 6, 2012] 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 2e118e50e..080a0d7af 100644 --- a/CHANGES +++ b/CHANGES @@ -4037,9 +4037,13 @@ Version 1.6.0beta16 [March 6, 2012] 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 3f1b7f745..cf8c29c07 100644 --- a/png.c +++ b/png.c @@ -283,41 +283,29 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, */ 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 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, /* 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 @@ png_get_copyright(png_const_structrp png_ptr) #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 @@ png_reset_zstream(png_structrp png_ptr) 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 @@ png_access_version_number(void) #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 748582a3d..c75b3b6b2 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 @@ PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), #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 e2c9175f3..0fedb441a 100644 --- a/pngget.c +++ b/pngget.c @@ -1070,7 +1070,24 @@ png_get_user_chunk_ptr(png_const_structrp png_ptr) 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 c7de794ae..aed706f09 100644 --- a/pngpread.c +++ b/pngpread.c @@ -838,7 +838,7 @@ png_push_read_IDAT(png_structrp png_ptr) 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 @@ png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer, { /* 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 @@ png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer, /* 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 af5dae017..313f0fe46 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -467,6 +467,7 @@ typedef const png_uint_16p * png_const_uint_16pp; #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 @@ typedef const png_uint_16p * png_const_uint_16pp; /* 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 @@ typedef const png_uint_16p * png_const_uint_16pp; #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 @@ extern "C" { */ /* 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 @@ PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY); /* 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 @@ PNG_INTERNAL_FUNCTION(void,png_write_hIST,(png_structrp png_ptr, #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_read_filter_row_paeth4_neon,(png_row_infop row_in 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 59721fe2d..68304b8b4 100644 --- a/pngread.c +++ b/pngread.c @@ -48,6 +48,14 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, 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 @@ png_start_read_image(png_structrp png_ptr) 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 @@ png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) 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 @@ png_read_end(png_structrp png_ptr, png_inforp info_ptr) 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_read_destroy(png_structrp png_ptr) 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 fd89c895d..2befd4add 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -203,17 +203,20 @@ png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length) 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 @@ png_crc_error(png_structrp png_ptr) 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 " 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 @@ png_inflate_claim(png_structrp png_ptr) * 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_decompress_chunk(png_structrp png_ptr, png_uint_32 chunklength, */ 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 @@ png_decompress_chunk(png_structrp png_ptr, png_uint_32 chunklength, 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 @@ png_decompress_chunk(png_structrp png_ptr, png_uint_32 chunklength, * 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_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { 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_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ 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 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) /* 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 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) * 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 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) * 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 @@ void /* PRIVATE */ 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 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } #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 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) #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_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { 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_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) (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_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } #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 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ void /* PRIVATE */ 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 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) * 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 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ void /* PRIVATE */ 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 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) * 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_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { 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 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 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); } @@ -3821,6 +3813,176 @@ png_read_filter_row(png_structrp pp, png_row_infop row_info, png_bytep row, } #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) { @@ -3886,66 +4048,7 @@ png_read_finish_row(png_structrp png_ptr) #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 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) 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 4fbac40ba..882be9718 100644 --- a/pngset.c +++ b/pngset.c @@ -1231,26 +1231,51 @@ png_set_compression_buffer_size(png_structrp png_ptr, png_size_t size) 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 fb29b5477..88a5ddad6 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 @@ struct png_struct_def 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 @@ struct png_struct_def /* 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 7fb857745..4b10b0417 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -475,6 +475,8 @@ png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, /* 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 @@ png_set_flush(png_structrp png_ptr, int nrows) 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 @@ png_write_flush(png_structrp png_ptr) 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 @@ png_write_destroy(png_structrp png_ptr) 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 d4a50f6af..44a841359 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -197,7 +197,7 @@ png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name, 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 @@ png_image_size(png_structrp png_ptr) 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 " 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 @@ png_deflate_claim(png_structrp png_ptr, int for_IDAT, 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 @@ png_deflate_claim(png_structrp png_ptr, int for_IDAT, * 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_deflate_claim(png_structrp png_ptr, int for_IDAT, 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 @@ png_deflate_claim(png_structrp png_ptr, int for_IDAT, * 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_write_PLTE(png_structrp png_ptr, png_const_colorp palette, 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 @@ void /* PRIVATE */ 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 @@ png_write_iCCP(png_structrp png_ptr, png_const_charp name, int compression_type, 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 @@ png_write_iCCP(png_structrp png_ptr, png_const_charp name, int compression_type, 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_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) 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_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) 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_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) 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 @@ png_write_hIST(png_structrp png_ptr, png_const_uint_16p hist, int num_hist) } #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 @@ png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text, 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 @@ void /* PRIVATE */ 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 @@ void /* PRIVATE */ 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_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, 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); + + if (purpose_len == 0) + png_error(png_ptr, "pCAL: invalid keyword"); + + ++purpose_len; /* terminator */ - purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1; 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_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, 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_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, 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_write_start_row(png_structrp png_ptr) 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 @@ png_write_finish_row(png_structrp png_ptr) 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 @@ png_write_finish_row(png_structrp png_ptr) /* 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 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) /* 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 8eba0e28b..cc8e78655 100644 --- a/scripts/pnglibconf.dfa +++ b/scripts/pnglibconf.dfa @@ -516,6 +516,30 @@ setting sCAL_PRECISION default 5 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 40e547c3d..417b1c82f 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