PR #24552 opened by michaelni
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24552
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24552.patch

Fixes: division by zero
Fixes: 1XvjZP0UGl6O
Found-by: Gabe Sherman (Trail of Bits)


>From c60b9b59ad86b025f4c32502bafe4170d68539d3 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Thu, 17 Sep 2026 16:21:42 +0200
Subject: [PATCH 1/7] avcodec/mpegvideo_enc: assert on the frame distance
 before it is truncated to 16 bits

Fixes: assertion failure
Fixes: 1XvjZP0UGl6O
Found-by: Gabe Sherman (Trail of Bits)
---
 libavcodec/mpegvideo_enc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c
index 7751de8510..1a6cdbfdfb 100644
--- a/libavcodec/mpegvideo_enc.c
+++ b/libavcodec/mpegvideo_enc.c
@@ -3665,9 +3665,9 @@ static void set_frame_distances(MPVEncContext *const s)
         s->c.pb_time = s->c.pp_time - (s->c.last_non_b_time - s->c.time);
         av_assert1(s->c.pb_time > 0 && s->c.pb_time < s->c.pp_time);
     }else{
+        av_assert1(s->picture_number == 0 || s->c.time > s->c.last_non_b_time);
         s->c.pp_time = s->c.time - s->c.last_non_b_time;
         s->c.last_non_b_time = s->c.time;
-        av_assert1(s->picture_number == 0 || s->c.pp_time > 0);
     }
 }
 
-- 
2.52.0


>From 63793db62946752c3a769299d24b0232f1f42871 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Thu, 17 Sep 2026 18:41:48 +0200
Subject: [PATCH 2/7] avcodec/mpegvideo_enc: keep MPEG-4 B frame chains within
 the 16 bit frame distance

Fixes: division by zero
Fixes: 1XvjZP0UGl6O
Found-by: Gabe Sherman (Trail of Bits)
---
 libavcodec/mpegvideo_enc.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c
index 1a6cdbfdfb..3d749360f9 100644
--- a/libavcodec/mpegvideo_enc.c
+++ b/libavcodec/mpegvideo_enc.c
@@ -1722,6 +1722,12 @@ static int set_bframe_chain_length(MPVMainEncContext 
*const m)
             }
         }
 
