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]
