summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Bowler <jbowler@acm.org>2013-01-10 11:01:00 -0600
committerGlenn Randers-Pehrson <glennrp at users.sourceforge.net>2013-01-10 11:01:00 -0600
commit4accd423c283f95fbd6672c2baa4df034a8afdef (patch)
treea1de4a42b9578f0dfae1a58ea4fa67d40bbd00fd
parentf62caaf4dcff59ac2090274f56ab91c804a9525a (diff)
downloadlibpng-4accd423c283f95fbd6672c2baa4df034a8afdef.tar.gz
[libpng17]Fixed conceivable but difficult to repro overflow. Also added
two test programs to generate and test a PNG which should have the problem.
-rw-r--r--ANNOUNCE7
-rw-r--r--CHANGES4
-rw-r--r--contrib/libtests/fakepng.c57
-rw-r--r--contrib/libtests/readpng.c104
-rw-r--r--pnginfo.h5
-rw-r--r--pngset.c28
6 files changed, 194 insertions, 11 deletions
diff --git a/ANNOUNCE b/ANNOUNCE
index a16eaf9cd..4f91261bd 100644
--- a/ANNOUNCE
+++ b/ANNOUNCE
@@ -1,5 +1,5 @@
-Libpng 1.7.0alpha07 - January 1, 2013
+Libpng 1.7.0alpha07 - January 10, 2013
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.
@@ -11,7 +11,6 @@ Source files with LF line endings (for Unix/Linux) and with a
1.7.0alpha07.tar.xz (LZMA-compressed, recommended)
1.7.0alpha07.tar.gz
- 1.7.0alpha07.tar.bz2
Source files with CRLF line endings (for Windows), without the
"configure" script
@@ -107,7 +106,9 @@ Version 1.7.0alpha05 [December 24, 2012]
Version 1.7.0alpha06 [January 1, 2013]
Fixed 'make distcheck' on SUN OS - libpng.so was not being removed
-Version 1.7.0alpha07 [January 1, 2013]
+Version 1.7.0alpha07 [January 10, 2013]
+ Fixed conceivable but difficult to repro overflow. Also added two test
+ programs to generate and test a PNG which should have the problem.
===========================================================================
NOTICE November 17, 2012:
diff --git a/CHANGES b/CHANGES
index 43224b950..4e7e7f0d1 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4392,7 +4392,9 @@ Version 1.7.0alpha05 [December 24, 2012]
Version 1.7.0alpha06 [January 1, 2013]
Fixed 'make distcheck' on SUN OS - libpng.so was not being removed
-Version 1.7.0alpha07 [January 1, 2013]
+Version 1.7.0alpha07 [January 10, 2013]
+ Fixed conceivable but difficult to repro overflow. Also added two test
+ programs to generate and test a PNG which should have the problem.
===========================================================================
NOTICE November 17, 2012:
diff --git a/contrib/libtests/fakepng.c b/contrib/libtests/fakepng.c
new file mode 100644
index 000000000..ba360d15a
--- /dev/null
+++ b/contrib/libtests/fakepng.c
@@ -0,0 +1,57 @@
+/* Fake a PNG - just write it out directly. */
+#include <stdio.h>
+#include <zlib.h> /* for crc32 */
+
+void
+put_uLong(uLong val)
+{
+ putchar(val >> 24);
+ putchar(val >> 16);
+ putchar(val >> 8);
+ putchar(val >> 0);
+}
+
+void
+put_chunk(const unsigned char *chunk, uInt length)
+{
+ uLong crc;
+
+ put_uLong(length-4); /* Exclude the tag */
+
+ fwrite(chunk, length, 1, stdout);
+
+ crc = crc32(0, Z_NULL, 0);
+ put_uLong(crc32(crc, chunk, length));
+}
+
+const unsigned char signature[] =
+{
+ 137, 80, 78, 71, 13, 10, 26, 10
+};
+
+const unsigned char IHDR[] =
+{
+ 73, 72, 68, 82, /* IHDR */
+ 0, 0, 0, 1, /* width */
+ 0, 0, 0, 1, /* height */
+ 1, /* bit depth */
+ 0, /* color type: greyscale */
+ 0, /* compression method */
+ 0, /* filter method */
+ 0 /* interlace method: none */
+};
+
+const unsigned char unknown[] =
+{
+ 'u', 'n', 'K', 'n' /* "unKn" - private safe to copy */
+};
+
+int
+main(void)
+{
+ fwrite(signature, sizeof signature, 1, stdout);
+ put_chunk(IHDR, sizeof IHDR);
+
+ for(;;)
+ put_chunk(unknown, sizeof unknown);
+}
diff --git a/contrib/libtests/readpng.c b/contrib/libtests/readpng.c
new file mode 100644
index 000000000..631325766
--- /dev/null
+++ b/contrib/libtests/readpng.c
@@ -0,0 +1,104 @@
+/* readpng.c
+ *
+ * Copyright (c) 2013 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.0 [(PENDING RELEASE)]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Load an arbitrary number of PNG files (from the command line, or, if there
+ * are no arguments on the command line, from stdin) then run a time test by
+ * reading each file by row. The test does nothing with the read result and
+ * does no transforms. The only output is a time as a floating point number of
+ * seconds with 9 decimal digits.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#if (defined HAVE_CONFIG_H) && !(defined PNG_NO_CONFIG_H)
+# include <config.h>
+#endif
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+# include <png.h>
+#else
+# include "../../png.h"
+#endif
+
+static int
+read_png(FILE *fp)
+{
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
+ png_infop info_ptr = NULL;
+ png_bytep row = NULL, display = NULL;
+
+ if (png_ptr == NULL)
+ return 0;
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ if (row != NULL) free(row);
+ if (display != NULL) free(display);
+ return 0;
+ }
+
+ png_init_io(png_ptr, fp);
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL)
+ png_error(png_ptr, "OOM allocating info structure");
+
+ png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0);
+
+ png_read_info(png_ptr, info_ptr);
+
+ {
+ png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+ row = malloc(rowbytes);
+ display = malloc(rowbytes);
+
+ if (row == NULL || display == NULL)
+ png_error(png_ptr, "OOM allocating row buffers");
+
+ {
+ png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
+ int passes = png_set_interlace_handling(png_ptr);
+ int pass;
+
+ png_start_read_image(png_ptr);
+
+ for (pass = 0; pass < passes; ++pass)
+ {
+ png_uint_32 y = height;
+
+ /* NOTE: this trashes the row each time; interlace handling won't
+ * work, but this avoids memory thrashing for speed testing.
+ */
+ while (y-- > 0)
+ png_read_row(png_ptr, row, display);
+ }
+ }
+ }
+
+ /* Make sure to read to the end of the file: */
+ png_read_end(png_ptr, info_ptr);
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ free(row);
+ free(display);
+ return 1;
+}
+
+int
+main(void)
+{
+ /* Exit code 0 on success. */
+ return !read_png(stdin);
+}
diff --git a/pnginfo.h b/pnginfo.h
index ee79beb8b..56cc6b394 100644
--- a/pnginfo.h
+++ b/pnginfo.h
@@ -223,7 +223,10 @@ defined(PNG_READ_BACKGROUND_SUPPORTED)
#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
/* Storage for unknown chunks that the library doesn't recognize. */
png_unknown_chunkp unknown_chunks;
- unsigned int unknown_chunks_num;
+ /* The type of this field must match png_struct::user_chunk_cache_max,
+ * else overflow can occur.
+ */
+ png_uint_32 unknown_chunks_num;
#endif
#ifdef PNG_sPLT_SUPPORTED
diff --git a/pngset.c b/pngset.c
index 1c1751d9b..70d4aa56c 100644
--- a/pngset.c
+++ b/pngset.c
@@ -1125,11 +1125,12 @@ check_location(png_const_structrp png_ptr, int location)
void PNGAPI
png_set_unknown_chunks(png_const_structrp png_ptr,
- png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)
+ png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns_in)
{
+ png_uint_32 num_unknowns;
png_unknown_chunkp np;
- if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0)
+ if (png_ptr == NULL || info_ptr == NULL || num_unknowns_in <= 0)
return;
/* Check for the failure cases where support has been disabled at compile
@@ -1160,9 +1161,24 @@ png_set_unknown_chunks(png_const_structrp png_ptr,
* undefined behavior. Changing to png_malloc fixes this by producing a
* png_error. The (png_size_t) cast was also removed as it hides a potential
* overflow.
- *
- * TODO: fix the potential overflow in the multiply
*/
+ num_unknowns = (unsigned int)/*SAFE*/num_unknowns_in;
+
+ /* There are two overflow conditions, one on the count one on memory, on a
+ * 32-bit system the memory limit is critical, on a 64-bit system the count
+ * limit.
+ */
+ if (num_unknowns > PNG_UINT_32_MAX - info_ptr->unknown_chunks_num ||
+ num_unknowns > PNG_SIZE_MAX/(sizeof *np) - info_ptr->unknown_chunks_num)
+ {
+ /* This is a benign read error (user limits are disabled and we are about
+ * to overflow 2^32 chunks) and an application write error.
+ */
+ png_chunk_report(png_ptr, "too many unknown chunks",
+ PNG_CHUNK_WRITE_ERROR);
+ return;
+ }
+
np = png_voidcast(png_unknown_chunkp, png_malloc(png_ptr,
(info_ptr->unknown_chunks_num + (unsigned int)num_unknowns) *
(sizeof (png_unknown_chunk))));
@@ -1179,8 +1195,8 @@ png_set_unknown_chunks(png_const_structrp png_ptr,
/* Increment unknown_chunks_num each time round the loop to protect the
* just-allocated chunk data.
*/
- for (; --num_unknowns >= 0;
- ++np, ++unknowns, ++(info_ptr->unknown_chunks_num))
+ for (; num_unknowns > 0;
+ --num_unknowns, ++np, ++unknowns, ++(info_ptr->unknown_chunks_num))
{
memcpy(np->name, unknowns->name, (sizeof unknowns->name));
np->name[(sizeof np->name)-1] = '\0';