By default now, if AV_EF_CRCCHECK or AV_EF_IGNORE_ERR are enabled the decoder
will skip the chunk and carry on with the next one. This should make the       
decoder able to decode more corrupt files because the functions which decode
individual chunks will very likely error out if fed invalid data and stop the
decoding of the entire image.
Should this be made default? CRC verification doesn't take long even for very
large files.                                                      
Also fix the length check for chunk size. It needs to take into account the
4 byte tag as well as the 4 byte CRC.

>From 7aff99d12faf557753c5ee860a9672c7a09a26e3 Mon Sep 17 00:00:00 2001
From: Lynne <d...@lynne.ee>
Date: Thu, 7 Mar 2019 18:15:23 +0000
Subject: [PATCH] pngdec: add ability to check chunk CRC

By default now, if AV_EF_CRCCHECK or AV_EF_IGNORE_ERR are enabled the decoder
will skip the chunk and carry on with the next one. This should make the
decoder able to decode more corrupt files because the functions which decode
individual chunks will very likely error out if fed invalid data and stop the
decoding of the entire image.
Should this be made default? CRC verification doesn't take long even for very
large files.
Also fix the length check for chunk size. It needs to take into account the
4 byte tag as well as the 4 byte CRC.
---
 libavcodec/pngdec.c | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c
index 189bb9a4c1..30b6dc3166 100644
--- a/libavcodec/pngdec.c
+++ b/libavcodec/pngdec.c
@@ -23,6 +23,7 @@
 
 #include "libavutil/avassert.h"
 #include "libavutil/bprint.h"
+#include "libavutil/crc.h"
 #include "libavutil/imgutils.h"
 #include "libavutil/stereo3d.h"
 #include "libavutil/mastering_display_metadata.h"
@@ -1169,6 +1170,7 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s,
 static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s,
                                AVFrame *p, AVPacket *avpkt)
 {
+    const AVCRC *crc_tab = av_crc_get_table(AV_CRC_32_IEEE_LE);
     AVDictionary **metadatap = NULL;
     uint32_t tag, length;
     int decode_next_dat = 0;
@@ -1198,11 +1200,26 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s,
         }
 
         length = bytestream2_get_be32(&s->gb);
-        if (length > 0x7fffffff || length > bytestream2_get_bytes_left(&s->gb)) {
+        if (length > 0x7fffffff || (length + 8) > bytestream2_get_bytes_left(&s->gb)) {
             av_log(avctx, AV_LOG_ERROR, "chunk too big\n");
             ret = AVERROR_INVALIDDATA;
             goto fail;
         }
+        if (avctx->err_recognition & (AV_EF_CRCCHECK | AV_EF_IGNORE_ERR)) {
+            uint32_t crc_sig = AV_RB32(s->gb.buffer + length + 4);
+            uint32_t crc_cal = ~av_crc(crc_tab, UINT32_MAX, s->gb.buffer, length + 4);
+            if (crc_sig ^ crc_cal) {
+                av_log(avctx, AV_LOG_ERROR, "CRC mismatch in chunk");
+                if (avctx->err_recognition & AV_EF_EXPLODE) {
+                    av_log(avctx, AV_LOG_ERROR, ", quitting\n");
+                    ret = AVERROR_INVALIDDATA;
+                    goto fail;
+                }
+                av_log(avctx, AV_LOG_ERROR, ", skipping\n");
+                bytestream2_skip(&s->gb, 4); /* tag */
+                goto skip_tag;
+            }
+        }
         tag = bytestream2_get_le32(&s->gb);
         if (avctx->debug & FF_DEBUG_STARTCODE)
             av_log(avctx, AV_LOG_DEBUG, "png: tag=%s length=%u\n",
-- 
2.21.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to