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


>From ce5fe46919165673056ef0efb0ce4e1c07235849 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 7 Nov 2026 22:08:08 +0100
Subject: [PATCH 1/4] swscale: reject interlaced frames whose bottom field
 lacks a chroma row

Fixes: heap-buffer-overflow (read and write)
Fixes: 5TZcJN8RG7oW

Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24851

Out of array read and write Replicated through API with ASAN
---
 libswscale/swscale.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/libswscale/swscale.c b/libswscale/swscale.c
index a7715a79a96..90a9a40d786 100644
--- a/libswscale/swscale.c
+++ b/libswscale/swscale.c
@@ -1535,6 +1535,12 @@ static int validate_params(SwsContext *ctx)
     return 0;
 }
 
+static int bottom_field_fits(const AVFrame *frame, const SwsFormat *field)
+{
+    const int vsub = field->desc->log2_chroma_h;
+    return 2 * AV_CEIL_RSHIFT(field->height, vsub) <= 
AV_CEIL_RSHIFT(frame->height, vsub);
+}
+
 int sws_frame_setup(SwsContext *ctx, const AVFrame *dst, const AVFrame *src)
 {
     SwsInternal *s = sws_internal(ctx);
@@ -1606,6 +1612,13 @@ int sws_frame_setup(SwsContext *ctx, const AVFrame *dst, 
const AVFrame *src)
             goto fail;
         }
 
