diff mbox series

[FFmpeg-devel,1/6] avformat/avidec: support huge durations

Message ID 20230929232001.23197-1-michael@niedermayer.cc
State New
Headers show
Series [FFmpeg-devel,1/6] avformat/avidec: support huge durations | 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 Sept. 29, 2023, 11:19 p.m. UTC
Fixes: signed integer overflow: 109817402400 * 301990077 cannot be represented in type 'long long'
Fixes: 51896/clusterfuzz-testcase-minimized-ffmpeg_dem_AVI_fuzzer-6706191715139584

Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
---
 libavformat/avidec.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

Comments

Marton Balint Sept. 30, 2023, 9:35 a.m. UTC | #1
On Sat, 30 Sep 2023, Michael Niedermayer wrote:

> Fixes: signed integer overflow: 109817402400 * 301990077 cannot be represented in type 'long long'
> Fixes: 51896/clusterfuzz-testcase-minimized-ffmpeg_dem_AVI_fuzzer-6706191715139584
>
> Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
> Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
> ---
> libavformat/avidec.c | 12 ++++++++++--
> 1 file changed, 10 insertions(+), 2 deletions(-)
>
> diff --git a/libavformat/avidec.c b/libavformat/avidec.c
> index 00bd7a98a9d..cfc693842b7 100644
> --- a/libavformat/avidec.c
> +++ b/libavformat/avidec.c
> @@ -27,6 +27,7 @@
> #include "libavutil/avstring.h"
> #include "libavutil/opt.h"
> #include "libavutil/dict.h"
> +#include "libavutil/integer.h"
> #include "libavutil/internal.h"
> #include "libavutil/intreadwrite.h"
> #include "libavutil/mathematics.h"
> @@ -476,7 +477,7 @@ static int calculate_bitrate(AVFormatContext *s)
>         AVStream *st = s->streams[i];
>         FFStream *const sti = ffstream(st);
>         int64_t duration;
> -        int64_t bitrate;
> +        int64_t bitrate = 0;
>
>         for (j = 0; j < sti->nb_index_entries; j++)
>             len += sti->index_entries[j].size;
> @@ -484,7 +485,14 @@ static int calculate_bitrate(AVFormatContext *s)
>         if (sti->nb_index_entries < 2 || st->codecpar->bit_rate > 0)
>             continue;
>         duration = sti->index_entries[j-1].timestamp - sti->index_entries[0].timestamp;
> -        bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
> +        if (INT64_MAX / duration >= st->time_base.num) {
> +            bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);

Why not always use the AVInteger version? This is not performance 
sensitive as far as I see.

Regards,
Marton

