diff mbox

[FFmpeg-devel] mpegpicture: use coded_width/coded_height to allocate frame

Message ID 75b43580-088b-3d28-8552-f5b64386f83d@googlemail.com
State Superseded
Headers show

Commit Message

Andreas Cadhalpun Nov. 7, 2016, 9:32 p.m. UTC
This fixes a heap-buffer-overflow in ff_er_frame_end when decoding mss2 with
coded_width/coded_height larger than width/height.

Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
---
 libavcodec/mpegpicture.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

Comments

Andreas Cadhalpun Nov. 7, 2016, 10:48 p.m. UTC | #1
On 07.11.2016 22:52, Luca Barbato wrote:
> On 07/11/2016 22:32, Andreas Cadhalpun wrote:
>> This fixes a heap-buffer-overflow in ff_er_frame_end when decoding mss2 with
>> coded_width/coded_height larger than width/height.
>>
>> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
>> ---
>>  libavcodec/mpegpicture.c | 12 ++++++------
>>  1 file changed, 6 insertions(+), 6 deletions(-)
>>
> 
> Do you have a sample to look at the output in that specific case?

Yes, and the output looks similar to most fuzzed samples: like garbage.

Best regards,
Andreas
Andreas Cadhalpun Nov. 22, 2016, 10:20 p.m. UTC | #2
On 07.11.2016 22:32, Andreas Cadhalpun wrote:
> This fixes a heap-buffer-overflow in ff_er_frame_end when decoding mss2 with
> coded_width/coded_height larger than width/height.
> 
> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
> ---
>  libavcodec/mpegpicture.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)
> 
> diff --git a/libavcodec/mpegpicture.c b/libavcodec/mpegpicture.c
> index 6748fc2..70b4d3c 100644
> --- a/libavcodec/mpegpicture.c
> +++ b/libavcodec/mpegpicture.c
> @@ -108,15 +108,15 @@ static int alloc_frame_buffer(AVCodecContext *avctx,  Picture *pic,
>          avctx->codec_id != AV_CODEC_ID_VC1IMAGE  &&
>          avctx->codec_id != AV_CODEC_ID_MSS2) {
>          if (edges_needed) {
> -            pic->f->width  = avctx->width  + 2 * EDGE_WIDTH;
> -            pic->f->height = avctx->height + 2 * EDGE_WIDTH;
> +            pic->f->width  = avctx->coded_width  + 2 * EDGE_WIDTH;
> +            pic->f->height = avctx->coded_height + 2 * EDGE_WIDTH;
>          }
>  
>          r = ff_thread_get_buffer(avctx, &pic->tf,
>                                   pic->reference ? AV_GET_BUFFER_FLAG_REF : 0);
>      } else {
> -        pic->f->width  = avctx->width;
> -        pic->f->height = avctx->height;
> +        pic->f->width  = avctx->coded_width;
> +        pic->f->height = avctx->coded_height;
>          pic->f->format = avctx->pix_fmt;
>          r = avcodec_default_get_buffer2(avctx, pic->f, 0);
>      }
> @@ -135,8 +135,8 @@ static int alloc_frame_buffer(AVCodecContext *avctx,  Picture *pic,
>                           (EDGE_WIDTH >> (i ? chroma_x_shift : 0));
>              pic->f->data[i] += offset;
>          }
> -        pic->f->width  = avctx->width;
> -        pic->f->height = avctx->height;
> +        pic->f->width  = avctx->coded_width;
> +        pic->f->height = avctx->coded_height;
>      }
>  
>      if (avctx->hwaccel) {
> 

Ping. It would be good to have this fixed in 3.2.1.

Best regards,
Andreas
Michael Niedermayer Nov. 23, 2016, 2:01 p.m. UTC | #3
On Mon, Nov 07, 2016 at 10:32:29PM +0100, Andreas Cadhalpun wrote:
> This fixes a heap-buffer-overflow in ff_er_frame_end when decoding mss2 with
> coded_width/coded_height larger than width/height.
> 
> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
> ---
>  libavcodec/mpegpicture.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)
> 
> diff --git a/libavcodec/mpegpicture.c b/libavcodec/mpegpicture.c
> index 6748fc2..70b4d3c 100644
> --- a/libavcodec/mpegpicture.c
> +++ b/libavcodec/mpegpicture.c
> @@ -108,15 +108,15 @@ static int alloc_frame_buffer(AVCodecContext *avctx,  Picture *pic,
>          avctx->codec_id != AV_CODEC_ID_VC1IMAGE  &&
>          avctx->codec_id != AV_CODEC_ID_MSS2) {
>          if (edges_needed) {
> -            pic->f->width  = avctx->width  + 2 * EDGE_WIDTH;
> -            pic->f->height = avctx->height + 2 * EDGE_WIDTH;
> +            pic->f->width  = avctx->coded_width  + 2 * EDGE_WIDTH;
> +            pic->f->height = avctx->coded_height + 2 * EDGE_WIDTH;
>          }
>  
>          r = ff_thread_get_buffer(avctx, &pic->tf,
>                                   pic->reference ? AV_GET_BUFFER_FLAG_REF : 0);
>      } else {
> -        pic->f->width  = avctx->width;
> -        pic->f->height = avctx->height;
> +        pic->f->width  = avctx->coded_width;
> +        pic->f->height = avctx->coded_height;
>          pic->f->format = avctx->pix_fmt;
>          r = avcodec_default_get_buffer2(avctx, pic->f, 0);
>      }
> @@ -135,8 +135,8 @@ static int alloc_frame_buffer(AVCodecContext *avctx,  Picture *pic,
>                           (EDGE_WIDTH >> (i ? chroma_x_shift : 0));
>              pic->f->data[i] += offset;
>          }
> -        pic->f->width  = avctx->width;
> -        pic->f->height = avctx->height;
> +        pic->f->width  = avctx->coded_width;
> +        pic->f->height = avctx->coded_height;
>      }