+        if (field == FIELD_BOTTOM &&
+            (!bottom_field_fits(src, &src_fmt) || !bottom_field_fits(dst, 
&dst_fmt))) {
+            err_msg = "The bottom field has fewer chroma rows than its height 
requires";
+            ret = AVERROR(EINVAL);
+            goto fail;
+        }
+
         src_ok = ff_test_fmt(backends, &src_fmt, 0);
         dst_ok = ff_test_fmt(backends, &dst_fmt, 1);
         if ((!src_ok || !dst_ok) && !ff_fmt_equal(&src_fmt, &dst_fmt)) {
-- 
2.54.0


>From 85e0ffe0d72a1cb7664ff1ffe7ff0d648fccb5d8 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 7 Nov 2026 22:08:08 +0100
Subject: [PATCH 2/4] swscale/graph: double the field linesizes by
 multiplication

Fixes: left shift of negative value
Fixes: n2XAJMw9D94O

Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25387
Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24851

Left shift of a negative value Replicated through UnModified FFmpeg with UBSAN
---
 libswscale/graph.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libswscale/graph.c b/libswscale/graph.c
index d34d2989013..98a04c7876c 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -1026,7 +1026,7 @@ static void get_field(SwsGraph *graph, const SwsFormat 
*fmt,
 
     /* Take only every second line */
     for (int i = 0; i < 4; i++)
-        frame->linesize[i] <<= 1;
+        frame->linesize[i] *= 2;
 
     frame->height = (frame->height + (fmt->field == FIELD_TOP)) >> 1;
 }
-- 
2.54.0


>From 609f5d6b207a480bd7655e9ef63ea17ce4f0dc7a Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 7 Nov 2026 22:08:08 +0100
Subject: [PATCH 3/4] avutil/frame: count the trailing zeros of the whole
 cropping offset

Fixes: passing zero to __builtin_ctz()
Fixes: bHs7sJdzdvhW

Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25020

Passing zero to __builtin_ctz() Replicated through API with UBSAN
---
 libavutil/frame.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavutil/frame.c b/libavutil/frame.c
index 81b77ba5696..c79c1b76d52 100644
--- a/libavutil/frame.c
+++ b/libavutil/frame.c
@@ -802,7 +802,7 @@ int av_frame_apply_cropping(AVFrame *frame, int flags)
         int min_log2_align = INT_MAX;
 
         for (int i = 0; frame->data[i]; i++) {
-            int log2_align = offsets[i] ? ff_ctz(offsets[i]) : INT_MAX;
+            int log2_align = offsets[i] ? ff_ctzll(offsets[i]) : INT_MAX;
             min_log2_align = FFMIN(log2_align, min_log2_align);
         }
 
-- 
2.54.0


>From 1a8519e6ebd0e02b967621e5c395eb17326334c3 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 7 Nov 2026 22:08:08 +0100
Subject: [PATCH 4/4] swscale: write the last pixel of odd width rows in the
 planar to packed YUV converters

Fixes: hEgEOrq8XP97

Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25034

Unwritten last pixel Replicated through UnModified FFmpeg with rawvideo output
---
 libswscale/rgb2rgb.h          | 12 +++++++-----
 libswscale/rgb2rgb_template.c |  6 ++++--
 libswscale/x86/rgb2rgb.c      |  3 ++-
 3 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/libswscale/rgb2rgb.h b/libswscale/rgb2rgb.h
index 6f16f95fa6a..db648f2d668 100644
--- a/libswscale/rgb2rgb.h
+++ b/libswscale/rgb2rgb.h
@@ -144,16 +144,18 @@ extern void (*yuyvtoyuv422)(uint8_t *ydst, uint8_t *udst, 
uint8_t *vdst, const u
 void ff_sws_rgb2rgb_init(void);
 
 /**
- * Packs the chroma positions from..to-1 of one line as YUYV when
- * luma_first is set and as UYVY otherwise.
+ * Packs one line of width pixels, starting at chroma position from, as YUYV
+ * when luma_first is set and as UYVY otherwise. The luma of an odd last
+ * pixel is repeated.
  */
 static inline void ff_pack_yuv422_line(uint8_t *dst, const uint8_t *ysrc,
                                        const uint8_t *usrc, const uint8_t 
*vsrc,
-                                       int from, int to, int luma_first)
+                                       int from, int width, int luma_first)
 {
-    for (int i = from; i < to; i++) {
+    for (int i = from; 2 * i < width; i++) {
+        const int y1 = 2 * i + 1 < width ? 2 * i + 1 : 2 * i;
         dst[4 * i +     !luma_first] = ysrc[2 * i];
-        dst[4 * i + 2 + !luma_first] = ysrc[2 * i + 1];
+        dst[4 * i + 2 + !luma_first] = ysrc[y1];
         dst[4 * i +      luma_first] = usrc[i];
         dst[4 * i + 2 +  luma_first] = vsrc[i];
     }
diff --git a/libswscale/rgb2rgb_template.c b/libswscale/rgb2rgb_template.c
index d857b74afb9..81515904f30 100644
--- a/libswscale/rgb2rgb_template.c
+++ b/libswscale/rgb2rgb_template.c
@@ -399,7 +399,7 @@ static inline void yuvPlanartoyuy2_c(const uint8_t *ysrc, 
const uint8_t *usrc,
             uc     += 2;
             vc     += 2;
         }
-        ff_pack_yuv422_line(dst, ysrc, usrc, vsrc, chromWidth & ~1, 
chromWidth, 1);
+        ff_pack_yuv422_line(dst, ysrc, usrc, vsrc, chromWidth & ~1, width, 1);
 
 #else
         int *idst = (int32_t *)dst;
@@ -417,6 +417,7 @@ static inline void yuvPlanartoyuy2_c(const uint8_t *ysrc, 
const uint8_t *usrc,
             uc++;
             vc++;
         }
+        ff_pack_yuv422_line(dst, ysrc, usrc, vsrc, chromWidth, width, 1);
 #endif
         if ((y & (vertLumPerChroma - 1)) == vertLumPerChroma - 1) {
             usrc += chromStride;
@@ -460,7 +461,7 @@ static inline void yuvPlanartouyvy_c(const uint8_t *ysrc, 
const uint8_t *usrc,
             uc     += 2;
             vc     += 2;
         }
-        ff_pack_yuv422_line(dst, ysrc, usrc, vsrc, chromWidth & ~1, 
chromWidth, 0);
+        ff_pack_yuv422_line(dst, ysrc, usrc, vsrc, chromWidth & ~1, width, 0);
 
 #else
         int *idst = (int32_t *)dst;
@@ -478,6 +479,7 @@ static inline void yuvPlanartouyvy_c(const uint8_t *ysrc, 
const uint8_t *usrc,
             uc++;
             vc++;
         }
+        ff_pack_yuv422_line(dst, ysrc, usrc, vsrc, chromWidth, width, 0);
 #endif
         if ((y & (vertLumPerChroma - 1)) == vertLumPerChroma - 1) {
             usrc += chromStride;
diff --git a/libswscale/x86/rgb2rgb.c b/libswscale/x86/rgb2rgb.c
index f22a9195751..5cf2ec47e2f 100644
--- a/libswscale/x86/rgb2rgb.c
+++ b/libswscale/x86/rgb2rgb.c
@@ -1210,13 +1210,14 @@ static inline void yuvPlanartopacked_mmxext(void 
(*pack_line)(uint8_t *, const u
     for (int y = 0; y < height; y++) {
         //FIXME handle 2 lines at once (fewer prefetches, reuse some chroma, 
but very likely memory-limited anyway)
         if (chromWidth < 8) {
-            ff_pack_yuv422_line(dst, ysrc, usrc, vsrc, 0, chromWidth, 
luma_first);
+            ff_pack_yuv422_line(dst, ysrc, usrc, vsrc, 0, width, luma_first);
         } else {
             pack_line(dst, ysrc, usrc, vsrc, chromWidth & ~7);
             if (chromWidth & 7) {
                 x86_reg i = chromWidth - 8;
                 pack_line(dst + 4 * i, ysrc + 2 * i, usrc + i, vsrc + i, 8);
             }
+            ff_pack_yuv422_line(dst, ysrc, usrc, vsrc, chromWidth, width, 
luma_first);
         }
         if ((y&(vertLumPerChroma-1)) == vertLumPerChroma-1) {
             usrc += chromStride;
-- 
2.54.0

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

Reply via email to