diff --git a/png.h b/png.h index 7b5b822ab..592ca3f55 100644 --- a/png.h +++ b/png.h @@ -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 */ /******************************************************************************* diff --git a/pngread.c b/pngread.c index d85c3d6ac..4557b2efe 100644 --- a/pngread.c +++ b/pngread.c @@ -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)) diff --git a/pngwrite.c b/pngwrite.c index e5e38944e..a85bcbbc3 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -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. */