From b772f5033c729ef2502bee8972cf497661ac096f Mon Sep 17 00:00:00 2001 From: Chun-Min Chang Date: Tue, 28 Jul 2026 17:49:35 -0700 Subject: [PATCH] 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 Signed-off-by: Michael Niedermayer --- 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,