why would the error concealment code need a differently sized output
than the decoder itself ?

[...]
Andreas Cadhalpun Nov. 24, 2016, 1:14 a.m. UTC | #4
On 23.11.2016 15:01, Michael Niedermayer wrote:
> On Mon, Nov 07, 2016 at 10:32:29PM +0100, Andreas Cadhalpun wrote:
>> This fixes a heap-buffer-overflow in ff_er_frame_end when decoding mss2 with
>> coded_width/coded_height larger than width/height.
>>
>> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
>> ---
>>  libavcodec/mpegpicture.c | 12 ++++++------
>>  1 file changed, 6 insertions(+), 6 deletions(-)
>>
>> diff --git a/libavcodec/mpegpicture.c b/libavcodec/mpegpicture.c
>> index 6748fc2..70b4d3c 100644
>> --- a/libavcodec/mpegpicture.c
>> +++ b/libavcodec/mpegpicture.c
>> @@ -108,15 +108,15 @@ static int alloc_frame_buffer(AVCodecContext *avctx,  Picture *pic,
>>          avctx->codec_id != AV_CODEC_ID_VC1IMAGE  &&
>>          avctx->codec_id != AV_CODEC_ID_MSS2) {
>>          if (edges_needed) {
>> -            pic->f->width  = avctx->width  + 2 * EDGE_WIDTH;
>> -            pic->f->height = avctx->height + 2 * EDGE_WIDTH;
>> +            pic->f->width  = avctx->coded_width  + 2 * EDGE_WIDTH;
>> +            pic->f->height = avctx->coded_height + 2 * EDGE_WIDTH;
>>          }
>>  
>>          r = ff_thread_get_buffer(avctx, &pic->tf,
>>                                   pic->reference ? AV_GET_BUFFER_FLAG_REF : 0);
>>      } else {
>> -        pic->f->width  = avctx->width;
>> -        pic->f->height = avctx->height;
>> +        pic->f->width  = avctx->coded_width;
>> +        pic->f->height = avctx->coded_height;
>>          pic->f->format = avctx->pix_fmt;
>>          r = avcodec_default_get_buffer2(avctx, pic->f, 0);
>>      }
>> @@ -135,8 +135,8 @@ static int alloc_frame_buffer(AVCodecContext *avctx,  Picture *pic,
>>                           (EDGE_WIDTH >> (i ? chroma_x_shift : 0));
>>              pic->f->data[i] += offset;
>>          }
>> -        pic->f->width  = avctx->width;
>> -        pic->f->height = avctx->height;
>> +        pic->f->width  = avctx->coded_width;
>> +        pic->f->height = avctx->coded_height;
>>      }
> 
> why would the error concealment code need a differently sized output
> than the decoder itself ?

This code is rather convoluted, so it's not quite obvious, however the
needed buffer size for the error concealment is determined by the
MpegEncContext.width/height, which is set via:
mss2_decode_init
|-> wmv9_init
    |-> ff_msmpeg4_decode_init
        |-> ff_h263_decode_init
            |-> ff_mpv_decode_init:
    s->width           = avctx->coded_width;
    s->height          = avctx->coded_height;

Thus the buffer needs the same size.

