Description: fix arbitrary code execution from additional data row via malformed PNG imageOrigin: Backport, from upstream 1.2.44diff -Nur libpng-1.2.42/pngpread.c libpng-1.2.42.new/pngpread.c--- libpng-1.2.42/pngpread.c 2010-01-03 00:04:19.000000000 -0500+++ libpng-1.2.42.new/pngpread.c 2010-07-05 11:26:26.000000000 -0400@@ -776,8 +776,7 @@ png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);- if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))- png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size);+ png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); png_ptr->idat_size -= save_size; png_ptr->buffer_size -= save_size;@@ -800,8 +799,8 @@ save_size = png_ptr->current_buffer_size; png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);- if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))- png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size);++ png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); png_ptr->idat_size -= save_size; png_ptr->buffer_size -= save_size;@@ -826,60 +825,101 @@ png_process_IDAT_data(png_structp png_ptr, png_bytep buffer, png_size_t buffer_length) {- int ret;-- if ((png_ptr->flags & PNG_FLAG_ZLIB_FINISHED) && buffer_length)- png_error(png_ptr, "Extra compression data");-+ /* The caller checks for a non-zero buffer length. */+ if (!(buffer_length > 0) || buffer == NULL)+ png_error(png_ptr, "No IDAT data (internal error)");++ /* This routine must process all the data it has been given+ * before returning, calling the row callback as required to+ * handle the uncompressed results.+ */ png_ptr->zstream.next_in = buffer; png_ptr->zstream.avail_in = (uInt)buffer_length;- for (;;)++ /* Keep going until the decompressed data is all processed+ * or the stream marked as finished.+ */+ while (png_ptr->zstream.avail_in > 0 &&+ !(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) {- ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);- if (ret != Z_OK)+ int ret;++ /* We have data for zlib, but we must check that zlib+ * has somewhere to put the results. It doesn't matter+ * if we don't expect any results -- it may be the input+ * data is just the LZ end code.+ */+ if (!(png_ptr->zstream.avail_out > 0)) {- if (ret == Z_STREAM_END)- {- if (png_ptr->zstream.avail_in)- png_error(png_ptr, "Extra compressed data");+ png_ptr->zstream.avail_out =+ (uInt) PNG_ROWBYTES(png_ptr->pixel_depth,+ png_ptr->iwidth) + 1;+ png_ptr->zstream.next_out = png_ptr->row_buf;+ }- if (!(png_ptr->zstream.avail_out))- {- png_push_process_row(png_ptr);- }+ /* Using Z_SYNC_FLUSH here means that an unterminated+ * LZ stream can still be handled (a stream with a missing+ * end code), otherwise (Z_NO_FLUSH) a future zlib+ * implementation might defer output and, therefore,+ * change the current behavior. (See comments in inflate.c+ * for why this doesn't happen at present with zlib 1.2.5.)+ */+ ret = inflate(&png_ptr->zstream, Z_SYNC_FLUSH);- png_ptr->mode |= PNG_AFTER_IDAT;- png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;- break;- }- else if (ret == Z_BUF_ERROR)- break;+ /* Check for any failure before proceeding. */+ if (ret != Z_OK && ret != Z_STREAM_END)+ {+ /* Terminate the decompression. */+ png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;++ /* This may be a truncated stream (missing or+ * damaged end code). Treat that as a warning.+ */+ if (png_ptr->row_number >= png_ptr->num_rows ||+ png_ptr->pass > 6)+ png_warning(png_ptr, "Truncated compressed data in IDAT");+ else+ png_error(png_ptr, "Decompression error in IDAT");- else- png_error(png_ptr, "Decompression Error");+ /* Skip the check on unprocessed input */+ return; }- if (!(png_ptr->zstream.avail_out))++ /* Did inflate output any data? */+ if (png_ptr->zstream.next_out != png_ptr->row_buf) {- if ((-#ifdef PNG_READ_INTERLACING_SUPPORTED- png_ptr->interlaced && png_ptr->pass > 6) ||- (!png_ptr->interlaced &&-#endif- png_ptr->row_number == png_ptr->num_rows))+ /* Is this unexpected data after the last row?+ * If it is, artificially terminate the LZ output+ * here.+ */+ if (png_ptr->row_number >= png_ptr->num_rows ||+ png_ptr->pass > 6) {- if (png_ptr->zstream.avail_in)- png_warning(png_ptr, "Too much data in IDAT chunks");- png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;- break;- }- png_push_process_row(png_ptr);- png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes;- png_ptr->zstream.next_out = png_ptr->row_buf;+ /* Extra data. */+ png_warning(png_ptr, "Extra compressed data in IDAT");+ png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;+ /* Do no more processing; skip the unprocessed+ * input check below.+ */+ return;+ }++ /* Do we have a complete row? */+ if (png_ptr->zstream.avail_out == 0)+ png_push_process_row(png_ptr); }- else- break;+ /* And check for the end of the stream. */+ if (ret == Z_STREAM_END)+ png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; }++ /* All the data should have been processed, if anything+ * is left at this point we have bytes of IDAT data+ * after the zlib end code.+ */+ if (png_ptr->zstream.avail_in > 0)+ png_warning(png_ptr, "Extra compression data"); } void /* PRIVATE */