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


>From c0649acb60a4cb42fbf785b7e8afdca53ff19737 Mon Sep 17 00:00:00 2001
From: Chun-Min Chang <[email protected]>
Date: Thu, 16 Jul 2026 16:57:49 -0700
Subject: [PATCH 1/4] avcodec/vp9: unref next_refs in vp9_decode_flush

vp9_decode_flush() releases s->s.frames[], s->s.refs[] and s->s.ref_frames[]
but leaves s->next_refs[] referenced. Under frame threading,
vp9_decode_update_thread_context() seeds a worker's s->s.refs[] from the source
worker's next_refs[] (not from its refs[]). The pre-flush next_refs therefore
survive a flush and can be resurrected into a worker's refs[] afterwards,
letting a subsequent inter frame pass the reference-availability check and
decode against references that no longer exist.

Unref s->next_refs[] alongside s->s.refs[] so that flushing fully drops the
decoder's reference state.

Fixes: heap out-of-bounds read and write after avcodec_flush_buffers()
Fixes: 34c2zQaUquOl
Found-by: Mozilla's bugmon automation, reviewed and reported by Chun-Min Chang 
<[email protected]>
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/vp9.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libavcodec/vp9.c b/libavcodec/vp9.c
index 7be733a0da..ce4e197c9d 100644
--- a/libavcodec/vp9.c
+++ b/libavcodec/vp9.c
@@ -1866,6 +1866,7 @@ static av_cold void vp9_decode_flush(AVCodecContext 
*avctx)
 
     for (i = 0; i < 8; i++) {
         ff_progress_frame_unref(&s->s.refs[i]);
+        ff_progress_frame_unref(&s->next_refs[i]);
         vp9_frame_unref(&s->s.ref_frames[i]);
     }
 
-- 
2.52.0


>From 39750947e23471fa6fb741fb85c13ede8ad1f527 Mon Sep 17 00:00:00 2001
From: Chun-Min Chang <[email protected]>
Date: Tue, 28 Jul 2026 17:49:35 -0700
Subject: [PATCH 2/4] avcodec/vp8: do not await a negative reference-frame row

vp8_mc_luma()/vp8_mc_chroma() derive the reference row to await from y_off,
which includes the block's motion vector, and never floor it at 0, so a vector
pointing above the frame makes the awaited row negative.

For a 16x16 block the expression still bottoms out at 0 for any vector inside
clamp_mv()'s +-16 px margin, which is why the omission is easy to miss. The
split modes, however, predict 8- and 4-row blocks, where a much smaller vector
suffices: at mb_y 0 with block_h 8, y_off -12 gives (3 - 12 + 8) >> 4 == -1,
and with block_h 4, y_off -8 does. SPLITMV sub-vectors are moreover never
clamped, and NEWMV deltas are applied after clamp_mv(), so larger vectors are
reachable too.

ff_thread_progress_await() returns as soon as progress >= n, and progress is
initialised to -1 under frame threading, so a negative row is treated as
already reached and no happens-before edge with the thread decoding the
reference frame is established. emulated_edge_mc() nevertheless clamps the
source rectangle and reads row 0 of the reference plane, i.e. the row is read
without waiting for it to be written.

Floor the awaited row at 0, the lowest row the subsequent read can touch. The
floor only ever raises a negative row to 0, never above a row already awaited
on the same frame, so it cannot introduce a stall.