Best regards,
Andreas
Michael Niedermayer Nov. 24, 2016, 4:45 p.m. UTC | #5
On Thu, Nov 24, 2016 at 02:14:59AM +0100, Andreas Cadhalpun wrote:
> On 23.11.2016 15:01, Michael Niedermayer wrote:
> > On Mon, Nov 07, 2016 at 10:32:29PM +0100, Andreas Cadhalpun wrote:
> >> This fixes a heap-buffer-overflow in ff_er_frame_end when decoding mss2 with
> >> coded_width/coded_height larger than width/height.
> >>
> >> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
> >> ---
> >>  libavcodec/mpegpicture.c | 12 ++++++------
> >>  1 file changed, 6 insertions(+), 6 deletions(-)
> >>
> >> diff --git a/libavcodec/mpegpicture.c b/libavcodec/mpegpicture.c
> >> index 6748fc2..70b4d3c 100644
> >> --- a/libavcodec/mpegpicture.c
> >> +++ b/libavcodec/mpegpicture.c
> >> @@ -108,15 +108,15 @@ static int alloc_frame_buffer(AVCodecContext *avctx,  Picture *pic,
> >>          avctx->codec_id != AV_CODEC_ID_VC1IMAGE  &&
> >>          avctx->codec_id != AV_CODEC_ID_MSS2) {
> >>          if (edges_needed) {
> >> -            pic->f->width  = avctx->width  + 2 * EDGE_WIDTH;
> >> -            pic->f->height = avctx->height + 2 * EDGE_WIDTH;
> >> +            pic->f->width  = avctx->coded_width  + 2 * EDGE_WIDTH;
> >> +            pic->f->height = avctx->coded_height + 2 * EDGE_WIDTH;
> >>          }
> >>  
> >>          r = ff_thread_get_buffer(avctx, &pic->tf,
> >>                                   pic->reference ? AV_GET_BUFFER_FLAG_REF : 0);
> >>      } else {
> >> -        pic->f->width  = avctx->width;
> >> -        pic->f->height = avctx->height;
> >> +        pic->f->width  = avctx->coded_width;
> >> +        pic->f->height = avctx->coded_height;
> >>          pic->f->format = avctx->pix_fmt;
> >>          r = avcodec_default_get_buffer2(avctx, pic->f, 0);
> >>      }
> >> @@ -135,8 +135,8 @@ static int alloc_frame_buffer(AVCodecContext *avctx,  Picture *pic,
> >>                           (EDGE_WIDTH >> (i ? chroma_x_shift : 0));
> >>              pic->f->data[i] += offset;
> >>          }
> >> -        pic->f->width  = avctx->width;
> >> -        pic->f->height = avctx->height;
> >> +        pic->f->width  = avctx->coded_width;
> >> +        pic->f->height = avctx->coded_height;
> >>      }
> > 
> > why would the error concealment code need a differently sized output
> > than the decoder itself ?
> 
> This code is rather convoluted, so it's not quite obvious, however the
> needed buffer size for the error concealment is determined by the
> MpegEncContext.width/height, which is set via:
> mss2_decode_init
> |-> wmv9_init
>     |-> ff_msmpeg4_decode_init
>         |-> ff_h263_decode_init
>             |-> ff_mpv_decode_init:
>     s->width           = avctx->coded_width;
>     s->height          = avctx->coded_height;
> 
> Thus the buffer needs the same size.

Is it correct that your cases uses
decode_wmv9() -> vc1_decode_i_blocks() ?
these decode a rectangele up to end_mb_y, end_mb_x
does this mismatch with what later code accesses ?

would using end_mb_* in the EC code fix this ? (or disabling EC if
they mismatch)

