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]

Reply via email to