diff mbox series

[FFmpeg-devel,2/3] avcodec/cbs_h266_syntax_template: sanity check num_multi_layer_olss

Message ID 20240126214651.22783-2-michael@niedermayer.cc
State New
Headers show
Series [FFmpeg-devel,1/3] avcodec/vvc/vvcdec: Do not submit frames without VVCFrameThread | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Michael Niedermayer Jan. 26, 2024, 9:46 p.m. UTC
It is not possible to encode a index into an empty list. Thus
this must be invalid at this point or before.
Its likely a broader earlier check can be used here, someone knowing
VVC should look at that. Its not immedeatly obvious from the spec
by looking for numlayerolss

Fixes: out of array access
Fixes: 65160/clusterfuzz-testcase-minimized-ffmpeg_BSF_VVC_METADATA_fuzzer-4665241535119360

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_h266_syntax_template.c | 2 ++
 1 file changed, 2 insertions(+)

Comments

James Almer Jan. 27, 2024, 12:25 p.m. UTC | #1
On 1/26/2024 6:46 PM, Michael Niedermayer wrote:
> It is not possible to encode a index into an empty list. Thus
> this must be invalid at this point or before.
> Its likely a broader earlier check can be used here, someone knowing
> VVC should look at that. Its not immedeatly obvious from the spec
> by looking for numlayerolss

Can you check if the following fixes it?

> diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c
> index 549d021211..40572dadb5 100644
> --- a/libavcodec/cbs_h266_syntax_template.c
> +++ b/libavcodec/cbs_h266_syntax_template.c
> @@ -793,6 +793,7 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw,
>      {
>          //calc NumMultiLayerOlss
>          int m;
> +        int num_layers_in_ols = 0;
>          uint8_t dependency_flag[VVC_MAX_LAYERS][VVC_MAX_LAYERS];
>          uint16_t num_output_layers_in_ols[VVC_MAX_TOTAL_NUM_OLSS];
>          uint8_t num_sub_layers_in_layer_in_ols[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_TOTAL_NUM_OLSS];
> @@ -895,7 +896,6 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw,
>                  return AVERROR_INVALIDDATA;
>          }
>          for (i = 1; i < total_num_olss; i++) {
> -            int num_layers_in_ols = 0;
>              if (current->vps_each_layer_is_an_ols_flag) {
>                  num_layers_in_ols = 1;
>              } else if (current->vps_ols_mode_idc == 0 ||

num_layers_in_ols is not meant to be reset on every loop.
Michael Niedermayer Jan. 27, 2024, 11:56 p.m. UTC | #2
On Sat, Jan 27, 2024 at 09:25:16AM -0300, James Almer wrote:
> On 1/26/2024 6:46 PM, Michael Niedermayer wrote:
> > It is not possible to encode a index into an empty list. Thus
> > this must be invalid at this point or before.
> > Its likely a broader earlier check can be used here, someone knowing
> > VVC should look at that. Its not immedeatly obvious from the spec
> > by looking for numlayerolss
> 
> Can you check if the following fixes it?
> 
> > diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c
> > index 549d021211..40572dadb5 100644
> > --- a/libavcodec/cbs_h266_syntax_template.c
> > +++ b/libavcodec/cbs_h266_syntax_template.c
> > @@ -793,6 +793,7 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw,
> >      {
> >          //calc NumMultiLayerOlss
> >          int m;
> > +        int num_layers_in_ols = 0;
> >          uint8_t dependency_flag[VVC_MAX_LAYERS][VVC_MAX_LAYERS];
> >          uint16_t num_output_layers_in_ols[VVC_MAX_TOTAL_NUM_OLSS];
> >          uint8_t num_sub_layers_in_layer_in_ols[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_TOTAL_NUM_OLSS];
> > @@ -895,7 +896,6 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw,
> >                  return AVERROR_INVALIDDATA;
> >          }
> >          for (i = 1; i < total_num_olss; i++) {
> > -            int num_layers_in_ols = 0;
> >              if (current->vps_each_layer_is_an_ols_flag) {
> >                  num_layers_in_ols = 1;
> >              } else if (current->vps_ols_mode_idc == 0 ||
> 
> num_layers_in_ols is not meant to be reset on every loop.

replacing my patch by yours does not change
num_multi_layer_olss from being 0
and if its 0 then "num_multi_layer_olss - 1" causes problems as a limit

more precissely this:
i can also send you the file if you want?


tools/target_bsf_vvc_metadata_fuzzer: Running 1 inputs 1 time(s) each.
Running: /home/michael/libfuzz-repro/65160/clusterfuzz-testcase-minimized-ffmpeg_BSF_VVC_METADATA_fuzzer-4665241535119360
libavcodec/cbs_h266_syntax_template.c:1001:21: runtime error: index 257 out of bounds for type 'uint8_t [257]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior libavcodec/cbs_h266_syntax_template.c:1001:21 in
libavcodec/cbs_h266_syntax_template.c:1004:38: runtime error: index 257 out of bounds for type 'uint8_t [257]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior libavcodec/cbs_h266_syntax_template.c:1004:38 in
=================================================================
==29823==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7ff104e7ca18 at pc 0x0000006a4fdb bp 0x7fffd376e7f0 sp 0x7fffd376e7e8
WRITE of size 1 at 0x7ff104e7ca18 thread T0
    #0 0x6a4fda in cbs_h266_read_vps libavcodec/cbs_h266_syntax_template.c:1001:21
    #1 0x5c87bc in cbs_h266_read_nal_unit libavcodec/cbs_h2645.c:1071:19
    #2 0x4ff3aa in cbs_read_fragment_content libavcodec/cbs.c:215:15
    #3 0x4ff3aa in cbs_read_data libavcodec/cbs.c:282
    #4 0x5a53ea in ff_cbs_bsf_generic_filter libavcodec/cbs_bsf.c:75:11
    #5 0x4c9b48 in LLVMFuzzerTestOneInput tools/target_bsf_fuzzer.c:154:16
    #6 0x138eb7d in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) Fuzzer/build/../FuzzerLoop.cpp:495:13
    #7 0x1383752 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) Fuzzer/build/../FuzzerDriver.cpp:273:6
    #8 0x1388951 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) Fuzzer/build/../FuzzerDriver.cpp:690:9
    #9 0x1383430 in main Fuzzer/build/../FuzzerMain.cpp:20:10
    #10 0x7ff10889ac86 in __libc_start_main /build/glibc-WcQU6j/glibc-2.27/csu/../csu/libc-start.c:310
    #11 0x41f429 in _start (tools/target_bsf_vvc_metadata_fuzzer+0x41f429)

0x7ff104e7ca18 is located 0 bytes to the right of 393752-byte region [0x7ff104e1c800,0x7ff104e7ca18)
allocated by thread T0 here:
    #0 0x497e47 in posix_memalign /b/swarming/w/ir/cache/builder/src/third_party/llvm/compiler-rt/lib/asan/asan_malloc_linux.cc:226:3
    #1 0x130d3a8 in av_malloc libavutil/mem.c:105:9
    #2 0x8fe1c5 in ff_refstruct_alloc_ext_c libavcodec/refstruct.c:109:11
    #3 0x50c2bb in ff_cbs_alloc_unit_content libavcodec/cbs.c:933:25
    #4 0x5c864f in cbs_h266_read_nal_unit libavcodec/cbs_h2645.c:1046:11
    #5 0x4ff3aa in cbs_read_fragment_content libavcodec/cbs.c:215:15
    #6 0x4ff3aa in cbs_read_data libavcodec/cbs.c:282
    #7 0x5a53ea in ff_cbs_bsf_generic_filter libavcodec/cbs_bsf.c:75:11
    #8 0x4c9b48 in LLVMFuzzerTestOneInput tools/target_bsf_fuzzer.c:154:16
    #9 0x138eb7d in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) Fuzzer/build/../FuzzerLoop.cpp:495:13
    #10 0x1383752 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) Fuzzer/build/../FuzzerDriver.cpp:273:6
    #11 0x1388951 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) Fuzzer/build/../FuzzerDriver.cpp:690:9
    #12 0x1383430 in main Fuzzer/build/../FuzzerMain.cpp:20:10
    #13 0x7ff10889ac86 in __libc_start_main /build/glibc-WcQU6j/glibc-2.27/csu/../csu/libc-start.c:310


[...]
James Almer Jan. 28, 2024, 12:02 a.m. UTC | #3
On 1/27/2024 8:56 PM, Michael Niedermayer wrote:
> On Sat, Jan 27, 2024 at 09:25:16AM -0300, James Almer wrote:
>> On 1/26/2024 6:46 PM, Michael Niedermayer wrote:
>>> It is not possible to encode a index into an empty list. Thus
>>> this must be invalid at this point or before.
>>> Its likely a broader earlier check can be used here, someone knowing
>>> VVC should look at that. Its not immedeatly obvious from the spec
>>> by looking for numlayerolss
>>
>> Can you check if the following fixes it?
>>
>>> diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c
>>> index 549d021211..40572dadb5 100644
>>> --- a/libavcodec/cbs_h266_syntax_template.c
>>> +++ b/libavcodec/cbs_h266_syntax_template.c
>>> @@ -793,6 +793,7 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw,
>>>       {
>>>           //calc NumMultiLayerOlss
>>>           int m;
>>> +        int num_layers_in_ols = 0;
>>>           uint8_t dependency_flag[VVC_MAX_LAYERS][VVC_MAX_LAYERS];
>>>           uint16_t num_output_layers_in_ols[VVC_MAX_TOTAL_NUM_OLSS];
>>>           uint8_t num_sub_layers_in_layer_in_ols[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_TOTAL_NUM_OLSS];
>>> @@ -895,7 +896,6 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw,
>>>                   return AVERROR_INVALIDDATA;
>>>           }
>>>           for (i = 1; i < total_num_olss; i++) {
>>> -            int num_layers_in_ols = 0;
>>>               if (current->vps_each_layer_is_an_ols_flag) {
>>>                   num_layers_in_ols = 1;
>>>               } else if (current->vps_ols_mode_idc == 0 ||
>>
>> num_layers_in_ols is not meant to be reset on every loop.
> 
> replacing my patch by yours does not change
> num_multi_layer_olss from being 0
> and if its 0 then "num_multi_layer_olss - 1" causes problems as a limit
> 
> more precissely this:
> i can also send you the file if you want?

No, this should be looked at by someone more familiar with VVC.
And my patch should be applied either way. The current code is wrong.
Michael Niedermayer Jan. 28, 2024, 12:05 a.m. UTC | #4
On Sat, Jan 27, 2024 at 09:02:30PM -0300, James Almer wrote:
> On 1/27/2024 8:56 PM, Michael Niedermayer wrote:
> > On Sat, Jan 27, 2024 at 09:25:16AM -0300, James Almer wrote:
> > > On 1/26/2024 6:46 PM, Michael Niedermayer wrote:
> > > > It is not possible to encode a index into an empty list. Thus
> > > > this must be invalid at this point or before.
> > > > Its likely a broader earlier check can be used here, someone knowing
> > > > VVC should look at that. Its not immedeatly obvious from the spec
> > > > by looking for numlayerolss
> > > 
> > > Can you check if the following fixes it?
> > > 
> > > > diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c
> > > > index 549d021211..40572dadb5 100644
> > > > --- a/libavcodec/cbs_h266_syntax_template.c
> > > > +++ b/libavcodec/cbs_h266_syntax_template.c
> > > > @@ -793,6 +793,7 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw,
> > > >       {
> > > >           //calc NumMultiLayerOlss
> > > >           int m;
> > > > +        int num_layers_in_ols = 0;
> > > >           uint8_t dependency_flag[VVC_MAX_LAYERS][VVC_MAX_LAYERS];
> > > >           uint16_t num_output_layers_in_ols[VVC_MAX_TOTAL_NUM_OLSS];
> > > >           uint8_t num_sub_layers_in_layer_in_ols[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_TOTAL_NUM_OLSS];
> > > > @@ -895,7 +896,6 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw,
> > > >                   return AVERROR_INVALIDDATA;
> > > >           }
> > > >           for (i = 1; i < total_num_olss; i++) {
> > > > -            int num_layers_in_ols = 0;
> > > >               if (current->vps_each_layer_is_an_ols_flag) {
> > > >                   num_layers_in_ols = 1;
> > > >               } else if (current->vps_ols_mode_idc == 0 ||
> > > 
> > > num_layers_in_ols is not meant to be reset on every loop.
> > 
> > replacing my patch by yours does not change
> > num_multi_layer_olss from being 0
> > and if its 0 then "num_multi_layer_olss - 1" causes problems as a limit
> > 
> > more precissely this:
> > i can also send you the file if you want?
> 
> No, this should be looked at by someone more familiar with VVC.

ive already sent the fuzzer samples to nuomi and frank plowman


> And my patch should be applied either way. The current code is wrong.

I did not suggest not to do that :)
just that it alone was not enough to fix this

thx

[...]
James Almer Jan. 29, 2024, 7:04 p.m. UTC | #5
On 1/27/2024 9:05 PM, Michael Niedermayer wrote:
> On Sat, Jan 27, 2024 at 09:02:30PM -0300, James Almer wrote:
>> On 1/27/2024 8:56 PM, Michael Niedermayer wrote:
>>> On Sat, Jan 27, 2024 at 09:25:16AM -0300, James Almer wrote:
>>>> On 1/26/2024 6:46 PM, Michael Niedermayer wrote:
>>>>> It is not possible to encode a index into an empty list. Thus
>>>>> this must be invalid at this point or before.
>>>>> Its likely a broader earlier check can be used here, someone knowing
>>>>> VVC should look at that. Its not immedeatly obvious from the spec
>>>>> by looking for numlayerolss
>>>>
>>>> Can you check if the following fixes it?
>>>>
>>>>> diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c
>>>>> index 549d021211..40572dadb5 100644
>>>>> --- a/libavcodec/cbs_h266_syntax_template.c
>>>>> +++ b/libavcodec/cbs_h266_syntax_template.c
>>>>> @@ -793,6 +793,7 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw,
>>>>>        {
>>>>>            //calc NumMultiLayerOlss
>>>>>            int m;
>>>>> +        int num_layers_in_ols = 0;
>>>>>            uint8_t dependency_flag[VVC_MAX_LAYERS][VVC_MAX_LAYERS];
>>>>>            uint16_t num_output_layers_in_ols[VVC_MAX_TOTAL_NUM_OLSS];
>>>>>            uint8_t num_sub_layers_in_layer_in_ols[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_TOTAL_NUM_OLSS];
>>>>> @@ -895,7 +896,6 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw,
>>>>>                    return AVERROR_INVALIDDATA;
>>>>>            }
>>>>>            for (i = 1; i < total_num_olss; i++) {
>>>>> -            int num_layers_in_ols = 0;
>>>>>                if (current->vps_each_layer_is_an_ols_flag) {
>>>>>                    num_layers_in_ols = 1;
>>>>>                } else if (current->vps_ols_mode_idc == 0 ||
>>>>
>>>> num_layers_in_ols is not meant to be reset on every loop.
>>>
>>> replacing my patch by yours does not change
>>> num_multi_layer_olss from being 0
>>> and if its 0 then "num_multi_layer_olss - 1" causes problems as a limit
>>>
>>> more precissely this:
>>> i can also send you the file if you want?
>>
>> No, this should be looked at by someone more familiar with VVC.
> 
> ive already sent the fuzzer samples to nuomi and frank plowman
> 
> 
>> And my patch should be applied either way. The current code is wrong.
> 
> I did not suggest not to do that :)

Well, turns out the current code is fine and my suggested change above 
is wrong. Fun how that goes.

Can you test the following instead?

> diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c
> index 549d021211..30b4ae3bc0 100644
> --- a/libavcodec/cbs_h266_syntax_template.c
> +++ b/libavcodec/cbs_h266_syntax_template.c
> @@ -764,7 +764,7 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw,
>              infer(vps_each_layer_is_an_ols_flag, 0);
>          if (!current->vps_each_layer_is_an_ols_flag) {
>              if (!current->vps_all_independent_layers_flag)
> -                ub(2, vps_ols_mode_idc);
> +                u(2, vps_ols_mode_idc, 0, 2);
>              else
>                  infer(vps_ols_mode_idc, 2);
>              if (current->vps_ols_mode_idc == 2) {
> @@ -902,11 +902,10 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw,
>                         current->vps_ols_mode_idc == 1) {
>                  num_layers_in_ols = i + 1;
>              } else if (current->vps_ols_mode_idc == 2) {
> -                for (k = 0, j = 0; k <= current->vps_max_layers_minus1; k++) {
> +                for (k = 0, j = 0; k <= current->vps_max_layers_minus1; k++)
>                      if (layer_included_in_ols_flag[i][k])
>                          j++;
> -                    num_layers_in_ols = j;
> -                }
> +                num_layers_in_ols = j;
>              }
>              if (num_layers_in_ols > 1) {
>                  num_multi_layer_olss++;
Frank Plowman Jan. 29, 2024, 8:19 p.m. UTC | #6
On 29/01/2024 19:04, James Almer wrote:

>
> Well, turns out the current code is fine and my suggested change above 
> is wrong. Fun how that goes.
>
> Can you test the following instead?
>
>> diff --git a/libavcodec/cbs_h266_syntax_template.c 
>> b/libavcodec/cbs_h266_syntax_template.c
>> index 549d021211..30b4ae3bc0 100644
>> --- a/libavcodec/cbs_h266_syntax_template.c
>> +++ b/libavcodec/cbs_h266_syntax_template.c
>> @@ -764,7 +764,7 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, 
>> RWContext *rw,
>>              infer(vps_each_layer_is_an_ols_flag, 0);
>>          if (!current->vps_each_layer_is_an_ols_flag) {
>>              if (!current->vps_all_independent_layers_flag)
>> -                ub(2, vps_ols_mode_idc);
>> +                u(2, vps_ols_mode_idc, 0, 2);
>>              else
>>                  infer(vps_ols_mode_idc, 2);
>>              if (current->vps_ols_mode_idc == 2) {
The spec reads "Decoders conforming to this version of this 
Specification shall *ignore* the OLSs with
vps_ols_mode_idc equal to 3."  This change throws an error for these 
OLSs, which I don't think is correct.
There is already some logic just below this to warn the user if 
vps_ols_mode_idc is 3.
>> @@ -902,11 +902,10 @@ static int FUNC(vps) (CodedBitstreamContext 
>> *ctx, RWContext *rw,
>>                         current->vps_ols_mode_idc == 1) {
>>                  num_layers_in_ols = i + 1;
>>              } else if (current->vps_ols_mode_idc == 2) {
>> -                for (k = 0, j = 0; k <= 
>> current->vps_max_layers_minus1; k++) {
>> +                for (k = 0, j = 0; k <= 
>> current->vps_max_layers_minus1; k++)
>>                      if (layer_included_in_ols_flag[i][k])
>>                          j++;
>> -                    num_layers_in_ols = j;
>> -                }
>> +                num_layers_in_ols = j;
>>              }
>>              if (num_layers_in_ols > 1) {
>>                  num_multi_layer_olss++;

This looks good to me, the old behaviour was wrong.  I don't think this 
is what was causing this
particular crash however.

Below is a patch which addresses the issue, an integer overflow when 
calculating the bounds for
vps_num_ols_timing_hrd_params_minus1.  There's also a similar fix for 
vps_num_dpb_params_minus1.

diff --git a/libavcodec/cbs_h266_syntax_template.c 
b/libavcodec/cbs_h266_syntax_template.c
index 549d021211..49bf2e45ac 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -946,7 +946,8 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, 
RWContext *rw,

      if (!current->vps_each_layer_is_an_ols_flag) {
          uint16_t vps_num_dpb_params;
-        ue(vps_num_dpb_params_minus1, 0, num_multi_layer_olss - 1);
+        ue(vps_num_dpb_params_minus1, 0,
+           num_multi_layer_olss > 0 ? num_multi_layer_olss - 1 : 0);
          if (current->vps_each_layer_is_an_ols_flag)
              vps_num_dpb_params = 0;
          else
@@ -991,7 +992,7 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, 
RWContext *rw,
              else
                  infer(vps_sublayer_cpb_params_present_flag, 0);
              ue(vps_num_ols_timing_hrd_params_minus1, 0,
-               num_multi_layer_olss - 1);
+               num_multi_layer_olss > 0 ? num_multi_layer_olss - 1 : 0);
              for (i = 0; i <= 
current->vps_num_ols_timing_hrd_params_minus1; i++) {
                  uint8_t first_sublayer;
                  if (!current->vps_default_ptl_dpb_hrd_max_tid_flag)
James Almer Jan. 29, 2024, 9:13 p.m. UTC | #7
On 1/29/2024 5:19 PM, Frank Plowman wrote:
> On 29/01/2024 19:04, James Almer wrote:
> 
>>
>> Well, turns out the current code is fine and my suggested change above 
>> is wrong. Fun how that goes.
>>
>> Can you test the following instead?
>>
>>> diff --git a/libavcodec/cbs_h266_syntax_template.c 
>>> b/libavcodec/cbs_h266_syntax_template.c
>>> index 549d021211..30b4ae3bc0 100644
>>> --- a/libavcodec/cbs_h266_syntax_template.c
>>> +++ b/libavcodec/cbs_h266_syntax_template.c
>>> @@ -764,7 +764,7 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, 
>>> RWContext *rw,
>>>              infer(vps_each_layer_is_an_ols_flag, 0);
>>>          if (!current->vps_each_layer_is_an_ols_flag) {
>>>              if (!current->vps_all_independent_layers_flag)
>>> -                ub(2, vps_ols_mode_idc);
>>> +                u(2, vps_ols_mode_idc, 0, 2);
>>>              else
>>>                  infer(vps_ols_mode_idc, 2);
>>>              if (current->vps_ols_mode_idc == 2) {
> The spec reads "Decoders conforming to this version of this 
> Specification shall *ignore* the OLSs with
> vps_ols_mode_idc equal to 3."  This change throws an error for these 
> OLSs, which I don't think is correct.
> There is already some logic just below this to warn the user if 
> vps_ols_mode_idc is 3.
>>> @@ -902,11 +902,10 @@ static int FUNC(vps) (CodedBitstreamContext 
>>> *ctx, RWContext *rw,
>>>                         current->vps_ols_mode_idc == 1) {
>>>                  num_layers_in_ols = i + 1;
>>>              } else if (current->vps_ols_mode_idc == 2) {
>>> -                for (k = 0, j = 0; k <= 
>>> current->vps_max_layers_minus1; k++) {
>>> +                for (k = 0, j = 0; k <= 
>>> current->vps_max_layers_minus1; k++)
>>>                      if (layer_included_in_ols_flag[i][k])
>>>                          j++;
>>> -                    num_layers_in_ols = j;
>>> -                }
>>> +                num_layers_in_ols = j;
>>>              }
>>>              if (num_layers_in_ols > 1) {
>>>                  num_multi_layer_olss++;
> 
> This looks good to me, the old behaviour was wrong.  I don't think this 
> is what was causing this
> particular crash however.

Will apply this part then.

> 
> Below is a patch which addresses the issue, an integer overflow when 
> calculating the bounds for
> vps_num_ols_timing_hrd_params_minus1.  There's also a similar fix for 
> vps_num_dpb_params_minus1.
> 
> diff --git a/libavcodec/cbs_h266_syntax_template.c 
> b/libavcodec/cbs_h266_syntax_template.c
> index 549d021211..49bf2e45ac 100644
> --- a/libavcodec/cbs_h266_syntax_template.c
> +++ b/libavcodec/cbs_h266_syntax_template.c
> @@ -946,7 +946,8 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, 
> RWContext *rw,
> 
>       if (!current->vps_each_layer_is_an_ols_flag) {
>           uint16_t vps_num_dpb_params;
> -        ue(vps_num_dpb_params_minus1, 0, num_multi_layer_olss - 1);
> +        ue(vps_num_dpb_params_minus1, 0,
> +           num_multi_layer_olss > 0 ? num_multi_layer_olss - 1 : 0);

FFMAX(0, num_multi_layer_olss - 1); looks better.

If the spec explicitly states num_multi_layer_olss - 1 should be used 
here, wouldn't that mean that it doesn't expect num_multi_layer_olss to 
be 0 at this point for vps_ols_mode_idc >= 0 && vps_ols_mode_idc < 3?
When vps_each_layer_is_an_ols_flag is true, num_multi_layer_olss is 
inferred as 1, so I'd expect it to also be at least 1 for 
vps_each_layer_is_an_ols_flag == false.

>           if (current->vps_each_layer_is_an_ols_flag)
>               vps_num_dpb_params = 0;
>           else
> @@ -991,7 +992,7 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, 
> RWContext *rw,
>               else
>                   infer(vps_sublayer_cpb_params_present_flag, 0);
>               ue(vps_num_ols_timing_hrd_params_minus1, 0,
> -               num_multi_layer_olss - 1);
> +               num_multi_layer_olss > 0 ? num_multi_layer_olss - 1 : 0);
>               for (i = 0; i <= 
> current->vps_num_ols_timing_hrd_params_minus1; i++) {
>                   uint8_t first_sublayer;
>                   if (!current->vps_default_ptl_dpb_hrd_max_tid_flag)
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Frank Plowman Jan. 29, 2024, 10:28 p.m. UTC | #8
On 29/01/2024 21:13, James Almer wrote:
> On 1/29/2024 5:19 PM, Frank Plowman wrote:
>> Below is a patch which addresses the issue, an integer overflow when 
>> calculating the bounds for
>> vps_num_ols_timing_hrd_params_minus1.  There's also a similar fix for 
>> vps_num_dpb_params_minus1.
>>
>> diff --git a/libavcodec/cbs_h266_syntax_template.c 
>> b/libavcodec/cbs_h266_syntax_template.c
>> index 549d021211..49bf2e45ac 100644
>> --- a/libavcodec/cbs_h266_syntax_template.c
>> +++ b/libavcodec/cbs_h266_syntax_template.c
>> @@ -946,7 +946,8 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, 
>> RWContext *rw,
>>
>>       if (!current->vps_each_layer_is_an_ols_flag) {
>>           uint16_t vps_num_dpb_params;
>> -        ue(vps_num_dpb_params_minus1, 0, num_multi_layer_olss - 1);
>> +        ue(vps_num_dpb_params_minus1, 0,
>> +           num_multi_layer_olss > 0 ? num_multi_layer_olss - 1 : 0);
>
> FFMAX(0, num_multi_layer_olss - 1); looks better.
>
> If the spec explicitly states num_multi_layer_olss - 1 should be used 
> here, wouldn't that mean that it doesn't expect num_multi_layer_olss 
> to be 0 at this point for vps_ols_mode_idc >= 0 && vps_ols_mode_idc < 3?
> When vps_each_layer_is_an_ols_flag is true, num_multi_layer_olss is 
> inferred as 1

num_multi_layer_olss is 0 if vps_each_layer_is_an_ols_flag is true, it 
is num_layers_in_ols which must be 1.

> so I'd expect it to also be at least 1 for 
> vps_each_layer_is_an_ols_flag == false.

Yes I think you're right.  The spec isn't especially clear, but the 
description of the vps_each_layer_is_an_ols_flag leads me to believe we 
are safe to assert that.  Patch which does so below.

diff --git a/libavcodec/cbs_h266_syntax_template.c 
b/libavcodec/cbs_h266_syntax_template.c
index 549d021211..5ae3fb9f08 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -912,6 +912,8 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, 
RWContext *rw,
                  num_multi_layer_olss++;
              }
          }
+        if (!current->vps_each_layer_is_an_ols_flag && 
num_multi_layer_olss == 0)
+            return AVERROR_INVALIDDATA;
      }

      for (i = 0; i <= current->vps_num_ptls_minus1; i++) {
James Almer Jan. 29, 2024, 11:40 p.m. UTC | #9
On 1/29/2024 7:28 PM, Frank Plowman wrote:
> 
> On 29/01/2024 21:13, James Almer wrote:
>> On 1/29/2024 5:19 PM, Frank Plowman wrote:
>>> Below is a patch which addresses the issue, an integer overflow when 
>>> calculating the bounds for
>>> vps_num_ols_timing_hrd_params_minus1.  There's also a similar fix for 
>>> vps_num_dpb_params_minus1.
>>>
>>> diff --git a/libavcodec/cbs_h266_syntax_template.c 
>>> b/libavcodec/cbs_h266_syntax_template.c
>>> index 549d021211..49bf2e45ac 100644
>>> --- a/libavcodec/cbs_h266_syntax_template.c
>>> +++ b/libavcodec/cbs_h266_syntax_template.c
>>> @@ -946,7 +946,8 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, 
>>> RWContext *rw,
>>>
>>>       if (!current->vps_each_layer_is_an_ols_flag) {
>>>           uint16_t vps_num_dpb_params;
>>> -        ue(vps_num_dpb_params_minus1, 0, num_multi_layer_olss - 1);
>>> +        ue(vps_num_dpb_params_minus1, 0,
>>> +           num_multi_layer_olss > 0 ? num_multi_layer_olss - 1 : 0);
>>
>> FFMAX(0, num_multi_layer_olss - 1); looks better.
>>
>> If the spec explicitly states num_multi_layer_olss - 1 should be used 
>> here, wouldn't that mean that it doesn't expect num_multi_layer_olss 
>> to be 0 at this point for vps_ols_mode_idc >= 0 && vps_ols_mode_idc < 3?
>> When vps_each_layer_is_an_ols_flag is true, num_multi_layer_olss is 
>> inferred as 1
> 
> num_multi_layer_olss is 0 if vps_each_layer_is_an_ols_flag is true, it 
> is num_layers_in_ols which must be 1.

You're right, i read it wrong. These names are all too similar :p

> 
>> so I'd expect it to also be at least 1 for 
>> vps_each_layer_is_an_ols_flag == false.
> 
> Yes I think you're right.  The spec isn't especially clear, but the 
> description of the vps_each_layer_is_an_ols_flag leads me to believe we 
> are safe to assert that.  Patch which does so below.
> 
> diff --git a/libavcodec/cbs_h266_syntax_template.c 
> b/libavcodec/cbs_h266_syntax_template.c
> index 549d021211..5ae3fb9f08 100644
> --- a/libavcodec/cbs_h266_syntax_template.c
> +++ b/libavcodec/cbs_h266_syntax_template.c
> @@ -912,6 +912,8 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, 
> RWContext *rw,
>                   num_multi_layer_olss++;
>               }
>           }
> +        if (!current->vps_each_layer_is_an_ols_flag && 
> num_multi_layer_olss == 0)
> +            return AVERROR_INVALIDDATA;
>       }

LGTM, can you send a patch for it?
diff mbox series

Patch

diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c
index 549d0212119..9e479c9c314 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -946,6 +946,8 @@  static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw,
 
     if (!current->vps_each_layer_is_an_ols_flag) {
         uint16_t vps_num_dpb_params;
+        if (!num_multi_layer_olss)
+            return AVERROR_INVALIDDATA;
         ue(vps_num_dpb_params_minus1, 0, num_multi_layer_olss - 1);
         if (current->vps_each_layer_is_an_ols_flag)
             vps_num_dpb_params = 0;