avcodec/snowenc: fix out-of-bounds memcpy in get_block_rd() for narrow planes
authorBogdan Lisman <bogdan@pydevsolutions.com>
Mon, 15 Jun 2026 01:07:59 +0000 (04:07 +0300)
committerMichael Niedermayer <michael@niedermayer.cc>
Mon, 15 Jun 2026 21:57:25 +0000 (23:57 +0200)
For an edge block, get_block_rd() copies the full-OBMC-weight central
region directly from cur[] into the reconstruction.  It moved one
boundary to block_w/block_h but overwrote the in-plane clip (x0/x1/y0/y1
computed earlier from the plane size) instead of intersecting with it.
When a plane is narrower than block_w - e.g. a tiny field/chroma plane
produced by the mcdeint filter - the right-edge case left x0 = block_w
while x1 stayed clipped to w - sx < block_w, so x1 - x0 became negative
and was passed to memcpy() as a huge size_t, crashing with SIGSEGV.

Intersect the moved boundaries with the existing clip so the copy region
stays inside the plane and the memcpy length can never be negative.

Reproducible with the GPL mcdeint filter in slow/extra_slow mode, e.g.

    ffmpeg -f lavfi -i testsrc=s=5x32 -vf mcdeint=mode=slow -f null -

This is a separate crash from the get_dc() SIGFPE (ticket #7779) reached
through the same iterative_me() path.  Add a lavfi-based FATE regression
test.

Signed-off-by: Bogdan Lisman <bogdan@pydevsolutions.com>
(cherry picked from commit 11684476268e5c518f70e9e120f4fc6ed58ff3ef)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
libavcodec/snowenc.c
tests/fate/filter-video.mak
tests/ref/fate/filter-mcdeint-slow-edge [new file with mode: 0644]

index 5312d48e9989a3a2ffeee01030aa65ccc9d7eb66..a30a89520050abbc73c645f71aa88776de63b7b7 100644 (file)
@@ -820,13 +820,14 @@ static int get_block_rd(SnowEncContext *enc, int mb_x, int mb_y,
         && (mb_x == 0 || mb_x == b_stride-1)
         && (mb_y == 0 || mb_y == b_height-1)){
         if(mb_x == 0)
-            x1 = block_w;
+            x1 = FFMIN(x1, block_w);
         else
-            x0 = block_w;
+            x0 = FFMAX(x0, block_w);
         if(mb_y == 0)
-            y1 = block_h;
+            y1 = FFMIN(y1, block_h);
         else
-            y0 = block_h;
+            y0 = FFMAX(y0, block_h);
+        x0 = FFMIN(x0, x1);
         for(y=y0; y<y1; y++)
             memcpy(dst + sx+x0 + (sy+y)*ref_stride, cur + x0 + y*ref_stride, x1-x0);
     }
index 07b8632c6f0d6747c7d83dc5dc74f8388b4f765b..cfb9b554629a6fe7b2642c93dfab04b2a0cc6f91 100644 (file)
@@ -51,6 +51,9 @@ fate-filter-mcdeint-medium: CMD = framecrc -flags bitexact -idct simple -i $(TAR
 
 FATE_FILTER_SAMPLES-$(call FILTERDEMDEC, MCDEINT, MPEGTS, MPEG2VIDEO, SNOW_ENCODER) += $(FATE_MCDEINT)
 
+FATE_FILTER-$(call FILTERFRAMECRC, MCDEINT TESTSRC, SNOW_ENCODER) += fate-filter-mcdeint-slow-edge
+fate-filter-mcdeint-slow-edge: CMD = framecrc -auto_conversion_filters -flags bitexact -lavfi testsrc=s=5x32:r=25:d=1,mcdeint=mode=slow -frames:v 3
+
 FATE_FILTER_SAMPLES-$(call FILTERDEMDEC, CODECVIEW, RM, RV40) += fate-filter-codecview-mvs
 fate-filter-codecview-mvs: CMD = framecrc -flags2 +export_mvs -i $(TARGET_SAMPLES)/real/spygames-2MB.rmvb -vf codecview=mv=pf+bf+bb -frames:v 60 -an
 
diff --git a/tests/ref/fate/filter-mcdeint-slow-edge b/tests/ref/fate/filter-mcdeint-slow-edge
new file mode 100644 (file)
index 0000000..c6411b3
--- /dev/null
@@ -0,0 +1,8 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 5x32
+#sar 0: 1/1
+0,          0,          0,        1,      480, 0xe8a1f302
+0,          1,          1,        1,      480, 0x0789f31c
+0,          2,          2,        1,      480, 0x113af329