+        if (s->c.codec_id == AV_CODEC_ID_MPEG4)
+            while (b_frames &&
+                   m->input_picture[b_frames]->f->pts * 
s->c.avctx->time_base.num -
+                   s->c.last_non_b_time > UINT16_MAX)
+                b_frames--;
+
         for (int i = b_frames - 1; i >= 0; i--) {
             int type = m->input_picture[i]->f->pict_type;
             if (type && type != AV_PICTURE_TYPE_B)
-- 
2.52.0


>From 1643f9aa74986026bd395009b0244db72656f987 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Thu, 17 Sep 2026 18:02:03 +0200
Subject: [PATCH 3/7] avcodec/mpegvideo_enc: reject pts outside the range the
 MPEG-4 time can represent

Fixes: signed integer overflow
Found during review of the fix for 1XvjZP0UGl6O
---
 libavcodec/mpegvideo_enc.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c
index 3d749360f9..f52e3617d7 100644
--- a/libavcodec/mpegvideo_enc.c
+++ b/libavcodec/mpegvideo_enc.c
@@ -1301,6 +1301,13 @@ static int load_input_picture(MPVMainEncContext *const 
m, const AVFrame *pic_arg
         display_picture_number = m->input_picture_number++;
 
         if (pts != AV_NOPTS_VALUE) {
+            if (s->c.codec_id == AV_CODEC_ID_MPEG4 &&
+                (pts > INT64_MAX / 2 / s->c.avctx->time_base.num ||
+                 pts < INT64_MIN / 2 / s->c.avctx->time_base.num)) {
+                av_log(s->c.avctx, AV_LOG_ERROR, "pts %"PRId64" is out of the 
supported range\n", pts);
+                return AVERROR_PATCHWELCOME;
+            }
+
             if (m->user_specified_pts != AV_NOPTS_VALUE) {
                 int64_t last = m->user_specified_pts;
 
-- 
2.52.0


>From ebbb735b5acd34d18eb1d33225e26866c6364ec4 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Thu, 17 Sep 2026 17:02:55 +0200
Subject: [PATCH 4/7] avcodec/mpeg4videoenc: compute the GOP header time with
 av_rescale_rnd()

pts * time_base.num can overflow int64_t; av_rescale_rnd() computes
the same floor division exactly.
---
 libavcodec/mpeg4videoenc.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/libavcodec/mpeg4videoenc.c b/libavcodec/mpeg4videoenc.c
index a10da6af82..f7aa635d2e 100644
--- a/libavcodec/mpeg4videoenc.c
+++ b/libavcodec/mpeg4videoenc.c
@@ -861,10 +861,9 @@ static void mpeg4_encode_gop_header(MPVMainEncContext 
*const m)
     time = s->c.cur_pic.ptr->f->pts;
     if (m->reordered_input_picture[1])
         time = FFMIN(time, m->reordered_input_picture[1]->f->pts);
-    time = time * s->c.avctx->time_base.num;
-    s->c.last_time_base = FFUDIV(time, s->c.avctx->time_base.den);
+    seconds = av_rescale_rnd(time, s->c.avctx->time_base.num, 
s->c.avctx->time_base.den, AV_ROUND_DOWN);
+    s->c.last_time_base = seconds;
 
-    seconds = FFUDIV(time, s->c.avctx->time_base.den);
     minutes = FFUDIV(seconds, 60); seconds = FFUMOD(seconds, 60);
     hours   = FFUDIV(minutes, 60); minutes = FFUMOD(minutes, 60);
     hours   = FFUMOD(hours  , 24);
-- 
2.52.0


>From 4b5da16fcec07b111a5584736542bc1e03148e3c Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Thu, 17 Sep 2026 18:02:03 +0200
Subject: [PATCH 5/7] avcodec/ituh263enc: compute the temporal reference with
 av_rescale_rnd()

The hand written multiply and divide can overflow int64_t for a large
time_base.num; av_rescale_rnd() computes the same truncating division
exactly.
---
 libavcodec/ituh263enc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavcodec/ituh263enc.c b/libavcodec/ituh263enc.c
index 56259783b0..e9c5d08a78 100644
--- a/libavcodec/ituh263enc.c
+++ b/libavcodec/ituh263enc.c
@@ -245,8 +245,8 @@ static int h263_encode_picture_header(MPVMainEncContext 
*const m)
     coded_frame_rate_base= (1000+best_clock_code)*best_divisor;
 
     put_bits(&s->pb, 22, 0x20); /* PSC */
-    temp_ref = s->picture_number * (int64_t)coded_frame_rate * 
s->c.avctx->time_base.num / //FIXME use timestamp
-                         (coded_frame_rate_base * 
(int64_t)s->c.avctx->time_base.den);
+    temp_ref = av_rescale_rnd(s->picture_number, coded_frame_rate * 
(int64_t)s->c.avctx->time_base.num, //FIXME use timestamp
+                              coded_frame_rate_base * 
(int64_t)s->c.avctx->time_base.den, AV_ROUND_ZERO);
     put_sbits(&s->pb, 8, temp_ref); /* TemporalReference */
 
     put_bits(&s->pb, 1, 1);     /* marker */
-- 
2.52.0


>From e01500e214b3bc2a1f5a9e42a66ba283df5c7c91 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Thu, 17 Sep 2026 18:41:48 +0200
Subject: [PATCH 6/7] avcodec/motion_est: scale the B frame vector predictors
 with the exact frame distances

Fixes: division by zero
Fixes: 1XvjZP0UGl6O
Found-by: Gabe Sherman (Trail of Bits)
---
 libavcodec/motion_est.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/libavcodec/motion_est.c b/libavcodec/motion_est.c
index 894fa85576..492336ca54 100644
--- a/libavcodec/motion_est.c
+++ b/libavcodec/motion_est.c
@@ -1149,11 +1149,12 @@ static int estimate_motion_b(MPVEncContext *const s, 
int mb_x, int mb_y,
         c->pred_x = P_LEFT[0];
         c->pred_y = P_LEFT[1];
 
-        if(mv_table == s->b_forw_mv_table){
-            mv_scale= (s->c.pb_time<<16) / (s->c.pp_time<<shift);
-        }else{
-            mv_scale = ((s->c.pb_time - s->c.pp_time) * (1 << 16)) / 
(s->c.pp_time<<shift);
-        }
+        int64_t pp = s->c.next_pic.ptr->f->pts - s->c.last_pic.ptr->f->pts;
+        int64_t pb = s->c. cur_pic.ptr->f->pts - s->c.last_pic.ptr->f->pts;
+        if (mv_table == s->b_forw_mv_table)
+            mv_scale = (pb << (16 - shift)) / pp;
+        else
+            mv_scale = (pb - pp) * (1 << (16 - shift)) / pp;
 
         dmin = ff_epzs_motion_search(s, &mx, &my, P, 0, ref_index, 
s->p_mv_table, mv_scale, 0, 16);
     }
-- 
2.52.0


>From a70f21a538a193f86ba0443b62c6184f5b497b9b Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Thu, 17 Sep 2026 18:41:48 +0200
Subject: [PATCH 7/7] avcodec/mpegvideo_enc: compute the frame distances only
 for MPEG-4

time, last_non_b_time, pp_time and pb_time are the MPEG-4 time
bookkeeping; the only other reader was the B frame motion estimation
of the MPEG-1/2 encoders, which now scales with the timestamps.
---
 libavcodec/mpegvideo_enc.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c
index f52e3617d7..6ce342dde8 100644
--- a/libavcodec/mpegvideo_enc.c
+++ b/libavcodec/mpegvideo_enc.c
@@ -3691,12 +3691,10 @@ static int encode_picture(MPVMainEncContext *const m, 
const AVPacket *pkt)
     int bits;
     int context_count = s->c.slice_context_count;
 
-    /* we need to initialize some time vars before we can encode B-frames */
-    // RAL: Condition added for MPEG1VIDEO
-    if (s->c.out_format == FMT_MPEG1 || (s->c.h263_pred && 
s->c.msmpeg4_version == MSMP4_UNUSED))
+    if (CONFIG_MPEG4_ENCODER && s->c.codec_id == AV_CODEC_ID_MPEG4) {
         set_frame_distances(s);
-    if (CONFIG_MPEG4_ENCODER && s->c.codec_id == AV_CODEC_ID_MPEG4)
         ff_set_mpeg4_time(s);
+    }
 
 //    s->lambda = s->c.cur_pic.ptr->quality; //FIXME qscale / ... stuff for ME 
rate distortion
 
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to