Simplified API: use system appropriate types

This is an API change for 1.7, albeit a quiet one; it may produce compiler
warnings but should not result in errors, unless warnings are treated as errors.

On 64-bit systems it widens the results of the various PNG_IMAGE_ macros that
return size values (component counts, byte sizes) to 64 bits.  It also changes
the row_stride parameter, which is the pointer difference between adjacent rows
of the image buffer, to ptrdiff_t which is the ANSI-C90 defined type of the
difference of two pointers.  The existing (1.6.22) checks for overflow are
preserved but now accomdate images that require more than 32 bits of address
space when size_t/ptrdiff_t are 64 bit types.

Signed-off-by: John Bowler <jbowler@acm.org>
This commit is contained in:
John Bowler 2016-01-18 20:44:26 -08:00
parent f2a160299e
commit e46cd2e6bc
3 changed files with 53 additions and 38 deletions

37
png.h
View File

@ -3312,15 +3312,17 @@ typedef struct
/* Information about the whole row, or whole image */
#define PNG_IMAGE_ROW_STRIDE(image)\
(PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width)
(PNG_IMAGE_PIXEL_CHANNELS((image).format) * (png_alloc_size_t)(image).width)
/* Return the total number of components in a single row of the image; this
* is the minimum 'row stride', the minimum count of components between each
* row. For a color-mapped image this is the minimum number of bytes in a
* row.
*
* WARNING: this macro overflows for some images with more than one component
* and very large image widths. libpng will refuse to process an image where
* this macro would overflow.
* WARNING: libpng 1.7: this macro now returns a png_alloc_size_t, previous
* versions returned a png_uint_32 and could overflow for images that fit in
* memory. This macro can still overflow, but if it does the row will not
* fit in memory. The simplified API functions detect this and refuse to
* handle the image.
*/
#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\
@ -3328,8 +3330,11 @@ typedef struct
/* Return the size, in bytes, of an image buffer given a png_image and a row
* stride - the number of components to leave space for in each row.
*
* WARNING: this macro overflows a 32-bit integer for some large PNG images,
* libpng will refuse to process an image where such an overflow would occur.
* WARNING: This is the total size of the image, for large images it will
* overflow on a 32-bit system. In libpng 1.7 (but not before) it returns a
* png_alloc_size_t which means that the result only overflows for
* ridiculously large PNG files. libpng checks and will refuse to handle
* such data (the PNG is probably invalid.)
*/
#define PNG_IMAGE_SIZE(image)\
@ -3409,7 +3414,7 @@ PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image,
/* The PNG header is read from the given memory buffer. */
PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image,
png_const_colorp background, void *buffer, png_int_32 row_stride,
png_const_colorp background, void *buffer, ptrdiff_t row_stride,
void *colormap));
/* Finish reading the image into the supplied buffer and clean up the
* png_image structure.
@ -3469,11 +3474,11 @@ PNG_EXPORT(238, void, png_image_free, (png_imagep image));
#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image,
const char *file, int convert_to_8bit, const void *buffer,
png_int_32 row_stride, const void *colormap));
ptrdiff_t row_stride, const void *colormap));
/* Write the image to the named file. */
PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file,
int convert_to_8_bit, const void *buffer, png_int_32 row_stride,
int convert_to_8_bit, const void *buffer, ptrdiff_t row_stride,
const void *colormap));
/* Write the image to the given (FILE*). */
#endif /* SIMPLIFIED_WRITE_STDIO */
@ -3501,7 +3506,7 @@ PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file,
PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory,
png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit,
const void *buffer, png_int_32 row_stride, const void *colormap));
const void *buffer, ptrdiff_t row_stride, const void *colormap));
/* Write the image to the given memory buffer. The function both writes the
* whole PNG data stream to *memory and updates *memory_bytes with the count
* of bytes written.
@ -3546,12 +3551,6 @@ PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory,
#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height)
/* The number of uncompressed bytes in the PNG byte encoding of the image;
* uncompressing the PNG IDAT data will give this number of bytes.
*
* NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this
* macro can because of the extra bytes used in the PNG byte encoding. You
* need to avoid this macro if your image size approaches 2^30 in width or
* height. The same goes for the remainder of these macros; they all produce
* bigger numbers than the actual in-memory image size.
*/
#ifndef PNG_ZLIB_MAX_SIZE
# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U)
@ -3585,6 +3584,12 @@ PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory,
* The result is of type png_alloc_size_t, on 32-bit systems this may
* overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will
* run out of buffer space but return a corrected size which should work.
*
* NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this
* macro can because of the extra bytes used in the PNG byte encoding. You
* need to avoid this macro if your image size approaches the limit of your
* system memory; typically the maximum value of size_t. Use the above
* function call instead.
*/
#endif /* SIMPLIFIED_WRITE */
/*******************************************************************************

View File

@ -1071,7 +1071,7 @@ typedef struct
/* Arguments: */
png_imagep image;
png_voidp buffer;
png_int_32 row_stride;
ptrdiff_t row_stride;
png_voidp colormap;
png_const_colorp background;
/* Local variables: */
@ -4128,7 +4128,7 @@ png_image_read_direct(png_voidp argument)
int PNGAPI
png_image_finish_read(png_imagep image, png_const_colorp background,
void *buffer, png_int_32 row_stride, void *colormap)
void *buffer, ptrdiff_t row_stride, void *colormap)
{
if (image != NULL && image->version == PNG_IMAGE_VERSION)
{
@ -4138,13 +4138,18 @@ png_image_finish_read(png_imagep image, png_const_colorp background,
*/
const unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format);
if (image->width <= 0x7FFFFFFFU/channels) /* no overflow */
/* The test is slightly evil: it assumes that a signed pointer difference
* (ptrdiff_t) can hold a maximum value of half, rounded down, of the
* maximum of a (size_t). This is almost certain to be true.
*/
if (image->width <= (PNG_SIZE_MAX >> 1)/channels) /* no overflow */
{
png_uint_32 check;
const png_uint_32 png_row_stride = image->width * channels;
png_alloc_size_t check;
const png_alloc_size_t png_row_stride =
(png_alloc_size_t)/*SAFE*/image->width * channels;
if (row_stride == 0)
row_stride = (png_int_32)/*SAFE*/png_row_stride;
row_stride = (ptrdiff_t)png_row_stride;
if (row_stride < 0)
check = -row_stride;
@ -4154,11 +4159,11 @@ png_image_finish_read(png_imagep image, png_const_colorp background,
if (image->opaque != NULL && buffer != NULL && check >= png_row_stride)
{
/* Now check for overflow of the image buffer calculation; this
* limits the whole image size to 32 bits for API compatibility with
* the current, 32-bit, PNG_IMAGE_BUFFER_SIZE macro.
/* Now check for overflow of the image buffer calculation; check for
* (size_t) overflow here. This detects issues with the
* PNG_IMAGE_BUFFER_SIZE macro.
*/
if (image->height <= 0xFFFFFFFF/png_row_stride)
if (image->height <= PNG_SIZE_MAX/png_row_stride)
{
if ((image->format & PNG_FORMAT_FLAG_COLORMAP) == 0 ||
(image->colormap_entries > 0 && colormap != NULL))

View File

@ -1329,7 +1329,7 @@ typedef struct
/* Arguments: */
png_imagep image;
png_const_voidp buffer;
png_int_32 row_stride;
ptrdiff_t row_stride;
png_const_voidp colormap;
int convert_to_8bit;
/* Local variables: */
@ -1781,13 +1781,18 @@ png_image_write_main(png_voidp argument)
{
const unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format);
if (image->width <= 0x7FFFFFFFU/channels) /* no overflow */
/* The test is slightly evil: it assumes that a signed pointer difference
* (ptrdiff_t) can hold a maximum value of half, rounded down, of the
* maximum of a (size_t). This is almost certain to be true.
*/
if (image->width <= (PNG_SIZE_MAX >> 1)/channels) /* no overflow */
{
png_uint_32 check;
const png_uint_32 png_row_stride = image->width * channels;
png_alloc_size_t check;
const png_alloc_size_t png_row_stride =
(png_alloc_size_t)/*SAFE*/image->width * channels;
if (display->row_stride == 0)
display->row_stride = (png_int_32)/*SAFE*/png_row_stride;
display->row_stride = (ptrdiff_t)png_row_stride;
if (display->row_stride < 0)
check = -display->row_stride;
@ -1797,11 +1802,11 @@ png_image_write_main(png_voidp argument)
if (check >= png_row_stride)
{
/* Now check for overflow of the image buffer calculation; this
* limits the whole image size to 32 bits for API compatibility with
* the current, 32-bit, PNG_IMAGE_BUFFER_SIZE macro.
/* Now check for overflow of the image buffer calculation; check for
* (size_t) overflow here. This detects issues with the
* PNG_IMAGE_BUFFER_SIZE macro.
*/
if (image->height > 0xFFFFFFFF/png_row_stride)
if (image->height > PNG_SIZE_MAX/png_row_stride)
png_error(image->opaque->png_ptr, "memory image too large");
}
@ -2042,7 +2047,7 @@ png_image_write_memory(png_voidp argument)
int PNGAPI
png_image_write_to_memory(png_imagep image, void *memory,
png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8bit,
const void *buffer, png_int_32 row_stride, const void *colormap)
const void *buffer, ptrdiff_t row_stride, const void *colormap)
{
/* Write the image to the given buffer, or count the bytes if it is NULL */
if (image != NULL && image->version == PNG_IMAGE_VERSION)
@ -2108,7 +2113,7 @@ png_image_write_to_memory(png_imagep image, void *memory,
#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
int PNGAPI
png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
const void *buffer, png_int_32 row_stride, const void *colormap)
const void *buffer, ptrdiff_t row_stride, const void *colormap)
{
/* Write the image to the given (FILE*). */
if (image != NULL && image->version == PNG_IMAGE_VERSION)
@ -2152,7 +2157,7 @@ png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
int PNGAPI
png_image_write_to_file(png_imagep image, const char *file_name,
int convert_to_8bit, const void *buffer, png_int_32 row_stride,
int convert_to_8bit, const void *buffer, ptrdiff_t row_stride,
const void *colormap)
{
/* Write the image to the named file. */