diff mbox series

[FFmpeg-devel] cbs_sei: Detect payload overflows when reading SEI messages

Message ID 183412cf-2869-b70c-ef0f-170e4315d07a@jkqxz.net
State New
Headers show
Series [FFmpeg-devel] cbs_sei: Detect payload overflows when reading SEI messages
Related show

Checks

Context Check Description
andriy/configure warning Failed to apply patch
andriy/configure warning Failed to apply patch

Commit Message

Mark Thompson Feb. 2, 2021, 8:58 p.m. UTC
The top-level GetBitContext is sized for the whole NAL unit, so it fails
to detect overflows where a payload continues into the following message.
To fix that, we make a new context on the stack for reading each payload.
---
On 01/02/2021 22:31, Michael Niedermayer wrote:
> Fixes: Timeout
> Fixes: 29892/clusterfuzz-testcase-minimized-ffmpeg_BSF_H264_REDUNDANT_PPS_fuzzer-6310830956216320
> 
> Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
> Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
> ---
>   libavcodec/cbs_sei_syntax_template.c | 2 ++
>   1 file changed, 2 insertions(+)
> 
> diff --git a/libavcodec/cbs_sei_syntax_template.c b/libavcodec/cbs_sei_syntax_template.c
> index 9114e61ff6..3b9bc942f5 100644
> --- a/libavcodec/cbs_sei_syntax_template.c
> +++ b/libavcodec/cbs_sei_syntax_template.c
> @@ -178,6 +178,8 @@ static int FUNC(message)(CodedBitstreamContext *ctx, RWContext *rw,
>               GetBitContext tmp = *rw;
>               int trailing_bits, trailing_zero_bits;
>   
> +            if (8 * current->payload_size < bits_written)
> +                return AVERROR_INVALIDDATA;
>               bits_left = 8 * current->payload_size - bits_written;
>               if (bits_left > 8)
>                   skip_bits_long(&tmp, bits_left - 8);
> 
So it looks like the actual problem is that we don't detect payload overflow, so the calculation here underflows if the payload is invalid such that we read more bits than there actually are.

How about this answer, which tries to fix the general problem by detecting overflow properly - does it fix your fuzzed case?

Thanks,

- Mark


  libavcodec/cbs_sei_syntax_template.c | 17 ++++++++++++++++-
  1 file changed, 16 insertions(+), 1 deletion(-)

Comments

Mark Thompson Feb. 20, 2021, 8:46 p.m. UTC | #1
On 02/02/2021 20:58, Mark Thompson wrote:
> The top-level GetBitContext is sized for the whole NAL unit, so it fails
> to detect overflows where a payload continues into the following message.
> To fix that, we make a new context on the stack for reading each payload.
> ---
> On 01/02/2021 22:31, Michael Niedermayer wrote:
>> Fixes: Timeout
>> Fixes: 29892/clusterfuzz-testcase-minimized-ffmpeg_BSF_H264_REDUNDANT_PPS_fuzzer-6310830956216320
>>
>> Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
>> Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
>> ---
>>   libavcodec/cbs_sei_syntax_template.c | 2 ++
>>   1 file changed, 2 insertions(+)
>>
>> diff --git a/libavcodec/cbs_sei_syntax_template.c b/libavcodec/cbs_sei_syntax_template.c
>> index 9114e61ff6..3b9bc942f5 100644
>> --- a/libavcodec/cbs_sei_syntax_template.c
>> +++ b/libavcodec/cbs_sei_syntax_template.c
>> @@ -178,6 +178,8 @@ static int FUNC(message)(CodedBitstreamContext *ctx, RWContext *rw,
>>               GetBitContext tmp = *rw;
>>               int trailing_bits, trailing_zero_bits;
>> +            if (8 * current->payload_size < bits_written)
>> +                return AVERROR_INVALIDDATA;
>>               bits_left = 8 * current->payload_size - bits_written;
>>               if (bits_left > 8)
>>                   skip_bits_long(&tmp, bits_left - 8);
>>
> So it looks like the actual problem is that we don't detect payload overflow, so the calculation here underflows if the payload is invalid such that we read more bits than there actually are.
> 
> How about this answer, which tries to fix the general problem by detecting overflow properly - does it fix your fuzzed case?
> 
> Thanks,
> 
> - Mark
> 
> 
>   libavcodec/cbs_sei_syntax_template.c | 17 ++++++++++++++++-
>   1 file changed, 16 insertions(+), 1 deletion(-)
> 
> diff --git a/libavcodec/cbs_sei_syntax_template.c b/libavcodec/cbs_sei_syntax_template.c
> index 9114e61ff6..0ef7b42ed9 100644
> --- a/libavcodec/cbs_sei_syntax_template.c
> +++ b/libavcodec/cbs_sei_syntax_template.c
> @@ -238,6 +238,7 @@ static int FUNC(message_list)(CodedBitstreamContext *ctx, RWContext *rw,
>           uint32_t payload_type = 0;
>           uint32_t payload_size = 0;
>           uint32_t tmp;
> +        GetBitContext payload_gbc;
> 
>           while (show_bits(rw, 8) == 0xff) {
>               fixed(8, ff_byte, 0xff);
> @@ -253,13 +254,27 @@ static int FUNC(message_list)(CodedBitstreamContext *ctx, RWContext *rw,
>           xu(8, last_payload_size_byte, tmp, 0, 254, 0);
>           payload_size += tmp;
> 
> +        // There must be space remaining for both the payload and
> +        // the trailing bits on the SEI NAL unit.
> +        if (payload_size + 1 > get_bits_left(rw) / 8) {
> +            av_log(ctx->log_ctx, AV_LOG_ERROR,
> +                   "Invalid SEI message: payload_size too large "
> +                   "(%"PRIu32" bytes).\n", payload_size);
> +            return AVERROR_INVALIDDATA;
> +        }
> +        CHECK(init_get_bits(&payload_gbc, rw->buffer,
> +                            get_bits_count(rw) + 8 * payload_size));
> +        skip_bits_long(&payload_gbc, get_bits_count(rw));
> +
>           CHECK(ff_cbs_sei_list_add(current));
>           message = &current->messages[k];
> 
>           message->payload_type = payload_type;
>           message->payload_size = payload_size;
> 
> -        CHECK(FUNC(message)(ctx, rw, message));
> +        CHECK(FUNC(message)(ctx, &payload_gbc, message));
> +
> +        skip_bits_long(rw, 8 * payload_size);
> 
>           if (!cbs_h2645_read_more_rbsp_data(rw))
>               break;

Ping.

- Mark
Michael Niedermayer Feb. 21, 2021, 8:58 p.m. UTC | #2
On Tue, Feb 02, 2021 at 08:58:11PM +0000, Mark Thompson wrote:
> The top-level GetBitContext is sized for the whole NAL unit, so it fails
> to detect overflows where a payload continues into the following message.
> To fix that, we make a new context on the stack for reading each payload.
> ---
> On 01/02/2021 22:31, Michael Niedermayer wrote:
> > Fixes: Timeout
> > Fixes: 29892/clusterfuzz-testcase-minimized-ffmpeg_BSF_H264_REDUNDANT_PPS_fuzzer-6310830956216320
> > 
> > Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
> > Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
> > ---
> >   libavcodec/cbs_sei_syntax_template.c | 2 ++
> >   1 file changed, 2 insertions(+)
> > 
> > diff --git a/libavcodec/cbs_sei_syntax_template.c b/libavcodec/cbs_sei_syntax_template.c
> > index 9114e61ff6..3b9bc942f5 100644
> > --- a/libavcodec/cbs_sei_syntax_template.c
> > +++ b/libavcodec/cbs_sei_syntax_template.c
> > @@ -178,6 +178,8 @@ static int FUNC(message)(CodedBitstreamContext *ctx, RWContext *rw,
> >               GetBitContext tmp = *rw;
> >               int trailing_bits, trailing_zero_bits;
> > +            if (8 * current->payload_size < bits_written)
> > +                return AVERROR_INVALIDDATA;
> >               bits_left = 8 * current->payload_size - bits_written;
> >               if (bits_left > 8)
> >                   skip_bits_long(&tmp, bits_left - 8);
> > 
> So it looks like the actual problem is that we don't detect payload overflow, so the calculation here underflows if the payload is invalid such that we read more bits than there actually are.
> 
> How about this answer, which tries to fix the general problem by detecting overflow properly - 

> does it fix your fuzzed case?

yes

thanks

[...]
diff mbox series

Patch

diff --git a/libavcodec/cbs_sei_syntax_template.c b/libavcodec/cbs_sei_syntax_template.c
index 9114e61ff6..0ef7b42ed9 100644
--- a/libavcodec/cbs_sei_syntax_template.c
+++ b/libavcodec/cbs_sei_syntax_template.c
@@ -238,6 +238,7 @@  static int FUNC(message_list)(CodedBitstreamContext *ctx, RWContext *rw,
          uint32_t payload_type = 0;
          uint32_t payload_size = 0;
          uint32_t tmp;
+        GetBitContext payload_gbc;

          while (show_bits(rw, 8) == 0xff) {
              fixed(8, ff_byte, 0xff);
@@ -253,13 +254,27 @@  static int FUNC(message_list)(CodedBitstreamContext *ctx, RWContext *rw,
          xu(8, last_payload_size_byte, tmp, 0, 254, 0);
          payload_size += tmp;

+        // There must be space remaining for both the payload and
+        // the trailing bits on the SEI NAL unit.
+        if (payload_size + 1 > get_bits_left(rw) / 8) {
+            av_log(ctx->log_ctx, AV_LOG_ERROR,
+                   "Invalid SEI message: payload_size too large "
+                   "(%"PRIu32" bytes).\n", payload_size);
+            return AVERROR_INVALIDDATA;
+        }
+        CHECK(init_get_bits(&payload_gbc, rw->buffer,
+                            get_bits_count(rw) + 8 * payload_size));
+        skip_bits_long(&payload_gbc, get_bits_count(rw));
+
          CHECK(ff_cbs_sei_list_add(current));
          message = &current->messages[k];

          message->payload_type = payload_type;
          message->payload_size = payload_size;

-        CHECK(FUNC(message)(ctx, rw, message));
+        CHECK(FUNC(message)(ctx, &payload_gbc, message));
+
+        skip_bits_long(rw, 8 * payload_size);

          if (!cbs_h2645_read_more_rbsp_data(rw))
              break;