[...]
Michael Niedermayer Nov. 24, 2016, 4:57 p.m. UTC | #6
On Thu, Nov 24, 2016 at 05:45:38PM +0100, Michael Niedermayer wrote:
> On Thu, Nov 24, 2016 at 02:14:59AM +0100, Andreas Cadhalpun wrote:
> > On 23.11.2016 15:01, Michael Niedermayer wrote:
> > > On Mon, Nov 07, 2016 at 10:32:29PM +0100, Andreas Cadhalpun wrote:
> > >> This fixes a heap-buffer-overflow in ff_er_frame_end when decoding mss2 with
> > >> coded_width/coded_height larger than width/height.
> > >>
> > >> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
> > >> ---
> > >>  libavcodec/mpegpicture.c | 12 ++++++------
> > >>  1 file changed, 6 insertions(+), 6 deletions(-)
> > >>
> > >> diff --git a/libavcodec/mpegpicture.c b/libavcodec/mpegpicture.c
> > >> index 6748fc2..70b4d3c 100644
> > >> --- a/libavcodec/mpegpicture.c
> > >> +++ b/libavcodec/mpegpicture.c
> > >> @@ -108,15 +108,15 @@ static int alloc_frame_buffer(AVCodecContext *avctx,  Picture *pic,
> > >>          avctx->codec_id != AV_CODEC_ID_VC1IMAGE  &&
> > >>          avctx->codec_id != AV_CODEC_ID_MSS2) {
> > >>          if (edges_needed) {
> > >> -            pic->f->width  = avctx->width  + 2 * EDGE_WIDTH;
> > >> -            pic->f->height = avctx->height + 2 * EDGE_WIDTH;
> > >> +            pic->f->width  = avctx->coded_width  + 2 * EDGE_WIDTH;
> > >> +            pic->f->height = avctx->coded_height + 2 * EDGE_WIDTH;
> > >>          }
> > >>  
> > >>          r = ff_thread_get_buffer(avctx, &pic->tf,
> > >>                                   pic->reference ? AV_GET_BUFFER_FLAG_REF : 0);
> > >>      } else {
> > >> -        pic->f->width  = avctx->width;
> > >> -        pic->f->height = avctx->height;
> > >> +        pic->f->width  = avctx->coded_width;
> > >> +        pic->f->height = avctx->coded_height;
> > >>          pic->f->format = avctx->pix_fmt;
> > >>          r = avcodec_default_get_buffer2(avctx, pic->f, 0);
> > >>      }
> > >> @@ -135,8 +135,8 @@ static int alloc_frame_buffer(AVCodecContext *avctx,  Picture *pic,
> > >>                           (EDGE_WIDTH >> (i ? chroma_x_shift : 0));
> > >>              pic->f->data[i] += offset;
> > >>          }
> > >> -        pic->f->width  = avctx->width;
> > >> -        pic->f->height = avctx->height;
> > >> +        pic->f->width  = avctx->coded_width;
> > >> +        pic->f->height = avctx->coded_height;
> > >>      }
> > > 
> > > why would the error concealment code need a differently sized output
> > > than the decoder itself ?
> > 
> > This code is rather convoluted, so it's not quite obvious, however the
> > needed buffer size for the error concealment is determined by the
> > MpegEncContext.width/height, which is set via:
> > mss2_decode_init
> > |-> wmv9_init
> >     |-> ff_msmpeg4_decode_init
> >         |-> ff_h263_decode_init
> >             |-> ff_mpv_decode_init:
> >     s->width           = avctx->coded_width;
> >     s->height          = avctx->coded_height;
> > 
> > Thus the buffer needs the same size.
> 
> Is it correct that your cases uses
> decode_wmv9() -> vc1_decode_i_blocks() ?
> these decode a rectangele up to end_mb_y, end_mb_x
> does this mismatch with what later code accesses ?
> 

> would using end_mb_* in the EC code fix this ? (or disabling EC if
> they mismatch)

Note, for this sadly end_mb_* in MSS2 would need to be treated
differently than other codecs as it has different semantics
disabling EC on end_mb_ mismatch might be easier

[...]
diff mbox

Patch

diff --git a/libavcodec/mpegpicture.c b/libavcodec/mpegpicture.c
index 6748fc2..70b4d3c 100644
--- a/libavcodec/mpegpicture.c
+++ b/libavcodec/mpegpicture.c
@@ -108,15 +108,15 @@  static int alloc_frame_buffer(AVCodecContext *avctx,  Picture *pic,
         avctx->codec_id != AV_CODEC_ID_VC1IMAGE  &&
         avctx->codec_id != AV_CODEC_ID_MSS2) {
         if (edges_needed) {
-            pic->f->width  = avctx->width  + 2 * EDGE_WIDTH;
-            pic->f->height = avctx->height + 2 * EDGE_WIDTH;
+            pic->f->width  = avctx->coded_width  + 2 * EDGE_WIDTH;
+            pic->f->height = avctx->coded_height + 2 * EDGE_WIDTH;
         }
 
         r = ff_thread_get_buffer(avctx, &pic->tf,
                                  pic->reference ? AV_GET_BUFFER_FLAG_REF : 0);
     } else {
-        pic->f->width  = avctx->width;
-        pic->f->height = avctx->height;
+        pic->f->width  = avctx->coded_width;
+        pic->f->height = avctx->coded_height;
         pic->f->format = avctx->pix_fmt;
         r = avcodec_default_get_buffer2(avctx, pic->f, 0);
     }
@@ -135,8 +135,8 @@  static int alloc_frame_buffer(AVCodecContext *avctx,  Picture *pic,
                          (EDGE_WIDTH >> (i ? chroma_x_shift : 0));
             pic->f->data[i] += offset;
         }
-        pic->f->width  = avctx->width;
-        pic->f->height = avctx->height;
+        pic->f->width  = avctx->coded_width;
+        pic->f->height = avctx->coded_height;
     }
 
     if (avctx->hwaccel) {