> +        } else {
> +            AVInteger bitrate_i = av_div_i(av_mul_i(av_int2i(8*len), av_int2i(st->time_base.den)),
> +                                           av_mul_i(av_int2i(duration), av_int2i(st->time_base.num)));
> +            if (av_cmp_i(bitrate_i, av_int2i(INT64_MAX)) <= 0)
> +                bitrate = av_i2int(bitrate_i);
> +        }
>         if (bitrate > 0) {
>             st->codecpar->bit_rate = bitrate;
>         }
> -- 
> 2.17.1
>
> _______________________________________________
> 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".
>
Michael Niedermayer Sept. 30, 2023, 2:04 p.m. UTC | #2
On Sat, Sep 30, 2023 at 11:35:19AM +0200, Marton Balint wrote:
> 
> 
> On Sat, 30 Sep 2023, Michael Niedermayer wrote:
> 
> > Fixes: signed integer overflow: 109817402400 * 301990077 cannot be represented in type 'long long'
> > Fixes: 51896/clusterfuzz-testcase-minimized-ffmpeg_dem_AVI_fuzzer-6706191715139584
> > 
> > Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
> > Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
> > ---
> > libavformat/avidec.c | 12 ++++++++++--
> > 1 file changed, 10 insertions(+), 2 deletions(-)
> > 
> > diff --git a/libavformat/avidec.c b/libavformat/avidec.c
> > index 00bd7a98a9d..cfc693842b7 100644
> > --- a/libavformat/avidec.c
> > +++ b/libavformat/avidec.c
> > @@ -27,6 +27,7 @@
> > #include "libavutil/avstring.h"
> > #include "libavutil/opt.h"
> > #include "libavutil/dict.h"
> > +#include "libavutil/integer.h"
> > #include "libavutil/internal.h"
> > #include "libavutil/intreadwrite.h"
> > #include "libavutil/mathematics.h"
> > @@ -476,7 +477,7 @@ static int calculate_bitrate(AVFormatContext *s)
> >         AVStream *st = s->streams[i];
> >         FFStream *const sti = ffstream(st);
> >         int64_t duration;
> > -        int64_t bitrate;
> > +        int64_t bitrate = 0;
> > 
> >         for (j = 0; j < sti->nb_index_entries; j++)
> >             len += sti->index_entries[j].size;
> > @@ -484,7 +485,14 @@ static int calculate_bitrate(AVFormatContext *s)
> >         if (sti->nb_index_entries < 2 || st->codecpar->bit_rate > 0)
> >             continue;
> >         duration = sti->index_entries[j-1].timestamp - sti->index_entries[0].timestamp;
> > -        bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
> > +        if (INT64_MAX / duration >= st->time_base.num) {
> > +            bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
> 
> Why not always use the AVInteger version? This is not performance sensitive
> as far as I see.

We can, i will have to fix the rounding though so it matches av_rescale()

[...]
Michael Niedermayer Sept. 30, 2023, 2:31 p.m. UTC | #3
On Sat, Sep 30, 2023 at 04:04:03PM +0200, Michael Niedermayer wrote:
> On Sat, Sep 30, 2023 at 11:35:19AM +0200, Marton Balint wrote:
> > 
> > 
> > On Sat, 30 Sep 2023, Michael Niedermayer wrote:
> > 
> > > Fixes: signed integer overflow: 109817402400 * 301990077 cannot be represented in type 'long long'
> > > Fixes: 51896/clusterfuzz-testcase-minimized-ffmpeg_dem_AVI_fuzzer-6706191715139584
> > > 
> > > Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
> > > Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
> > > ---
> > > libavformat/avidec.c | 12 ++++++++++--
> > > 1 file changed, 10 insertions(+), 2 deletions(-)
> > > 
> > > diff --git a/libavformat/avidec.c b/libavformat/avidec.c
> > > index 00bd7a98a9d..cfc693842b7 100644
> > > --- a/libavformat/avidec.c
> > > +++ b/libavformat/avidec.c
> > > @@ -27,6 +27,7 @@
> > > #include "libavutil/avstring.h"
> > > #include "libavutil/opt.h"
> > > #include "libavutil/dict.h"
> > > +#include "libavutil/integer.h"
> > > #include "libavutil/internal.h"
> > > #include "libavutil/intreadwrite.h"
> > > #include "libavutil/mathematics.h"
> > > @@ -476,7 +477,7 @@ static int calculate_bitrate(AVFormatContext *s)
> > >         AVStream *st = s->streams[i];
> > >         FFStream *const sti = ffstream(st);
> > >         int64_t duration;
> > > -        int64_t bitrate;
> > > +        int64_t bitrate = 0;
> > > 
> > >         for (j = 0; j < sti->nb_index_entries; j++)
> > >             len += sti->index_entries[j].size;
> > > @@ -484,7 +485,14 @@ static int calculate_bitrate(AVFormatContext *s)
> > >         if (sti->nb_index_entries < 2 || st->codecpar->bit_rate > 0)
> > >             continue;
> > >         duration = sti->index_entries[j-1].timestamp - sti->index_entries[0].timestamp;
> > > -        bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
> > > +        if (INT64_MAX / duration >= st->time_base.num) {
> > > +            bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
> > 
> > Why not always use the AVInteger version? This is not performance sensitive
> > as far as I see.
> 
> We can, i will have to fix the rounding though so it matches av_rescale()

will apply this with just AVInteger and fixed rounding with my next push probably

thx

[...]
Anton Khirnov Sept. 30, 2023, 8:18 p.m. UTC | #4
Quoting Michael Niedermayer (2023-09-30 16:31:43)
> On Sat, Sep 30, 2023 at 04:04:03PM +0200, Michael Niedermayer wrote:
> > On Sat, Sep 30, 2023 at 11:35:19AM +0200, Marton Balint wrote:
> > > 
> > > 
> > > On Sat, 30 Sep 2023, Michael Niedermayer wrote:
> > > 
> > > > Fixes: signed integer overflow: 109817402400 * 301990077 cannot be represented in type 'long long'
> > > > Fixes: 51896/clusterfuzz-testcase-minimized-ffmpeg_dem_AVI_fuzzer-6706191715139584
> > > > 
> > > > Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
> > > > Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
> > > > ---
> > > > libavformat/avidec.c | 12 ++++++++++--
> > > > 1 file changed, 10 insertions(+), 2 deletions(-)
> > > > 
> > > > diff --git a/libavformat/avidec.c b/libavformat/avidec.c
> > > > index 00bd7a98a9d..cfc693842b7 100644
> > > > --- a/libavformat/avidec.c
> > > > +++ b/libavformat/avidec.c
> > > > @@ -27,6 +27,7 @@
> > > > #include "libavutil/avstring.h"
> > > > #include "libavutil/opt.h"
> > > > #include "libavutil/dict.h"
> > > > +#include "libavutil/integer.h"
> > > > #include "libavutil/internal.h"
> > > > #include "libavutil/intreadwrite.h"
> > > > #include "libavutil/mathematics.h"
> > > > @@ -476,7 +477,7 @@ static int calculate_bitrate(AVFormatContext *s)
> > > >         AVStream *st = s->streams[i];
> > > >         FFStream *const sti = ffstream(st);
> > > >         int64_t duration;
> > > > -        int64_t bitrate;
> > > > +        int64_t bitrate = 0;
> > > > 
> > > >         for (j = 0; j < sti->nb_index_entries; j++)
> > > >             len += sti->index_entries[j].size;
> > > > @@ -484,7 +485,14 @@ static int calculate_bitrate(AVFormatContext *s)
> > > >         if (sti->nb_index_entries < 2 || st->codecpar->bit_rate > 0)
> > > >             continue;
> > > >         duration = sti->index_entries[j-1].timestamp - sti->index_entries[0].timestamp;
> > > > -        bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
> > > > +        if (INT64_MAX / duration >= st->time_base.num) {
> > > > +            bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
> > > 
> > > Why not always use the AVInteger version? This is not performance sensitive
> > > as far as I see.
> > 
> > We can, i will have to fix the rounding though so it matches av_rescale()
> 
> will apply this with just AVInteger and fixed rounding with my next push probably

This seems MUCH less readable to me.

Do we expect such huge durations in actual files rather than fuzzed
ones? Would it not be better to just not export the duration at all when
the computation would overflow?
Michael Niedermayer Sept. 30, 2023, 10:28 p.m. UTC | #5
On Sat, Sep 30, 2023 at 10:18:38PM +0200, Anton Khirnov wrote:
> Quoting Michael Niedermayer (2023-09-30 16:31:43)
> > On Sat, Sep 30, 2023 at 04:04:03PM +0200, Michael Niedermayer wrote:
> > > On Sat, Sep 30, 2023 at 11:35:19AM +0200, Marton Balint wrote:
> > > > 
> > > > 
> > > > On Sat, 30 Sep 2023, Michael Niedermayer wrote:
> > > > 
> > > > > Fixes: signed integer overflow: 109817402400 * 301990077 cannot be represented in type 'long long'
> > > > > Fixes: 51896/clusterfuzz-testcase-minimized-ffmpeg_dem_AVI_fuzzer-6706191715139584
> > > > > 
> > > > > Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
> > > > > Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
> > > > > ---
> > > > > libavformat/avidec.c | 12 ++++++++++--
> > > > > 1 file changed, 10 insertions(+), 2 deletions(-)
> > > > > 
> > > > > diff --git a/libavformat/avidec.c b/libavformat/avidec.c
> > > > > index 00bd7a98a9d..cfc693842b7 100644
> > > > > --- a/libavformat/avidec.c
> > > > > +++ b/libavformat/avidec.c
> > > > > @@ -27,6 +27,7 @@
> > > > > #include "libavutil/avstring.h"
> > > > > #include "libavutil/opt.h"
> > > > > #include "libavutil/dict.h"
> > > > > +#include "libavutil/integer.h"
> > > > > #include "libavutil/internal.h"
> > > > > #include "libavutil/intreadwrite.h"
> > > > > #include "libavutil/mathematics.h"
> > > > > @@ -476,7 +477,7 @@ static int calculate_bitrate(AVFormatContext *s)
> > > > >         AVStream *st = s->streams[i];
> > > > >         FFStream *const sti = ffstream(st);
> > > > >         int64_t duration;
> > > > > -        int64_t bitrate;
> > > > > +        int64_t bitrate = 0;
> > > > > 
> > > > >         for (j = 0; j < sti->nb_index_entries; j++)
> > > > >             len += sti->index_entries[j].size;
> > > > > @@ -484,7 +485,14 @@ static int calculate_bitrate(AVFormatContext *s)
> > > > >         if (sti->nb_index_entries < 2 || st->codecpar->bit_rate > 0)
> > > > >             continue;
> > > > >         duration = sti->index_entries[j-1].timestamp - sti->index_entries[0].timestamp;
> > > > > -        bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
> > > > > +        if (INT64_MAX / duration >= st->time_base.num) {
> > > > > +            bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
> > > > 
> > > > Why not always use the AVInteger version? This is not performance sensitive
> > > > as far as I see.
> > > 
> > > We can, i will have to fix the rounding though so it matches av_rescale()
> > 
> > will apply this with just AVInteger and fixed rounding with my next push probably
> 
> This seems MUCH less readable to me.

we can add a av_rescale_2den()


> 
> Do we expect such huge durations in actual files rather than fuzzed
> ones?

no, i dont expect that.


> Would it not be better to just not export the duration at all when
> the computation would overflow?

Better ? i dont know
if i scale that idea up and replace EVERY computation with a subset
that seems enough and prettier. I think that would not result in
overall very robust or clean code.
If its better to do it just here i dont know but ill do whatever
people prefer

thx

[...]
Anton Khirnov Oct. 2, 2023, 9:07 a.m. UTC | #6
Quoting Michael Niedermayer (2023-10-01 00:28:56)
> On Sat, Sep 30, 2023 at 10:18:38PM +0200, Anton Khirnov wrote:
> > Quoting Michael Niedermayer (2023-09-30 16:31:43)
> > > On Sat, Sep 30, 2023 at 04:04:03PM +0200, Michael Niedermayer wrote:
> > > > On Sat, Sep 30, 2023 at 11:35:19AM +0200, Marton Balint wrote:
> > > > > 
> > > > > 
> > > > > On Sat, 30 Sep 2023, Michael Niedermayer wrote:
> > > > > 
> > > > > > Fixes: signed integer overflow: 109817402400 * 301990077 cannot be represented in type 'long long'
> > > > > > Fixes: 51896/clusterfuzz-testcase-minimized-ffmpeg_dem_AVI_fuzzer-6706191715139584
> > > > > > 
> > > > > > Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
> > > > > > Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
> > > > > > ---
> > > > > > libavformat/avidec.c | 12 ++++++++++--
> > > > > > 1 file changed, 10 insertions(+), 2 deletions(-)
> > > > > > 
> > > > > > diff --git a/libavformat/avidec.c b/libavformat/avidec.c
> > > > > > index 00bd7a98a9d..cfc693842b7 100644
> > > > > > --- a/libavformat/avidec.c
> > > > > > +++ b/libavformat/avidec.c
> > > > > > @@ -27,6 +27,7 @@
> > > > > > #include "libavutil/avstring.h"
> > > > > > #include "libavutil/opt.h"
> > > > > > #include "libavutil/dict.h"
> > > > > > +#include "libavutil/integer.h"
> > > > > > #include "libavutil/internal.h"
> > > > > > #include "libavutil/intreadwrite.h"
> > > > > > #include "libavutil/mathematics.h"
> > > > > > @@ -476,7 +477,7 @@ static int calculate_bitrate(AVFormatContext *s)
> > > > > >         AVStream *st = s->streams[i];
> > > > > >         FFStream *const sti = ffstream(st);
> > > > > >         int64_t duration;
> > > > > > -        int64_t bitrate;
> > > > > > +        int64_t bitrate = 0;
> > > > > > 
> > > > > >         for (j = 0; j < sti->nb_index_entries; j++)
> > > > > >             len += sti->index_entries[j].size;
> > > > > > @@ -484,7 +485,14 @@ static int calculate_bitrate(AVFormatContext *s)
> > > > > >         if (sti->nb_index_entries < 2 || st->codecpar->bit_rate > 0)
> > > > > >             continue;
> > > > > >         duration = sti->index_entries[j-1].timestamp - sti->index_entries[0].timestamp;
> > > > > > -        bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
> > > > > > +        if (INT64_MAX / duration >= st->time_base.num) {
> > > > > > +            bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
> > > > > 
> > > > > Why not always use the AVInteger version? This is not performance sensitive
> > > > > as far as I see.
> > > > 
> > > > We can, i will have to fix the rounding though so it matches av_rescale()
> > > 
> > > will apply this with just AVInteger and fixed rounding with my next push probably
> > 
> > This seems MUCH less readable to me.
> 
> we can add a av_rescale_2den()

Won't av_rescale_q(len, (AVRational){8, duration}, st->time_base)
achieve the same effect?
Michael Niedermayer Oct. 2, 2023, 7:03 p.m. UTC | #7
On Mon, Oct 02, 2023 at 11:07:47AM +0200, Anton Khirnov wrote:
> Quoting Michael Niedermayer (2023-10-01 00:28:56)
> > On Sat, Sep 30, 2023 at 10:18:38PM +0200, Anton Khirnov wrote:
> > > Quoting Michael Niedermayer (2023-09-30 16:31:43)
> > > > On Sat, Sep 30, 2023 at 04:04:03PM +0200, Michael Niedermayer wrote:
> > > > > On Sat, Sep 30, 2023 at 11:35:19AM +0200, Marton Balint wrote:
> > > > > > 
> > > > > > 
> > > > > > On Sat, 30 Sep 2023, Michael Niedermayer wrote:
> > > > > > 
> > > > > > > Fixes: signed integer overflow: 109817402400 * 301990077 cannot be represented in type 'long long'
> > > > > > > Fixes: 51896/clusterfuzz-testcase-minimized-ffmpeg_dem_AVI_fuzzer-6706191715139584
> > > > > > > 
> > > > > > > Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
> > > > > > > Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
> > > > > > > ---
> > > > > > > libavformat/avidec.c | 12 ++++++++++--
> > > > > > > 1 file changed, 10 insertions(+), 2 deletions(-)
> > > > > > > 
> > > > > > > diff --git a/libavformat/avidec.c b/libavformat/avidec.c
> > > > > > > index 00bd7a98a9d..cfc693842b7 100644
> > > > > > > --- a/libavformat/avidec.c
> > > > > > > +++ b/libavformat/avidec.c
> > > > > > > @@ -27,6 +27,7 @@
> > > > > > > #include "libavutil/avstring.h"
> > > > > > > #include "libavutil/opt.h"
> > > > > > > #include "libavutil/dict.h"
> > > > > > > +#include "libavutil/integer.h"
> > > > > > > #include "libavutil/internal.h"
> > > > > > > #include "libavutil/intreadwrite.h"
> > > > > > > #include "libavutil/mathematics.h"
> > > > > > > @@ -476,7 +477,7 @@ static int calculate_bitrate(AVFormatContext *s)
> > > > > > >         AVStream *st = s->streams[i];
> > > > > > >         FFStream *const sti = ffstream(st);
> > > > > > >         int64_t duration;
> > > > > > > -        int64_t bitrate;
> > > > > > > +        int64_t bitrate = 0;
> > > > > > > 
> > > > > > >         for (j = 0; j < sti->nb_index_entries; j++)
> > > > > > >             len += sti->index_entries[j].size;
> > > > > > > @@ -484,7 +485,14 @@ static int calculate_bitrate(AVFormatContext *s)
> > > > > > >         if (sti->nb_index_entries < 2 || st->codecpar->bit_rate > 0)
> > > > > > >             continue;
> > > > > > >         duration = sti->index_entries[j-1].timestamp - sti->index_entries[0].timestamp;
> > > > > > > -        bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
> > > > > > > +        if (INT64_MAX / duration >= st->time_base.num) {
> > > > > > > +            bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
> > > > > > 
> > > > > > Why not always use the AVInteger version? This is not performance sensitive
> > > > > > as far as I see.
> > > > > 
> > > > > We can, i will have to fix the rounding though so it matches av_rescale()
> > > > 
> > > > will apply this with just AVInteger and fixed rounding with my next push probably
> > > 
> > > This seems MUCH less readable to me.
> > 
> > we can add a av_rescale_2den()
> 
> Won't av_rescale_q(len, (AVRational){8, duration}, st->time_base)
> achieve the same effect?

duration is 64bit AVRational is 32/32 bit, so i would expect that to not
work.
If duration was fitting in 32bit then duration * st->time_base.num would not
have overflowed

thx

[...]
Tomas Härdin Oct. 3, 2023, 9:10 a.m. UTC | #8
mån 2023-10-02 klockan 21:03 +0200 skrev Michael Niedermayer:
> On Mon, Oct 02, 2023 at 11:07:47AM +0200, Anton Khirnov wrote:
> > Quoting Michael Niedermayer (2023-10-01 00:28:56)
> > > On Sat, Sep 30, 2023 at 10:18:38PM +0200, Anton Khirnov wrote:
> > > > Quoting Michael Niedermayer (2023-09-30 16:31:43)
> > > > > On Sat, Sep 30, 2023 at 04:04:03PM +0200, Michael Niedermayer
> > > > > wrote:
> > > > > > On Sat, Sep 30, 2023 at 11:35:19AM +0200, Marton Balint
> > > > > > wrote:
> > > > > > > 
> > > > > > > 
> > > > > > > On Sat, 30 Sep 2023, Michael Niedermayer wrote:
> > > > > > > 
> > > > > > > > Fixes: signed integer overflow: 109817402400 *
> > > > > > > > 301990077 cannot be represented in type 'long long'
> > > > > > > > Fixes: 51896/clusterfuzz-testcase-minimized-
> > > > > > > > ffmpeg_dem_AVI_fuzzer-6706191715139584
> > > > > > > > 
> > > > > > > > Found-by: continuous fuzzing process
> > > > > > > > https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
> > > > > > > > Signed-off-by: Michael Niedermayer
> > > > > > > > <michael@niedermayer.cc>
> > > > > > > > ---
> > > > > > > > libavformat/avidec.c | 12 ++++++++++--
> > > > > > > > 1 file changed, 10 insertions(+), 2 deletions(-)
> > > > > > > > 
> > > > > > > > diff --git a/libavformat/avidec.c
> > > > > > > > b/libavformat/avidec.c
> > > > > > > > index 00bd7a98a9d..cfc693842b7 100644
> > > > > > > > --- a/libavformat/avidec.c
> > > > > > > > +++ b/libavformat/avidec.c
> > > > > > > > @@ -27,6 +27,7 @@
> > > > > > > > #include "libavutil/avstring.h"
> > > > > > > > #include "libavutil/opt.h"
> > > > > > > > #include "libavutil/dict.h"
> > > > > > > > +#include "libavutil/integer.h"
> > > > > > > > #include "libavutil/internal.h"
> > > > > > > > #include "libavutil/intreadwrite.h"
> > > > > > > > #include "libavutil/mathematics.h"
> > > > > > > > @@ -476,7 +477,7 @@ static int
> > > > > > > > calculate_bitrate(AVFormatContext *s)
> > > > > > > >         AVStream *st = s->streams[i];
> > > > > > > >         FFStream *const sti = ffstream(st);
> > > > > > > >         int64_t duration;
> > > > > > > > -        int64_t bitrate;
> > > > > > > > +        int64_t bitrate = 0;
> > > > > > > > 
> > > > > > > >         for (j = 0; j < sti->nb_index_entries; j++)
> > > > > > > >             len += sti->index_entries[j].size;
> > > > > > > > @@ -484,7 +485,14 @@ static int
> > > > > > > > calculate_bitrate(AVFormatContext *s)
> > > > > > > >         if (sti->nb_index_entries < 2 || st->codecpar-
> > > > > > > > >bit_rate > 0)
> > > > > > > >             continue;
> > > > > > > >         duration = sti->index_entries[j-1].timestamp -
> > > > > > > > sti->index_entries[0].timestamp;
> > > > > > > > -        bitrate = av_rescale(8*len, st->time_base.den,
> > > > > > > > duration * st->time_base.num);
> > > > > > > > +        if (INT64_MAX / duration >= st->time_base.num)
> > > > > > > > {
> > > > > > > > +            bitrate = av_rescale(8*len, st-
> > > > > > > > >time_base.den, duration * st->time_base.num);
> > > > > > > 
> > > > > > > Why not always use the AVInteger version? This is not
> > > > > > > performance sensitive
> > > > > > > as far as I see.
> > > > > > 
> > > > > > We can, i will have to fix the rounding though so it
> > > > > > matches av_rescale()
> > > > > 
> > > > > will apply this with just AVInteger and fixed rounding with
> > > > > my next push probably
> > > > 
> > > > This seems MUCH less readable to me.
> > > 
> > > we can add a av_rescale_2den()
> > 
> > Won't av_rescale_q(len, (AVRational){8, duration}, st->time_base)
> > achieve the same effect?
> 
> duration is 64bit AVRational is 32/32 bit, so i would expect that to
> not
> work.
> If duration was fitting in 32bit then duration * st->time_base.num
> would not
> have overflowed

Maybe we need a 64/64-bit version of AVRational..

/Tomas
Michael Niedermayer Oct. 3, 2023, 6:53 p.m. UTC | #9
On Tue, Oct 03, 2023 at 11:10:20AM +0200, Tomas Härdin wrote:
> mån 2023-10-02 klockan 21:03 +0200 skrev Michael Niedermayer:
> > On Mon, Oct 02, 2023 at 11:07:47AM +0200, Anton Khirnov wrote:
> > > Quoting Michael Niedermayer (2023-10-01 00:28:56)
> > > > On Sat, Sep 30, 2023 at 10:18:38PM +0200, Anton Khirnov wrote:
> > > > > Quoting Michael Niedermayer (2023-09-30 16:31:43)
> > > > > > On Sat, Sep 30, 2023 at 04:04:03PM +0200, Michael Niedermayer
> > > > > > wrote:
> > > > > > > On Sat, Sep 30, 2023 at 11:35:19AM +0200, Marton Balint
> > > > > > > wrote:
> > > > > > > > 
> > > > > > > > 
> > > > > > > > On Sat, 30 Sep 2023, Michael Niedermayer wrote:
> > > > > > > > 
> > > > > > > > > Fixes: signed integer overflow: 109817402400 *
> > > > > > > > > 301990077 cannot be represented in type 'long long'
> > > > > > > > > Fixes: 51896/clusterfuzz-testcase-minimized-
> > > > > > > > > ffmpeg_dem_AVI_fuzzer-6706191715139584
> > > > > > > > > 
> > > > > > > > > Found-by: continuous fuzzing process
> > > > > > > > > https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
> > > > > > > > > Signed-off-by: Michael Niedermayer
> > > > > > > > > <michael@niedermayer.cc>
> > > > > > > > > ---
> > > > > > > > > libavformat/avidec.c | 12 ++++++++++--
> > > > > > > > > 1 file changed, 10 insertions(+), 2 deletions(-)
> > > > > > > > > 
> > > > > > > > > diff --git a/libavformat/avidec.c
> > > > > > > > > b/libavformat/avidec.c
> > > > > > > > > index 00bd7a98a9d..cfc693842b7 100644
> > > > > > > > > --- a/libavformat/avidec.c
> > > > > > > > > +++ b/libavformat/avidec.c
> > > > > > > > > @@ -27,6 +27,7 @@
> > > > > > > > > #include "libavutil/avstring.h"
> > > > > > > > > #include "libavutil/opt.h"
> > > > > > > > > #include "libavutil/dict.h"
> > > > > > > > > +#include "libavutil/integer.h"
> > > > > > > > > #include "libavutil/internal.h"
> > > > > > > > > #include "libavutil/intreadwrite.h"
> > > > > > > > > #include "libavutil/mathematics.h"
> > > > > > > > > @@ -476,7 +477,7 @@ static int
> > > > > > > > > calculate_bitrate(AVFormatContext *s)
> > > > > > > > >         AVStream *st = s->streams[i];
> > > > > > > > >         FFStream *const sti = ffstream(st);
> > > > > > > > >         int64_t duration;
> > > > > > > > > -        int64_t bitrate;
> > > > > > > > > +        int64_t bitrate = 0;
> > > > > > > > > 
> > > > > > > > >         for (j = 0; j < sti->nb_index_entries; j++)
> > > > > > > > >             len += sti->index_entries[j].size;
> > > > > > > > > @@ -484,7 +485,14 @@ static int
> > > > > > > > > calculate_bitrate(AVFormatContext *s)
> > > > > > > > >         if (sti->nb_index_entries < 2 || st->codecpar-
> > > > > > > > > >bit_rate > 0)
> > > > > > > > >             continue;
> > > > > > > > >         duration = sti->index_entries[j-1].timestamp -
> > > > > > > > > sti->index_entries[0].timestamp;
> > > > > > > > > -        bitrate = av_rescale(8*len, st->time_base.den,
> > > > > > > > > duration * st->time_base.num);
> > > > > > > > > +        if (INT64_MAX / duration >= st->time_base.num)
> > > > > > > > > {
> > > > > > > > > +            bitrate = av_rescale(8*len, st-
> > > > > > > > > >time_base.den, duration * st->time_base.num);
> > > > > > > > 
> > > > > > > > Why not always use the AVInteger version? This is not
> > > > > > > > performance sensitive
> > > > > > > > as far as I see.
> > > > > > > 
> > > > > > > We can, i will have to fix the rounding though so it
> > > > > > > matches av_rescale()
> > > > > > 
> > > > > > will apply this with just AVInteger and fixed rounding with
> > > > > > my next push probably
> > > > > 
> > > > > This seems MUCH less readable to me.
> > > > 
> > > > we can add a av_rescale_2den()
> > > 
> > > Won't av_rescale_q(len, (AVRational){8, duration}, st->time_base)
> > > achieve the same effect?
> > 
> > duration is 64bit AVRational is 32/32 bit, so i would expect that to
> > not
> > work.
> > If duration was fitting in 32bit then duration * st->time_base.num
> > would not
> > have overflowed
> 
> Maybe we need a 64/64-bit version of AVRational..

Iam not convinced at this point that we need that.
But its not hard to do, our AVInteger code will do any size integers by just
changing a single number.

The real annoyance is if you have 32/32 and 64/64 rationals then suddenly
you need many functions to handle both. Sofar only a fuzzed file exceeded
our 64 * 64 / 64 == 64 * 32/32 * 32/32 rescale in one place
adding a 64 * 64 / (64 * 64) rescale would fix that one which seems more
limited in spreading complexity through the codebase

thx


[...]
Michael Niedermayer March 25, 2024, 10:50 p.m. UTC | #10
On Tue, Oct 03, 2023 at 08:53:33PM +0200, Michael Niedermayer wrote:
> On Tue, Oct 03, 2023 at 11:10:20AM +0200, Tomas Härdin wrote:
> > mån 2023-10-02 klockan 21:03 +0200 skrev Michael Niedermayer:
> > > On Mon, Oct 02, 2023 at 11:07:47AM +0200, Anton Khirnov wrote:
> > > > Quoting Michael Niedermayer (2023-10-01 00:28:56)
> > > > > On Sat, Sep 30, 2023 at 10:18:38PM +0200, Anton Khirnov wrote:
> > > > > > Quoting Michael Niedermayer (2023-09-30 16:31:43)
> > > > > > > On Sat, Sep 30, 2023 at 04:04:03PM +0200, Michael Niedermayer
> > > > > > > wrote:
> > > > > > > > On Sat, Sep 30, 2023 at 11:35:19AM +0200, Marton Balint
> > > > > > > > wrote:
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > On Sat, 30 Sep 2023, Michael Niedermayer wrote:
> > > > > > > > > 
> > > > > > > > > > Fixes: signed integer overflow: 109817402400 *
> > > > > > > > > > 301990077 cannot be represented in type 'long long'
> > > > > > > > > > Fixes: 51896/clusterfuzz-testcase-minimized-
> > > > > > > > > > ffmpeg_dem_AVI_fuzzer-6706191715139584
> > > > > > > > > > 
> > > > > > > > > > Found-by: continuous fuzzing process
> > > > > > > > > > https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
> > > > > > > > > > Signed-off-by: Michael Niedermayer
> > > > > > > > > > <michael@niedermayer.cc>
> > > > > > > > > > ---
> > > > > > > > > > libavformat/avidec.c | 12 ++++++++++--
> > > > > > > > > > 1 file changed, 10 insertions(+), 2 deletions(-)
> > > > > > > > > > 
> > > > > > > > > > diff --git a/libavformat/avidec.c
> > > > > > > > > > b/libavformat/avidec.c
> > > > > > > > > > index 00bd7a98a9d..cfc693842b7 100644
> > > > > > > > > > --- a/libavformat/avidec.c
> > > > > > > > > > +++ b/libavformat/avidec.c
> > > > > > > > > > @@ -27,6 +27,7 @@
> > > > > > > > > > #include "libavutil/avstring.h"
> > > > > > > > > > #include "libavutil/opt.h"
> > > > > > > > > > #include "libavutil/dict.h"
> > > > > > > > > > +#include "libavutil/integer.h"
> > > > > > > > > > #include "libavutil/internal.h"
> > > > > > > > > > #include "libavutil/intreadwrite.h"
> > > > > > > > > > #include "libavutil/mathematics.h"
> > > > > > > > > > @@ -476,7 +477,7 @@ static int
> > > > > > > > > > calculate_bitrate(AVFormatContext *s)
> > > > > > > > > >         AVStream *st = s->streams[i];
> > > > > > > > > >         FFStream *const sti = ffstream(st);
> > > > > > > > > >         int64_t duration;
> > > > > > > > > > -        int64_t bitrate;
> > > > > > > > > > +        int64_t bitrate = 0;
> > > > > > > > > > 
> > > > > > > > > >         for (j = 0; j < sti->nb_index_entries; j++)
> > > > > > > > > >             len += sti->index_entries[j].size;
> > > > > > > > > > @@ -484,7 +485,14 @@ static int
> > > > > > > > > > calculate_bitrate(AVFormatContext *s)
> > > > > > > > > >         if (sti->nb_index_entries < 2 || st->codecpar-
> > > > > > > > > > >bit_rate > 0)
> > > > > > > > > >             continue;
> > > > > > > > > >         duration = sti->index_entries[j-1].timestamp -
> > > > > > > > > > sti->index_entries[0].timestamp;
> > > > > > > > > > -        bitrate = av_rescale(8*len, st->time_base.den,
> > > > > > > > > > duration * st->time_base.num);
> > > > > > > > > > +        if (INT64_MAX / duration >= st->time_base.num)
> > > > > > > > > > {
> > > > > > > > > > +            bitrate = av_rescale(8*len, st-
> > > > > > > > > > >time_base.den, duration * st->time_base.num);
> > > > > > > > > 
> > > > > > > > > Why not always use the AVInteger version? This is not
> > > > > > > > > performance sensitive
> > > > > > > > > as far as I see.
> > > > > > > > 
> > > > > > > > We can, i will have to fix the rounding though so it
> > > > > > > > matches av_rescale()
> > > > > > > 
> > > > > > > will apply this with just AVInteger and fixed rounding with
> > > > > > > my next push probably
> > > > > > 
> > > > > > This seems MUCH less readable to me.
> > > > > 
> > > > > we can add a av_rescale_2den()
> > > > 
> > > > Won't av_rescale_q(len, (AVRational){8, duration}, st->time_base)
> > > > achieve the same effect?
> > > 
> > > duration is 64bit AVRational is 32/32 bit, so i would expect that to
> > > not
> > > work.
> > > If duration was fitting in 32bit then duration * st->time_base.num
> > > would not
> > > have overflowed
> > 
> > Maybe we need a 64/64-bit version of AVRational..
> 
> Iam not convinced at this point that we need that.
> But its not hard to do, our AVInteger code will do any size integers by just
> changing a single number.
> 
> The real annoyance is if you have 32/32 and 64/64 rationals then suddenly
> you need many functions to handle both. Sofar only a fuzzed file exceeded
> our 64 * 64 / 64 == 64 * 32/32 * 32/32 rescale in one place
> adding a 64 * 64 / (64 * 64) rescale would fix that one which seems more
> limited in spreading complexity through the codebase

Ill apply this below, because its undefined behavior and needs to be fixed.
If someone has a better solution please replace my code with it.

@@ -476,7 +477,7 @@ static int calculate_bitrate(AVFormatContext *s)
         AVStream *st = s->streams[i];
         FFStream *const sti = ffstream(st);
         int64_t duration;
-        int64_t bitrate;
+        AVInteger bitrate_i, den_i, num_i;

         for (j = 0; j < sti->nb_index_entries; j++)
             len += sti->index_entries[j].size;
@@ -484,9 +485,14 @@ static int calculate_bitrate(AVFormatContext *s)
         if (sti->nb_index_entries < 2 || st->codecpar->bit_rate > 0)
             continue;
         duration = sti->index_entries[j-1].timestamp - sti->index_entries[0].timestamp;
-        bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
-        if (bitrate > 0) {
-            st->codecpar->bit_rate = bitrate;
+        den_i = av_mul_i(av_int2i(duration), av_int2i(st->time_base.num));
+        num_i = av_add_i(av_mul_i(av_int2i(8*len), av_int2i(st->time_base.den)), av_shr_i(den_i, 1));
+        bitrate_i = av_div_i(num_i, den_i);
+        if (av_cmp_i(bitrate_i, av_int2i(INT64_MAX)) <= 0) {
+            int64_t bitrate = av_i2int(bitrate_i);
+            if (bitrate > 0) {
+                st->codecpar->bit_rate = bitrate;
+            }
         }
     }
     return 1;

[...]
diff mbox series

Patch

diff --git a/libavformat/avidec.c b/libavformat/avidec.c
index 00bd7a98a9d..cfc693842b7 100644
--- a/libavformat/avidec.c
+++ b/libavformat/avidec.c
@@ -27,6 +27,7 @@ 
 #include "libavutil/avstring.h"
 #include "libavutil/opt.h"
 #include "libavutil/dict.h"
+#include "libavutil/integer.h"
 #include "libavutil/internal.h"
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mathematics.h"
@@ -476,7 +477,7 @@  static int calculate_bitrate(AVFormatContext *s)
         AVStream *st = s->streams[i];
         FFStream *const sti = ffstream(st);
         int64_t duration;
-        int64_t bitrate;
+        int64_t bitrate = 0;
 
         for (j = 0; j < sti->nb_index_entries; j++)
             len += sti->index_entries[j].size;
@@ -484,7 +485,14 @@  static int calculate_bitrate(AVFormatContext *s)
         if (sti->nb_index_entries < 2 || st->codecpar->bit_rate > 0)
             continue;
         duration = sti->index_entries[j-1].timestamp - sti->index_entries[0].timestamp;
-        bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
+        if (INT64_MAX / duration >= st->time_base.num) {
+            bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
+        } else {
+            AVInteger bitrate_i = av_div_i(av_mul_i(av_int2i(8*len), av_int2i(st->time_base.den)),
+                                           av_mul_i(av_int2i(duration), av_int2i(st->time_base.num)));
+            if (av_cmp_i(bitrate_i, av_int2i(INT64_MAX)) <= 0)
+                bitrate = av_i2int(bitrate_i);
+        }
         if (bitrate > 0) {
             st->codecpar->bit_rate = bitrate;
         }