PR #24381 opened by michaelni URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24381 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24381.patch
>From cad0974a1a7257a3d5299820707d78d3744aaff3 Mon Sep 17 00:00:00 2001 From: Sensei Wa1nut4 <[email protected]> Date: Sat, 5 Sep 2026 02:47:35 +0200 Subject: [PATCH 1/3] swscale/uops: pad the horizontal filter weights to whole blocks Fixes: out of array read Fixes: ffmpeg -i poc_input.ppm -vf scale=17:17:flags=unstable -pix_fmt rgba -f rawvideo - Fixes: sAlT1ubFAGko Found-by: Sensei Wa1nut4 Signed-off-by: Michael Niedermayer <[email protected]> --- libswscale/uops_tmpl.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libswscale/uops_tmpl.c b/libswscale/uops_tmpl.c index 52152bcdd5..68449c7602 100644 --- a/libswscale/uops_tmpl.c +++ b/libswscale/uops_tmpl.c @@ -307,9 +307,18 @@ DECL_SETUP(setup_filter_h, params, out) return AVERROR(ENOTSUP); SwsFilterWeights *filter = params->uop->data.kernel; - out->priv.ptr = av_refstruct_ref(filter->weights); + /* The horizontal filter uop reads weights for a full SWS_BLOCK_SIZE + * outputs per block (see read_planar_fh), so the weights array must be + * padded up to the block-aligned output count. Pad the tail with zero + * weights, which contribute nothing to the accumulated sums. */ + const size_t padded = (size_t) FFALIGN(filter->dst_size, SWS_BLOCK_SIZE) * filter->filter_size; + int *weights = av_calloc(padded, sizeof(*weights)); + if (!weights) + return AVERROR(ENOMEM); + memcpy(weights, filter->weights, filter->num_weights * sizeof(*weights)); + out->priv.ptr = weights; out->priv.i32[2] = filter->filter_size; - out->free = ff_op_priv_unref; + out->free = ff_op_priv_free; return 0; } -- 2.52.0 >From ce81ca397249f76bd827f1cbacd7875512075394 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 6 Sep 2026 01:57:34 +0200 Subject: [PATCH 2/3] swscale/ops_dispatch: account for the vertical filter taps in the tail copy Fixes: out of array read Fixes: ffmpeg -cpuflags 0 -i poc_input.ppm -vf scale=17:17:flags=unstable -pix_fmt rgba -f rawvideo - Fixes: sAlT1ubFAGko --- libswscale/ops_dispatch.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c index ac5e0b6438..b08dda9a3e 100644 --- a/libswscale/ops_dispatch.c +++ b/libswscale/ops_dispatch.c @@ -54,6 +54,7 @@ typedef struct SwsOpPass { int palette_idx; int *offsets_y; int filter_size_h; + int filter_size_v; bool memcpy_first; bool memcpy_last; bool memcpy_out; @@ -166,7 +167,7 @@ static inline int get_lines_in(const SwsOpPass *p, const int y, const int h, return h >> base->in_sub_y[plane]; const int y0 = p->offsets_y[y] >> base->in_sub_y[plane]; - const int y1 = p->offsets_y[y + h - 1] >> base->in_sub_y[plane]; + const int y1 = (p->offsets_y[y + h - 1] + p->filter_size_v - 1) >> base->in_sub_y[plane]; return y1 - y0 + 1; } @@ -400,8 +401,11 @@ static void op_pass_run(const SwsFrame *out, const SwsFrame *in, const int y, * memcpy the last column on the output side if unpadded. */ - const bool memcpy_in = p->memcpy_last && y + h == pass->lines || - p->memcpy_first && y == 0; + const int y_in_first = p->offsets_y ? p->offsets_y[y] : y; + const int y_in_last = p->offsets_y ? p->offsets_y[y + h - 1] + p->filter_size_v - 1 + : y + h - 1; + const bool memcpy_in = p->memcpy_last && y_in_last == in->height - 1 || + p->memcpy_first && y_in_first == 0; const bool memcpy_out = p->memcpy_out; const size_t num_blocks = p->num_blocks; const size_t tail_blocks = p->tail_blocks; @@ -435,7 +439,7 @@ static void op_pass_run(const SwsFrame *out, const SwsFrame *in, const int y, /* Input offsets are relative to the base pointer */ if (!exec.in_offset_x || memcpy_in) exec.in[i] += p->tail_off_in; - tail.in[i] += y * tail.in_stride[i]; + tail.in[i] += (y_in_first >> exec.in_sub_y[i]) * tail.in_stride[i]; } for (int i = 0; i < p->planes_out; i++) { exec.out[i] += p->tail_off_out; @@ -640,6 +644,7 @@ static int compile_single(const CompileArgs *args, const SwsOpList *ops, const SwsFilterWeights *filter = read ? read->rw.filter.kernel : NULL; if (read && read->rw.filter.op == SWS_OP_FILTER_V) { p->offsets_y = av_refstruct_ref(filter->offsets); + p->filter_size_v = filter->filter_size; /* Compute relative pointer bumps for each output line */ int32_t *bump = av_malloc_array(filter->dst_size, sizeof(*bump)); -- 2.52.0 >From 5617a69770a5bf1ebafd268b1b8c7366cf269d68 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 6 Sep 2026 05:37:22 +0200 Subject: [PATCH 3/3] swscale/ops_dispatch: pass the line strides to copy_lines() as ptrdiff_t --- libswscale/ops_dispatch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c index b08dda9a3e..a1f43a867c 100644 --- a/libswscale/ops_dispatch.c +++ b/libswscale/ops_dispatch.c @@ -365,8 +365,8 @@ static int op_pass_setup(const SwsFrame *out, const SwsFrame *in, return 0; } -static void copy_lines(uint8_t *dst, const size_t dst_stride, - const uint8_t *src, const size_t src_stride, +static void copy_lines(uint8_t *dst, const ptrdiff_t dst_stride, + const uint8_t *src, const ptrdiff_t src_stride, const int h, const size_t bytes) { for (int y = 0; y < h; y++) { -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
