[libpng16] Fixed conceivable but difficult to repro overflow. Also added
two test programs to generate and test a PNG which should have the problem.
diff --git a/pngset.c b/pngset.c
index 1ea5ff3..1b01030 100644
--- a/pngset.c
+++ b/pngset.c
@@ -1086,11 +1086,12 @@
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
@@ -1121,9 +1122,24 @@
* 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))));
@@ -1140,8 +1156,8 @@
/* 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';