diff mbox

[FFmpeg-devel,2/2] pngdec: decode and expose iCCP chunks as side data

Message ID 20170720204622.27337-2-atomnuker@gmail.com
State New
Headers show

Commit Message

Rostislav Pehlivanov July 20, 2017, 8:46 p.m. UTC
Signed-off-by: Rostislav Pehlivanov <atomnuker@gmail.com>
---
 libavcodec/pngdec.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

Comments

Michael Niedermayer July 21, 2017, 1:11 p.m. UTC | #1
On Thu, Jul 20, 2017 at 09:46:22PM +0100, Rostislav Pehlivanov wrote:
> Signed-off-by: Rostislav Pehlivanov <atomnuker@gmail.com>
> ---
>  libavcodec/pngdec.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 45 insertions(+)
> 
> diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c
> index 083f61f4f8..64811c6fc3 100644
> --- a/libavcodec/pngdec.c
> +++ b/libavcodec/pngdec.c
> @@ -836,6 +836,46 @@ static int decode_trns_chunk(AVCodecContext *avctx, PNGDecContext *s,
>      return 0;
>  }
>  
> +static int decode_iccp_chunk(PNGDecContext *s, uint32_t length, AVFrame *f)
> +{
> +    int ret, cnt = 0;
> +    uint8_t *data, profile_name[82];
> +    AVBPrint bp;
> +    AVFrameSideData *sd;
> +
> +    while ((profile_name[cnt++] = bytestream2_get_byte(&s->gb)) && cnt < 81);
> +    if (cnt > 80) {
> +        av_log(s->avctx, AV_LOG_ERROR, "iCCP with invalid name!\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    length -= cnt;
> +
> +    if (bytestream2_get_byte(&s->gb) != 0) {
> +        av_log(s->avctx, AV_LOG_ERROR, "iCCP with invalid compression!\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    length -= 1;

length could have overflowed and become rather big from one of the 2
subtractions
the following code would then misbehave


> +
> +    if ((ret = decode_zbuf(&bp, s->gb.buffer, s->gb.buffer + length) < 0))

() is placed incorrectly


> +        return ret;
> +
> +    av_bprint_finalize(&bp, (char **)&data);
> +
> +    if (!(sd = av_frame_new_side_data(f, AV_FRAME_DATA_ICC_PROFILE, bp.len)))
> +        return AVERROR(ENOMEM);
> +
> +    av_dict_set(&sd->metadata, "name", profile_name, 0);
> +    memcpy(sd->data, data, bp.len);
> +    av_free(data);
> +
> +    /* ICC compressed data and CRC */
> +    bytestream2_skip(&s->gb, length + 4);
> +
> +    return 0;
> +}
> +
>  static void handle_small_bpp(PNGDecContext *s, AVFrame *p)
>  {
>      if (s->bits_per_pixel == 1 && s->color_type == PNG_COLOR_TYPE_PALETTE) {
> @@ -1239,6 +1279,11 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s,
>              bytestream2_skip(&s->gb, 4); /* crc */
>              break;
>          }
> +        case MKTAG('i', 'C', 'C', 'P'): {
> +            if (decode_iccp_chunk(s, length, p) < 0)
> +                goto fail;
> +            break;
> +        }
>          case MKTAG('I', 'E', 'N', 'D'):
>              if (!(s->pic_state & PNG_ALLIMAGE))
>                  av_log(avctx, AV_LOG_ERROR, "IEND without all image\n");
> -- 
> 2.14.0.rc0.284.gd933b75aa4
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Rostislav Pehlivanov July 22, 2017, 8:15 p.m. UTC | #2
On 21 July 2017 at 14:11, Michael Niedermayer <michael@niedermayer.cc>
wrote:

> On Thu, Jul 20, 2017 at 09:46:22PM +0100, Rostislav Pehlivanov wrote:
> > Signed-off-by: Rostislav Pehlivanov <atomnuker@gmail.com>
> > ---
> >  libavcodec/pngdec.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 45 insertions(+)
> >
> > diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c
> > index 083f61f4f8..64811c6fc3 100644
> > --- a/libavcodec/pngdec.c
> > +++ b/libavcodec/pngdec.c
> > @@ -836,6 +836,46 @@ static int decode_trns_chunk(AVCodecContext
> *avctx, PNGDecContext *s,
> >      return 0;
> >  }
> >
> > +static int decode_iccp_chunk(PNGDecContext *s, uint32_t length,
> AVFrame *f)
> > +{
> > +    int ret, cnt = 0;
> > +    uint8_t *data, profile_name[82];
> > +    AVBPrint bp;
> > +    AVFrameSideData *sd;
> > +
> > +    while ((profile_name[cnt++] = bytestream2_get_byte(&s->gb)) && cnt
> < 81);
> > +    if (cnt > 80) {
> > +        av_log(s->avctx, AV_LOG_ERROR, "iCCP with invalid name!\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    length -= cnt;
> > +
> > +    if (bytestream2_get_byte(&s->gb) != 0) {
> > +        av_log(s->avctx, AV_LOG_ERROR, "iCCP with invalid
> compression!\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    length -= 1;
>
> length could have overflowed and become rather big from one of the 2
> subtractions
> the following code would then misbehave
>
>
Thanks to pointing this out

Changed to:
+ length = FFMAX(length - cnt, 0);
and
+ length = FFMAX(length - 1, 0);


>
> > +
> > +    if ((ret = decode_zbuf(&bp, s->gb.buffer, s->gb.buffer + length) <
> 0))
>
> () is placed incorrectly
>
>
Changed to:
+ ret = decode_zbuf(&bp, s->gb.buffer, s->gb.buffer + length);
+ if (ret < 0)
Michael Niedermayer July 23, 2017, 12:36 p.m. UTC | #3
On Sat, Jul 22, 2017 at 09:15:53PM +0100, Rostislav Pehlivanov wrote:
> On 21 July 2017 at 14:11, Michael Niedermayer <michael@niedermayer.cc>
> wrote:
> 
> > On Thu, Jul 20, 2017 at 09:46:22PM +0100, Rostislav Pehlivanov wrote:
> > > Signed-off-by: Rostislav Pehlivanov <atomnuker@gmail.com>
> > > ---
> > >  libavcodec/pngdec.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 45 insertions(+)
> > >
> > > diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c
> > > index 083f61f4f8..64811c6fc3 100644
> > > --- a/libavcodec/pngdec.c
> > > +++ b/libavcodec/pngdec.c
> > > @@ -836,6 +836,46 @@ static int decode_trns_chunk(AVCodecContext
> > *avctx, PNGDecContext *s,
> > >      return 0;
> > >  }
> > >
> > > +static int decode_iccp_chunk(PNGDecContext *s, uint32_t length,
> > AVFrame *f)
> > > +{
> > > +    int ret, cnt = 0;
> > > +    uint8_t *data, profile_name[82];
> > > +    AVBPrint bp;
> > > +    AVFrameSideData *sd;
> > > +
> > > +    while ((profile_name[cnt++] = bytestream2_get_byte(&s->gb)) && cnt
> > < 81);
> > > +    if (cnt > 80) {
> > > +        av_log(s->avctx, AV_LOG_ERROR, "iCCP with invalid name!\n");
> > > +        return AVERROR_INVALIDDATA;
> > > +    }
> > > +
> > > +    length -= cnt;
> > > +
> > > +    if (bytestream2_get_byte(&s->gb) != 0) {
> > > +        av_log(s->avctx, AV_LOG_ERROR, "iCCP with invalid
> > compression!\n");
> > > +        return AVERROR_INVALIDDATA;
> > > +    }
> > > +
> > > +    length -= 1;
> >
> > length could have overflowed and become rather big from one of the 2
> > subtractions
> > the following code would then misbehave
> >
> >
> Thanks to pointing this out
> 
> Changed to:
> + length = FFMAX(length - cnt, 0);

if length is unsigned int and cnt signed int then length - cnt is unsigned
so the FFMAX will not work as intended


> and
> + length = FFMAX(length - 1, 0);
> 

[...]
Rostislav Pehlivanov July 25, 2017, 4:33 p.m. UTC | #4
On 23 July 2017 at 13:36, Michael Niedermayer <michael@niedermayer.cc>
wrote:

> On Sat, Jul 22, 2017 at 09:15:53PM +0100, Rostislav Pehlivanov wrote:
> > On 21 July 2017 at 14:11, Michael Niedermayer <michael@niedermayer.cc>
> > wrote:
> >
> > > On Thu, Jul 20, 2017 at 09:46:22PM +0100, Rostislav Pehlivanov wrote:
> > > > Signed-off-by: Rostislav Pehlivanov <atomnuker@gmail.com>
> > > > ---
> > > >  libavcodec/pngdec.c | 45 ++++++++++++++++++++++++++++++
> +++++++++++++++
> > > >  1 file changed, 45 insertions(+)
> > > >
> > > > diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c
> > > > index 083f61f4f8..64811c6fc3 100644
> > > > --- a/libavcodec/pngdec.c
> > > > +++ b/libavcodec/pngdec.c
> > > > @@ -836,6 +836,46 @@ static int decode_trns_chunk(AVCodecContext
> > > *avctx, PNGDecContext *s,
> > > >      return 0;
> > > >  }
> > > >
> > > > +static int decode_iccp_chunk(PNGDecContext *s, uint32_t length,
> > > AVFrame *f)
> > > > +{
> > > > +    int ret, cnt = 0;
> > > > +    uint8_t *data, profile_name[82];
> > > > +    AVBPrint bp;
> > > > +    AVFrameSideData *sd;
> > > > +
> > > > +    while ((profile_name[cnt++] = bytestream2_get_byte(&s->gb)) &&
> cnt
> > > < 81);
> > > > +    if (cnt > 80) {
> > > > +        av_log(s->avctx, AV_LOG_ERROR, "iCCP with invalid name!\n");
> > > > +        return AVERROR_INVALIDDATA;
> > > > +    }
> > > > +
> > > > +    length -= cnt;
> > > > +
> > > > +    if (bytestream2_get_byte(&s->gb) != 0) {
> > > > +        av_log(s->avctx, AV_LOG_ERROR, "iCCP with invalid
> > > compression!\n");
> > > > +        return AVERROR_INVALIDDATA;
> > > > +    }
> > > > +
> > > > +    length -= 1;
> > >
> > > length could have overflowed and become rather big from one of the 2
> > > subtractions
> > > the following code would then misbehave
> > >
> > >
> > Thanks to pointing this out
> >
> > Changed to:
> > + length = FFMAX(length - cnt, 0);
>
> if length is unsigned int and cnt signed int then length - cnt is unsigned
> so the FFMAX will not work as intended
>
>
> > and
> > + length = FFMAX(length - 1, 0);
> >
>
> [...]
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> Rewriting code that is poorly written but fully understood is good.
> Rewriting code that one doesnt understand is a sign that one is less smart
> then the original author, trying to rewrite it will not make it better.
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
>
Changed to an int as discussed on IRC

I'll give it a few more hours before I push both patches
Rostislav Pehlivanov July 25, 2017, 7:17 p.m. UTC | #5
On 25 July 2017 at 17:33, Rostislav Pehlivanov <atomnuker@gmail.com> wrote:

>
>
> On 23 July 2017 at 13:36, Michael Niedermayer <michael@niedermayer.cc>
> wrote:
>
>> On Sat, Jul 22, 2017 at 09:15:53PM +0100, Rostislav Pehlivanov wrote:
>> > On 21 July 2017 at 14:11, Michael Niedermayer <michael@niedermayer.cc>
>> > wrote:
>> >
>> > > On Thu, Jul 20, 2017 at 09:46:22PM +0100, Rostislav Pehlivanov wrote:
>> > > > Signed-off-by: Rostislav Pehlivanov <atomnuker@gmail.com>
>> > > > ---
>> > > >  libavcodec/pngdec.c | 45 ++++++++++++++++++++++++++++++
>> +++++++++++++++
>> > > >  1 file changed, 45 insertions(+)
>> > > >
>> > > > diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c
>> > > > index 083f61f4f8..64811c6fc3 100644
>> > > > --- a/libavcodec/pngdec.c
>> > > > +++ b/libavcodec/pngdec.c
>> > > > @@ -836,6 +836,46 @@ static int decode_trns_chunk(AVCodecContext
>> > > *avctx, PNGDecContext *s,
>> > > >      return 0;
>> > > >  }
>> > > >
>> > > > +static int decode_iccp_chunk(PNGDecContext *s, uint32_t length,
>> > > AVFrame *f)
>> > > > +{
>> > > > +    int ret, cnt = 0;
>> > > > +    uint8_t *data, profile_name[82];
>> > > > +    AVBPrint bp;
>> > > > +    AVFrameSideData *sd;
>> > > > +
>> > > > +    while ((profile_name[cnt++] = bytestream2_get_byte(&s->gb)) &&
>> cnt
>> > > < 81);
>> > > > +    if (cnt > 80) {
>> > > > +        av_log(s->avctx, AV_LOG_ERROR, "iCCP with invalid
>> name!\n");
>> > > > +        return AVERROR_INVALIDDATA;
>> > > > +    }
>> > > > +
>> > > > +    length -= cnt;
>> > > > +
>> > > > +    if (bytestream2_get_byte(&s->gb) != 0) {
>> > > > +        av_log(s->avctx, AV_LOG_ERROR, "iCCP with invalid
>> > > compression!\n");
>> > > > +        return AVERROR_INVALIDDATA;
>> > > > +    }
>> > > > +
>> > > > +    length -= 1;
>> > >
>> > > length could have overflowed and become rather big from one of the 2
>> > > subtractions
>> > > the following code would then misbehave
>> > >
>> > >
>> > Thanks to pointing this out
>> >
>> > Changed to:
>> > + length = FFMAX(length - cnt, 0);
>>
>> if length is unsigned int and cnt signed int then length - cnt is unsigned
>> so the FFMAX will not work as intended
>>
>>
>> > and
>> > + length = FFMAX(length - 1, 0);
>> >
>>
>> [...]
>> --
>> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>>
>> Rewriting code that is poorly written but fully understood is good.
>> Rewriting code that one doesnt understand is a sign that one is less smart
>> then the original author, trying to rewrite it will not make it better.
>>
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>>
> Changed to an int as discussed on IRC
>
> I'll give it a few more hours before I push both patches
>

Thanks for the review, pushed with your suggestions.
diff mbox

Patch

diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c
index 083f61f4f8..64811c6fc3 100644
--- a/libavcodec/pngdec.c
+++ b/libavcodec/pngdec.c
@@ -836,6 +836,46 @@  static int decode_trns_chunk(AVCodecContext *avctx, PNGDecContext *s,
     return 0;
 }
 
+static int decode_iccp_chunk(PNGDecContext *s, uint32_t length, AVFrame *f)
+{
+    int ret, cnt = 0;
+    uint8_t *data, profile_name[82];
+    AVBPrint bp;
+    AVFrameSideData *sd;
+
+    while ((profile_name[cnt++] = bytestream2_get_byte(&s->gb)) && cnt < 81);
+    if (cnt > 80) {
+        av_log(s->avctx, AV_LOG_ERROR, "iCCP with invalid name!\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    length -= cnt;
+
+    if (bytestream2_get_byte(&s->gb) != 0) {
+        av_log(s->avctx, AV_LOG_ERROR, "iCCP with invalid compression!\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    length -= 1;
+
+    if ((ret = decode_zbuf(&bp, s->gb.buffer, s->gb.buffer + length) < 0))
+        return ret;
+
+    av_bprint_finalize(&bp, (char **)&data);
+
+    if (!(sd = av_frame_new_side_data(f, AV_FRAME_DATA_ICC_PROFILE, bp.len)))
+        return AVERROR(ENOMEM);
+
+    av_dict_set(&sd->metadata, "name", profile_name, 0);
+    memcpy(sd->data, data, bp.len);
+    av_free(data);
+
+    /* ICC compressed data and CRC */
+    bytestream2_skip(&s->gb, length + 4);
+
+    return 0;
+}
+
 static void handle_small_bpp(PNGDecContext *s, AVFrame *p)
 {
     if (s->bits_per_pixel == 1 && s->color_type == PNG_COLOR_TYPE_PALETTE) {
@@ -1239,6 +1279,11 @@  static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s,
             bytestream2_skip(&s->gb, 4); /* crc */
             break;
         }
+        case MKTAG('i', 'C', 'C', 'P'): {
+            if (decode_iccp_chunk(s, length, p) < 0)
+                goto fail;
+            break;
+        }
         case MKTAG('I', 'E', 'N', 'D'):
             if (!(s->pic_state & PNG_ALLIMAGE))
                 av_log(avctx, AV_LOG_ERROR, "IEND without all image\n");