Fixes: unsynchronized cross-thread read of reference frame rows
Fixes: IFu1maPxsZFJ
Found-by: Mozilla's AI bot, reviewed and reported by Chun-Min Chang 
<[email protected]>
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/vp8.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavcodec/vp8.c b/libavcodec/vp8.c
index fe154c6914..40564ec9f9 100644
--- a/libavcodec/vp8.c
+++ b/libavcodec/vp8.c
@@ -1848,7 +1848,7 @@ void vp8_mc_luma(VP8Context *s, VP8ThreadData *td, 
uint8_t *dst,
         y_off += mv->y >> 2;
 
         // edge emulation
-        ff_progress_frame_await(ref, (3 + y_off + block_h + subpel_idx[2][my]) 
>> 4);
+        ff_progress_frame_await(ref, FFMAX((3 + y_off + block_h + 
subpel_idx[2][my]) >> 4, 0));
         src += y_off * linesize + x_off;
         if (x_off < mx_idx || x_off >= width  - block_w - subpel_idx[2][mx] ||
             y_off < my_idx || y_off >= height - block_h - subpel_idx[2][my]) {
@@ -1906,7 +1906,7 @@ void vp8_mc_chroma(VP8Context *s, VP8ThreadData *td, 
uint8_t *dst1,
         // edge emulation
         src1 += y_off * linesize + x_off;
         src2 += y_off * linesize + x_off;
-        ff_progress_frame_await(ref, (3 + y_off + block_h + subpel_idx[2][my]) 
>> 3);
+        ff_progress_frame_await(ref, FFMAX((3 + y_off + block_h + 
subpel_idx[2][my]) >> 3, 0));
         if (x_off < mx_idx || x_off >= width  - block_w - subpel_idx[2][mx] ||
             y_off < my_idx || y_off >= height - block_h - subpel_idx[2][my]) {
             s->vdsp.emulated_edge_mc(td->edge_emu_buffer,
-- 
2.52.0


>From 81a41cbd6ac1fbeae76270e046e7126d34420226 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sun, 6 Sep 2026 14:42:56 +0200
Subject: [PATCH 3/4] avcodec/rv34: do not await a negative reference-frame row

Fixes: unsynchronized cross-thread read of reference frame rows
Fixes: IFu1maPxsZFJ
Found-by: Mozilla's AI bot, reviewed and reported by Chun-Min Chang 
<[email protected]>
---
 libavcodec/rv34.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/rv34.c b/libavcodec/rv34.c
index dd5ee05eba..1204260ee1 100644
--- a/libavcodec/rv34.c
+++ b/libavcodec/rv34.c
@@ -735,7 +735,7 @@ static inline void rv34_mc(RV34DecContext *r, const int 
block_type,
         /* wait for the referenced mb row to be finished */
         int mb_row = s->mb_y + ((yoff + my + 5 + 8 * height) >> 4);
         const ThreadProgress *p = dir ? &s->next_pic.ptr->progress : 
&s->last_pic.ptr->progress;
-        ff_thread_progress_await(p, mb_row);
+        ff_thread_progress_await(p, FFMAX(0, mb_row));
     }
 
     dxy = ly*4 + lx;
-- 
2.52.0


>From 99c34afe5dd1d19fa01fafb3af344eead19b04e5 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sun, 6 Sep 2026 22:19:01 +0200
Subject: [PATCH 4/4] avcodec/rv34: do not copy an unused reference line in the
 edge emulation

The luma interpolation filters read two lines above and three lines
below a block, so rv34_mc() needs 8 * height + 5 reference lines, but it
had emulated_edge_mc() copy one more.

Fixes: data race between rv40_loop_filter() and rv34_mc() under frame threading
---
 libavcodec/rv34.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/rv34.c b/libavcodec/rv34.c
index 1204260ee1..654115486a 100644
--- a/libavcodec/rv34.c
+++ b/libavcodec/rv34.c
@@ -755,7 +755,7 @@ static inline void rv34_mc(RV34DecContext *r, const int 
block_type,
         srcY -= 2 + 2*s->linesize;
         s->vdsp.emulated_edge_mc(s->sc.edge_emu_buffer, srcY,
                                  s->linesize, s->linesize,
-                                 (width << 3) + 6, (height << 3) + 6,
+                                 (width << 3) + 6, (height << 3) + 5,
                                  src_x - 2, src_y - 2,
                                  s->h_edge_pos, s->v_edge_pos);
         srcY = s->sc.edge_emu_buffer + 2 + 2*s->linesize;
-- 
2.52.0

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

Reply via email to