diff --git a/contrib/examples/pngcp.c b/contrib/examples/pngcp.c index e6de44bfb..ebd13d76d 100644 --- a/contrib/examples/pngcp.c +++ b/contrib/examples/pngcp.c @@ -92,6 +92,8 @@ typedef enum #define SIZES 0x080 /* Report input and output sizes */ #define SEARCH 0x100 /* Search IDAT compression options */ #define NOWRITE 0x200 /* Do not write an output file */ +#define IGNORE_INDEX 0x400 /* Ignore out of range palette indices (BAD!) */ +#define FIX_INDEX 0x800 /* 'Fix' out of range palette indices (OK) */ #define OPTION 0x80000000 /* Used for handling options */ #define LIST 0x80000001 /* Used for handling options */ @@ -229,6 +231,8 @@ static const struct option S(sizes, SIZES) S(search, SEARCH) S(nowrite, NOWRITE) + S(ignore-palette-index, IGNORE_INDEX) + S(fix-palette-index, FIX_INDEX) # undef S /* OPTION settings, these and LIST settings are read on demand */ @@ -1558,7 +1562,7 @@ read_function(png_structp pp, png_bytep data, png_size_t size) { struct display *dp = get_dp(pp); - if (fread(data, size, 1U, dp->fp) == 1U) + if (size == 0U || fread(data, size, 1U, dp->fp) == 1U) dp->read_size += size; else @@ -1584,6 +1588,12 @@ read_png(struct display *dp, const char *filename) png_set_benign_errors(dp->read_pp, 1/*allowed*/); + if ((dp->options & FIX_INDEX) != 0) + png_set_check_for_invalid_index(dp->read_pp, 1/*on, no warning*/); + + else if ((dp->options & IGNORE_INDEX) != 0) /* DANGEROUS */ + png_set_check_for_invalid_index(dp->read_pp, -1/*off completely*/); + /* The png_read_png API requires us to make the info struct, but it does the * call to png_read_info. */ @@ -1621,6 +1631,37 @@ read_png(struct display *dp, const char *filename) dp->size = rb * dp->h + dp->h/*filter byte*/; } + if (dp->ct == PNG_COLOR_TYPE_PALETTE && (dp->options & FIX_INDEX) != 0) + { + int max = png_get_palette_max(dp->read_pp, dp->ip); + png_colorp palette = NULL; + int num = -1; + + if (png_get_PLTE(dp->read_pp, dp->ip, &palette, &num) != PNG_INFO_PLTE + || max < 0 || num <= 0 || palette == NULL) + display_log(dp, LIBPNG_ERROR, "invalid png_get_PLTE result"); + + if (max != num-1) + { + /* 'Fix' the palette. */ + int i; + png_color newpal[256]; + + for (i=0; iread_pp, dp->ip, newpal, i); + } + } + display_clean_read(dp); dp->operation = "none"; } @@ -1744,6 +1785,9 @@ write_png(struct display *dp, const char *destname) png_set_benign_errors(dp->write_pp, 1/*allowed*/); png_set_write_fn(dp->write_pp, dp, write_function, NULL/*flush*/); + if ((dp->options & IGNORE_INDEX) != 0) /* DANGEROUS */ + png_set_check_for_invalid_index(dp->write_pp, -1/*off completely*/); + /* Restore the text chunks when using libpng 1.6 or less; this is a macro * which expands to nothing in 1.7+ In earlier versions it tests * dp->text_stashed, which is only set (below) *after* the first write. diff --git a/png.h b/png.h index 8267e4fac..e0818e62d 100644 --- a/png.h +++ b/png.h @@ -2885,9 +2885,17 @@ PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); #ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED PNG_EXPORT(242, void, png_set_check_for_invalid_index, (png_structrp png_ptr, int enabled_if_greater_than_0)); - /* By default the check is enabled on both read and write, passing a value - * which is 0 or negative disables the check. Disabling the check also - * prevents the following API from working. + /* By default the check is enabled on both read and write when the number of + * entries in the palette is less than the maximum required by the bit depth + * of a palette image. + * + * Passing 1 to 'enabled' turns the check on in all cases. + * Passing -1 turns it off and the PNG may have invalid palette index values. + * Passing 0 restores the default. + * + * On read chunk (benign) error messages are only produced with the default + * setting; it is assumed that when the check is turned on explicitly the + * caller will call png_get_palette_max to check the result. */ #endif /* CHECK_FOR_INVALID_INDEX */ #ifdef PNG_GET_PALETTE_MAX_SUPPORTED @@ -2895,7 +2903,7 @@ PNG_EXPORT(243, int, png_get_palette_max, (png_const_structrp png_ptr, png_const_inforp info_ptr)); /* The info_ptr is not used, it may be NULL in 1.7.0 (not in earlier * versions). If the information is not available because - * png_set_check_for_invalid_index disabled the check this API returns -1. + * png_set_check_for_invalid_index was not used to turn it on -1 is returned. * Valid results can only be obtained after the complete image has been read, * though it may be called at any time to get the result so far. */ diff --git a/pngpriv.h b/pngpriv.h index c93b20b65..f09e5dd8b 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -1378,7 +1378,12 @@ PNG_INTERNAL_FUNCTION(png_transformp,png_add_transform,(png_structrp png_ptr, * * PNG_RWTR_CHECK_PALETTE PI W11: happens in pngwrite.c last */ -# define PNG_TR_INIT_ALPHA (PNG_TR_START + 0x0300U) +# define PNG_TR_START_CACHE (PNG_TR_START + 0x0300U) + /* Not used on a transform; this is just a marker for the point at which + * palette or low-bit-depth caching can start on read. (The previous + * operations cannot be cached). + */ +# define PNG_TR_INIT_ALPHA (PNG_TR_START + 0x0400U) /* This just handles alpha/tRNS initialization issues to resolve the * inter-dependencies with tRNS expansion and background composition; it * doesn't do anything itself, just sets flags and pushes transforms. diff --git a/pngread.c b/pngread.c index 8cd02e2c1..e89ffb986 100644 --- a/pngread.c +++ b/pngread.c @@ -196,7 +196,9 @@ png_read_sequential_unknown(png_structrp png_ptr, png_inforp info_ptr) if (buffer != NULL) { - png_crc_read(png_ptr, buffer, png_ptr->chunk_length); + if (png_ptr->chunk_length > 0U) + png_crc_read(png_ptr, buffer, png_ptr->chunk_length); + png_crc_finish(png_ptr, 0); png_handle_unknown(png_ptr, info_ptr, buffer); } @@ -323,7 +325,7 @@ png_read_IDAT(png_structrp png_ptr) { png_uint_32 l = png_ptr->chunk_length; - if (l == 0) /* end of this IDAT */ + while (l == 0) /* end of this IDAT */ { png_crc_finish(png_ptr, 0); png_read_chunk_header(png_ptr); @@ -331,7 +333,7 @@ png_read_IDAT(png_structrp png_ptr) if (png_ptr->chunk_name != png_IDAT) /* end of all IDAT */ { png_ptr->mode |= PNG_AFTER_IDAT; - break; + goto done; } l = png_ptr->chunk_length; @@ -346,6 +348,7 @@ png_read_IDAT(png_structrp png_ptr) IDAT_size += (uInt)/*SAFE*/l; png_ptr->chunk_length -= l; } +done: /* IDAT_size may be zero if the compressed image stream is truncated; * this is likely given a broken PNG. diff --git a/pngrio.c b/pngrio.c index 9ac4781fa..a66e6b6f4 100644 --- a/pngrio.c +++ b/pngrio.c @@ -19,7 +19,7 @@ */ #include "pngpriv.h" -#define PNG_SRC_FILE PNG_SRC_FILE_rio +#define PNG_SRC_FILE PNG_SRC_FILE_pngrio #ifdef PNG_READ_SUPPORTED @@ -34,6 +34,11 @@ png_read_data(png_structrp png_ptr, png_voidp data, png_size_t length) { png_debug1(4, "reading %d bytes", (int)length); + /* This was guaranteed by prior versions of libpng, so app callbacks may + * assume it even though it isn't documented to be the case. + */ + debug(length > 0U); + if (png_ptr->rw_data_fn != NULL) png_ptr->rw_data_fn(png_ptr, png_voidcast(png_bytep,data), length); diff --git a/pngrtran.c b/pngrtran.c index d6bb290fa..e744fb9cf 100644 --- a/pngrtran.c +++ b/pngrtran.c @@ -5885,26 +5885,26 @@ typedef struct } png_cache_params, *png_cache_paramsp; static void -init_caching(png_structp png_ptr, png_cache_paramsp cp) - /* Given an already initialized cp->tend turn on caching if appropriate. */ +init_caching(png_structp png_ptr, png_transform_controlp tend) + /* Given an already initialized tend turn on caching if appropriate. */ { /* Handle the colormap case, where a cache is always required: */ - if (cp->tend.format & PNG_FORMAT_FLAG_COLORMAP) + if (tend->format & PNG_FORMAT_FLAG_COLORMAP) { /* This turns starts the palette caching with the next transform: */ - cp->tend.palette = cp->tend.caching = 1U; - cp->tend.transparent_alpha = png_ptr->transparent_palette; - cp->tend.format = PNG_FORMAT_FLAG_COLOR; + tend->palette = tend->caching = 1U; + tend->transparent_alpha = png_ptr->transparent_palette; + tend->format = PNG_FORMAT_FLAG_COLOR; # ifdef PNG_READ_tRNS_SUPPORTED - if (png_ptr->num_trans > 0 && !(cp->tend.invalid_info & PNG_INFO_tRNS)) + if (png_ptr->num_trans > 0 && !(tend->invalid_info & PNG_INFO_tRNS)) { - cp->tend.format |= PNG_FORMAT_FLAG_ALPHA; + tend->format |= PNG_FORMAT_FLAG_ALPHA; } # endif /* READ_tRNS */ - cp->tend.bit_depth = 8U; + tend->bit_depth = 8U; } - else if (PNG_TC_PIXEL_DEPTH(cp->tend) <= 8) + else if (PNG_TC_PIXEL_DEPTH(*tend) <= 8) { /* Cacheable pixel transforms; the pixel is less than 8 bits in size so * the cache makes sense. @@ -5912,7 +5912,7 @@ init_caching(png_structp png_ptr, png_cache_paramsp cp) * TODO: check the cost estimate and the image size to avoid expensive * caches of very small images. */ - cp->tend.caching = 1U; + tend->caching = 1U; } /* TODO: handle handle 8-bit GA/RGB/RGBA */ @@ -6019,7 +6019,6 @@ update_palette(png_structp png_ptr, png_cache_paramsp cp, * list, so: */ affirm((cp->tstart.format & PNG_FORMAT_FLAG_COLORMAP) != 0); /* required */ - debug(cp->start == &png_ptr->transform_list); /* should be harmless */ /* Run the whole of the given list on the palette data. PNG_TC_INIT_FINAL * has already been run; this is a full run (with init == 0). @@ -6029,7 +6028,7 @@ update_palette(png_structp png_ptr, png_cache_paramsp cp, only_deb(png_transform_control orig = cp->tend;) cp->tend = cp->tstart; - init_caching(png_ptr, cp); + init_caching(png_ptr, &cp->tend); /* And set up tend to actually work out the palette: */ cp->tend.init = 0U; cp->tend.width = setup_palette_cache(png_ptr, cache.b8); @@ -6296,7 +6295,7 @@ make_cache(png_structp png_ptr, png_cache_paramsp cp, unsigned int max_depth) */ save_cp_channel_data(&save, &cp->tend); cp->tend = cp->tstart; - init_caching(png_ptr, cp); + init_caching(png_ptr, &cp->tend); /* And set tend to work out the result of transforming each possible pixel * value: */ @@ -6589,7 +6588,7 @@ png_read_init_transform_mech(png_structp png_ptr, png_transform_controlp tc) */ { png_transformp *list = &png_ptr->transform_list; - unsigned int max_depth; + unsigned int max_depth, cache_start_depth; png_cache_params cp; /* PNG color-mapped data must be handled here so that the palette is updated @@ -6608,8 +6607,7 @@ png_read_init_transform_mech(png_structp png_ptr, png_transform_controlp tc) # endif /* READ_tRNS */ cp.end = cp.start = list; cp.tend = cp.tstart = *tc; - init_caching(png_ptr, &cp); - max_depth = PNG_TC_PIXEL_DEPTH(cp.tend); + max_depth = cache_start_depth = PNG_TC_PIXEL_DEPTH(cp.tend); while (*cp.end != NULL) { @@ -6619,6 +6617,22 @@ png_read_init_transform_mech(png_structp png_ptr, png_transform_controlp tc) if (tr->order >= PNG_TR_USER) break; + /* If caching is not on and this transform is after PNG_TR_START_CACHE + * try to turn it on. + */ + if (tr->order > PNG_TR_START_CACHE && !cp.tend.caching) + { + cp.start = cp.end; + cp.tstart = cp.tend; + init_caching(png_ptr, &cp.tend); + + if (cp.tend.caching) + { + cache_start_depth = max_depth; + max_depth = PNG_TC_PIXEL_DEPTH(cp.tend); + } + } + /* If the 'palette' flag is set and the next transform has order * PNG_TR_ENCODING or later cache the results so far and continue with the * original palette data (cp.tstart). @@ -6630,6 +6644,8 @@ png_read_init_transform_mech(png_structp png_ptr, png_transform_controlp tc) /* The cache handling function must maintain cp.end; */ affirm(tr == *cp.end); max_depth = PNG_TC_PIXEL_DEPTH(cp.tend); + if (max_depth < cache_start_depth) + max_depth = cache_start_depth; } /* Now run the transform list entry: */ @@ -6664,6 +6680,8 @@ png_read_init_transform_mech(png_structp png_ptr, png_transform_controlp tc) handle_cache(png_ptr, &cp, max_depth); affirm(tr == *cp.end); max_depth = PNG_TC_PIXEL_DEPTH(cp.tend); + if (max_depth < cache_start_depth) + max_depth = cache_start_depth; } /* At the end run the init on the user transform: */ diff --git a/pngrutil.c b/pngrutil.c index 1ee3e9efc..a6df46180 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -955,8 +955,11 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr) debug(png_ptr->palette == NULL); /* should only get set once */ png_ptr->palette = png_voidcast(png_colorp, png_malloc(png_ptr, sizeof (png_color[PNG_MAX_PALETTE_LENGTH]))); - memset(png_ptr->palette, 0xFFU, sizeof (png_color[PNG_MAX_PALETTE_LENGTH])); - memcpy(png_ptr->palette, info_ptr->palette, 3*num); + /* This works because we know png_set_PLTE also expands the palette to the + * full size: + */ + memcpy(png_ptr->palette, info_ptr->palette, + sizeof (png_color[PNG_MAX_PALETTE_LENGTH])); png_ptr->num_palette = info_ptr->num_palette; /* The three chunks, bKGD, hIST and tRNS *must* appear after PLTE and before @@ -4330,11 +4333,10 @@ png_read_process_IDAT(png_structrp png_ptr, png_bytep transformed_row, /* Run the list. It is ok if it doesn't end up doing * anything; this can happen with a lazy init. * - * TODO: I don't think lazy inits happen any more, hence - * the 'debug' below. + * NOTE: if the only thingin the list is a palette check + * function it can remove itself at this point. */ max_depth = png_run_transform_list_forwards(png_ptr, &tc); - debug(png_ptr->transform_list != NULL); /* This is too late, a stack overwrite has already * happened, but it may still prevent exploits: diff --git a/pngset.c b/pngset.c index deec83455..e428cca1c 100644 --- a/pngset.c +++ b/pngset.c @@ -539,12 +539,30 @@ png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr, * of num_palette entries, in case of an invalid PNG file or incorrect * call to png_set_PLTE() with too-large sample values. */ - info_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr, + info_ptr->palette = png_voidcast(png_colorp, png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)))); if (num_palette > 0) memcpy(info_ptr->palette, palette, num_palette * (sizeof (png_color))); + /* Set the remainder of the palette entries to something recognizable; the + * code used to leave them set to 0, which made seeing palette index errors + * difficult. + */ + if (num_palette < PNG_MAX_PALETTE_LENGTH) + { + int i; + png_color c; + + memset(&c, 0x42, sizeof c); /* fill in any padding */ + c.red = 0xbe; + c.green = 0xad; + c.blue = 0xed; /* Visible in memory as 'beaded' */ + + for (i=num_palette; ipalette[i] = c; + } + info_ptr->num_palette = png_check_bits(png_ptr, num_palette, 9); info_ptr->free_me |= PNG_FREE_PLTE; info_ptr->valid |= PNG_INFO_PLTE; diff --git a/pngstruct.h b/pngstruct.h index 449a1e733..c8d150366 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -393,13 +393,15 @@ struct png_struct_def unsigned int num_trans :9; /* number of transparency values */ unsigned int transparent_palette :1; /* if they are all 0 or 255 */ #endif /* READ_tRNS */ -#ifdef PNG_GET_PALETTE_MAX_SUPPORTED - unsigned int palette_index_max :9; /* maximum palette index found in IDAT */ -#endif /* GET_PALETTE_MAX */ -#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED - unsigned int palette_index_check_disabled :1; /* defaults to 0, 'enabled' */ - unsigned int palette_index_check_issued :1; /* error message output */ -#endif /* CHECK_FOR_INVALID_INDEX */ +#ifdef PNG_PALETTE_MAX_SUPPORTED + unsigned int palette_index_max :8; /* maximum palette index in IDAT */ + unsigned int palette_index_check :2; /* one of the following: */ +# define PNG_PALETTE_CHECK_DEFAULT 0U +# define PNG_PALETTE_CHECK_OFF 1U +# define PNG_PALETTE_CHECK_ON 2U + unsigned int palette_index_have_max :1; /* max is being set */ + unsigned int palette_index_check_issued :1; /* error message output */ +#endif /* PALETTE_MAX */ #ifdef PNG_READ_tRNS_SUPPORTED png_color_16 trans_color; /* transparent color for non-paletted files */ #endif /* READ_tRNS */ diff --git a/pngtrans.c b/pngtrans.c index 3aafb1afb..291987ab1 100644 --- a/pngtrans.c +++ b/pngtrans.c @@ -586,23 +586,35 @@ set_palette_max(png_structrp png_ptr, png_transformp tr, unsigned int max, { /* One of these must be true: */ # ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED - if (max >= tr->args && !png_ptr->palette_index_check_issued) + if (max >= (tr->args & 0x1FFU) && !png_ptr->palette_index_check_issued) { -# ifdef PNG_READ_SUPPORTED + /* In 1.7 only issue the error/warning by default; the 'check' API is + * used to enable/disable the check. Assume that if the app enabled it + * then the app will be checking the result with get_palette_max in + * read. In write an error results unless the check is disabled. + */ + if (png_ptr->palette_index_check == PNG_PALETTE_CHECK_DEFAULT # ifdef PNG_WRITE_SUPPORTED - (png_ptr->read_struct ? png_chunk_benign_error : png_error) -# else /* !WRITE */ - png_chunk_benign_error -# endif /* !WRITE */ -# else /* !READ */ - png_error -# endif /* !READ */ - (png_ptr, "palette index too large"); + || (!png_ptr->read_struct && + png_ptr->palette_index_check != PNG_PALETTE_CHECK_OFF) +# endif /* WRITE */ + ) +# ifdef PNG_READ_SUPPORTED +# ifdef PNG_WRITE_SUPPORTED + (png_ptr->read_struct ? png_chunk_benign_error : png_error) +# else /* !WRITE */ + png_chunk_benign_error +# endif /* READ */ +# else /* !READ */ + png_error +# endif /* WRITE */ + (png_ptr, "palette index too large"); + png_ptr->palette_index_check_issued = 1; } # endif # ifdef PNG_GET_PALETTE_MAX_SUPPORTED - png_ptr->palette_index_max = png_check_bits(png_ptr, max, 9); + png_ptr->palette_index_max = png_check_byte(png_ptr, max); # endif if (max == format_max) @@ -642,7 +654,8 @@ palette_max_2bpp(png_transformp *tr, png_transform_controlp tc) { png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); png_uint_32 width = tc->width; - unsigned int max = (*tr)->args; /* saved maximum */ + const png_uint_32 args = (*tr)->args; + unsigned int max = args >> 24; /* saved maximum */ while (width > 0) { @@ -699,7 +712,7 @@ palette_max_2bpp(png_transformp *tr, png_transform_controlp tc) } /* End of input, check the next line. */ - (*tr)->args = max; + (*tr)->args = (max << 24) + (args & 0xFFFFFFU); } static void @@ -707,7 +720,8 @@ palette_max_4bpp(png_transformp *tr, png_transform_controlp tc) { png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); png_uint_32 width = tc->width; - unsigned int max = (*tr)->args; /* saved maximum */ + const png_uint_32 args = (*tr)->args; + unsigned int max = args >> 24; /* saved maximum */ while (width > 0) { @@ -725,12 +739,12 @@ palette_max_4bpp(png_transformp *tr, png_transform_controlp tc) max = (input >> 4) & 0xFU; } - if (max > (*tr)->args) + if (max > (args >> 24)) { if (set_palette_max(tc->png_ptr, *tr, max, 15U)) return; - (*tr)->args = max; + (*tr)->args = (max << 24) + (args & 0xFFFFFFU); } } @@ -739,7 +753,8 @@ palette_max_8bpp(png_transformp *tr, png_transform_controlp tc) { png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); png_uint_32 width = tc->width; - unsigned int max = (*tr)->args; /* saved maximum */ + const png_uint_32 args = (*tr)->args; + unsigned int max = args >> 24; /* saved maximum */ while (width > 0) { @@ -751,12 +766,12 @@ palette_max_8bpp(png_transformp *tr, png_transform_controlp tc) --width; } - if (max > (*tr)->args) + if (max > (args >> 24)) { if (set_palette_max(tc->png_ptr, *tr, max, 255U)) return; - (*tr)->args = max; + (*tr)->args = (max << 24) + (args & 0xFFFFFFU); } } @@ -764,26 +779,27 @@ static void palette_max_init(png_transformp *tr, png_transform_controlp tc) { # define png_ptr (tc->png_ptr) - if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0) + affirm((tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0); + debug(tc->init); + + if (tc->init == PNG_TC_INIT_FINAL) { - if (tc->init == PNG_TC_INIT_FINAL) + /* Record the palette depth to check here along with the running total + * in the top 8 bits (initially 0, which is always valid). + */ + (*tr)->args = png_ptr->num_palette; + + switch (tc->bit_depth) { - /* Record the palette depth to check here: */ - (*tr)->args = png_ptr->num_palette; - - switch (tc->bit_depth) - { - case 1: (*tr)->fn = palette_max_1bpp; break; - case 2: (*tr)->fn = palette_max_2bpp; break; - case 4: (*tr)->fn = palette_max_4bpp; break; - case 8: (*tr)->fn = palette_max_8bpp; break; - default:impossible("palette bit depth"); - } + case 1: (*tr)->fn = palette_max_1bpp; break; + case 2: (*tr)->fn = palette_max_2bpp; break; + case 4: (*tr)->fn = palette_max_4bpp; break; + case 8: (*tr)->fn = palette_max_8bpp; break; + default:impossible("palette bit depth"); } - } - else - (*tr)->fn = NULL; /* not applicable */ + png_ptr->palette_index_have_max = 1U; + } # undef png_ptr } #endif /* PALETTE_MAX */ @@ -792,11 +808,7 @@ palette_max_init(png_transformp *tr, png_transform_controlp tc) int PNGAPI png_get_palette_max(png_const_structrp png_ptr, png_const_inforp info_ptr) { - if (png_ptr != NULL -# ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED - && !png_ptr->palette_index_check_disabled -# endif - ) + if (png_ptr != NULL && png_ptr->palette_index_have_max) return png_ptr->palette_index_max; /* This indicates to the caller that the information is not available: */ @@ -812,7 +824,7 @@ png_get_palette_max(png_const_structrp png_ptr, png_const_inforp info_ptr) * fewer entries than the image's bit-depth would allow. We recover * from this gracefully by filling any incomplete palette with zeros * (opaque black). By default, when this occurs libpng will issue - * a benign error. This API can be used to override that behavior. + * an error. This API can be used to override that behavior. */ void PNGAPI png_set_check_for_invalid_index(png_structrp png_ptr, int enabled) @@ -821,17 +833,31 @@ png_set_check_for_invalid_index(png_structrp png_ptr, int enabled) if (png_ptr != NULL) { if (png_ptr->read_struct) + { # ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED - png_ptr->palette_index_check_disabled = enabled <= 0; + if (enabled > 0) + png_ptr->palette_index_check = PNG_PALETTE_CHECK_ON; + else if (enabled < 0) + png_ptr->palette_index_check = PNG_PALETTE_CHECK_OFF; + else + png_ptr->palette_index_check = PNG_PALETTE_CHECK_DEFAULT; # else /* !READ_CHECK_FOR_INVALID_INDEX */ png_app_error(png_ptr, "no read palette check support"); # endif /* !READ_CHECK_FOR_INVALID_INDEX */ + } else /* write struct */ + { # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED - png_ptr->palette_index_check_disabled = enabled <= 0; + if (enabled > 0) + png_ptr->palette_index_check = PNG_PALETTE_CHECK_ON; + else if (enabled < 0) + png_ptr->palette_index_check = PNG_PALETTE_CHECK_OFF; + else + png_ptr->palette_index_check = PNG_PALETTE_CHECK_DEFAULT; # else /* !WRITE_CHECK_FOR_INVALID_INDEX */ png_app_error(png_ptr, "no write palette check support"); # endif /* !WRITE_CHECK_FOR_INVALID_INDEX */ + } } } #endif /* CHECK_FOR_INVALID_INDEX */ @@ -856,8 +882,11 @@ png_init_row_info(png_structrp png_ptr) defined (PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) (png_ptr->read_struct # ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED - && !png_ptr->palette_index_check_disabled) + && (png_ptr->palette_index_check == PNG_PALETTE_CHECK_ON || + (png_ptr->palette_index_check == PNG_PALETTE_CHECK_DEFAULT + && png_ptr->num_palette < (1U << png_ptr->bit_depth))) # endif /* READ_CHECK_FOR_INVALID_INDEX */ + ) # else /* no READ support */ 0 # endif /* READ checks */ @@ -866,8 +895,11 @@ png_init_row_info(png_structrp png_ptr) defined (PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) (!png_ptr->read_struct # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED - && !png_ptr->palette_index_check_disabled) + && (png_ptr->palette_index_check == PNG_PALETTE_CHECK_ON || + (png_ptr->palette_index_check == PNG_PALETTE_CHECK_DEFAULT + && png_ptr->num_palette < (1U << png_ptr->bit_depth))) # endif /* WRITE_CHECK_FOR_INVALID_INDEX */ + ) # else /* no WRITE support */ 0 # endif /* WRITE checks */ diff --git a/pngwio.c b/pngwio.c index 8112bc205..980e39a4e 100644 --- a/pngwio.c +++ b/pngwio.c @@ -33,6 +33,11 @@ void /* PRIVATE */ png_write_data(png_structrp png_ptr, png_const_voidp data, png_size_t length) { + /* This was guaranteed by prior versions of libpng, so app callbacks may + * assume it even though it isn't documented to be the case. + */ + debug(length > 0U); + /* NOTE: write_data_fn must not change the buffer! * This cast is required because of the API; changing the type of the * callback would require every app to change the callback and that change diff --git a/pngwrite.c b/pngwrite.c index ff1555361..d49e31e64 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -61,13 +61,7 @@ write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr, (keep == PNG_HANDLE_CHUNK_AS_DEFAULT && png_ptr->unknown_default == PNG_HANDLE_CHUNK_ALWAYS))) #endif - { - /* TODO: review, what is wrong with a zero length unknown chunk? */ - if (up->size == 0) - png_warning(png_ptr, "Writing zero-length unknown chunk"); - png_write_chunk(png_ptr, up->name, up->data, up->size); - } } } } diff --git a/pngwutil.c b/pngwutil.c index a49073b2b..489c550ab 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -3918,6 +3918,12 @@ typedef struct filter_selector * filling the entire window (i.e. the maximum number that can be fitted * in (window-1) bytes). */ + png_uint_32 sum_bias[PNG_FILTER_VALUE_LAST]; + /* For each filter a measure of its goodness in the filter sum + * calculation. This allows filter selection based on the + * sum-of-absolute-dfferences method to be biased to favour particular + * filters. There was no such bias before 1.7 + */ png_uint_32 distance; /* Distance from beginning */ png_codeset codeset; /* Set of seen codes */ png_uint_32 code_distance[256]; /* Distance at last occurence */ @@ -3942,6 +3948,7 @@ png_start_filter_select(png_zlib_statep ps, unsigned int bpp) ps->filter_select_window = window = PNG_FILTER_SELECT_WINDOW_MAX; fs->code_count = 0; + memset(fs->sum_bias, 0U, sizeof fs->sum_bias); /* This is the maximum row width, in pixels, of a row which fits and * leaves 1 byte free in the window. For any bigger row filter @@ -3975,17 +3982,31 @@ typedef struct /* Per-filter data. This remains separate from the above until the filter * selection has been made. It reflects the above however the codeset only * records codes present in this row. + * + * The 'sum' fields are the sum of the absolute deviation of each code from + * 0, the algorithm from 1.6 and earlier. In other words: + * + * if (code >= 128) + * sum += code; + * else + * sum += 256-code; */ unsigned int code_count; /* Number of distinct codes seen in row */ unsigned int new_code_count; /* Number of new codes seen in row */ + png_uint_32 sum_low; /* Low 31 bits of code sum */ + png_uint_32 sum_high; /* High 32 bits of code sum */ png_codeset codeset; /* Set of codes seen in this row */ png_uint_32 code_distance[256]; /* Distance at last occurence in this row */ } filter_data; static void -filter_data_init(filter_data *fd, png_uint_32 distance, unsigned int filter) +filter_data_init(filter_data *fd, png_uint_32 distance, unsigned int filter, + unsigned int code_is_set) { fd->code_count = 1U; + fd->new_code_count = !code_is_set; + fd->sum_low = filter; + fd->sum_high = 0U; memset(&fd->codeset, 0U, sizeof fd->codeset); PNG_CODE_SET(fd->codeset, filter); fd->code_distance[filter] = distance; @@ -4003,6 +4024,23 @@ add_code(const filter_selector *fs, filter_data *fd, png_uint_32 distance, if (!PNG_CODE_IS_SET(fs->codeset, code)) ++(fd->new_code_count); } + + { + png_uint_32 low = fd->sum_low; + + if (code >= 128U) + low += code; + + else + low += 256U-code; + + /* Handle overflow into the top bit: */ + if (low & 0x80000000U) + fd->sum_low = low & 0x7FFFFFFFU, ++fd->sum_high; + + else + fd->sum_low = low; + } } static png_byte @@ -4139,7 +4177,8 @@ select_filter(png_zlib_statep ps, png_const_bytep row, distance = fs->distance; filter_data_init(fd+PNG_FILTER_VALUE_NONE, distance++, - PNG_FILTER_VALUE_NONE); + PNG_FILTER_VALUE_NONE, + PNG_CODE_IS_SET(fs->codeset, PNG_FILTER_VALUE_NONE)); if (bpp >= 8) /* complete bytes */ { @@ -4192,7 +4231,8 @@ select_filter(png_zlib_statep ps, png_const_bytep row, for (i=PNG_FILTER_VALUE_NONE+1U; icodeset, i)); } ++distance; @@ -4297,16 +4337,40 @@ select_filter(png_zlib_statep ps, png_const_bytep row, * adjacent values, the assumption is that this will result in values * close to 0. */ + { + png_uint_32 high = -1; + png_uint_32 low = -1; + unsigned int min_f = 0 /*unset, but safe*/; + unsigned int f; + for (f=PNG_FILTER_VALUE_NONE; fsum_bias[f]; - /* The final fallback is to use the 'none' filter. */ - return filter_data_select(ps, fd, PNG_FILTER_VALUE_NONE, distance, width); + if (low & 0x80000000U) + { + low &= 0x7FFFFFFFU, --high; + if (high & 0x80000000U) + low = high = 0U; + } + + min_f = f; + } + + return filter_data_select(ps, fd, min_f, distance, width); + } } debug(filters < PNG_FILTER_VALUE_LAST); -# undef png_ptr - return ps->filters = filters; +# undef png_ptr } #else /* !SELECT_FILTER */ /* Filter selection not being done, just call png_write_start_row: */