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

Fixes: nan_pad_test.c

This PR depends on one commit from 
https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24381 (which is why theres a 2nd 
commit in this PR)


>From 619aae3a90e259cd47cf05f9ff98117be6f88dde Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sun, 6 Sep 2026 01:57:34 +0200
Subject: [PATCH 1/2] 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 dd53ce8000bb78a5d76ff4c1c8ea66ff5a2dad92 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sun, 6 Sep 2026 05:37:09 +0200
Subject: [PATCH 2/2] swscale/ops_dispatch: fix leaking NaN from outside the
 picture into it

Fixes: nan_pad_test.c
---
 libswscale/ops_dispatch.c | 24 +++++++++---------------
 1 file changed, 9 insertions(+), 15 deletions(-)

diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c
index b08dda9a3e..7fa4c90e8f 100644
--- a/libswscale/ops_dispatch.c
+++ b/libswscale/ops_dispatch.c
@@ -55,8 +55,7 @@ typedef struct SwsOpPass {
     int *offsets_y;
     int filter_size_h;
     int filter_size_v;
-    bool memcpy_first;
-    bool memcpy_last;
+    bool memcpy_in;
     bool memcpy_out;
     size_t tail_blocks;
     uint8_t *tail_buf; /* extra memory for fixing unpadded tails */
@@ -223,8 +222,7 @@ static int op_pass_setup(const SwsFrame *out, const 
SwsFrame *in,
     if (aligned_w < width) /* overflow */
         return AVERROR(EINVAL);
     p->num_blocks   = num_blocks;
-    p->memcpy_first = false;
-    p->memcpy_last  = false;
+    p->memcpy_in    = false;
     p->memcpy_out   = false;
 
     size_t safe_blocks = num_blocks;
@@ -250,8 +248,7 @@ static int op_pass_setup(const SwsFrame *out, const 
SwsFrame *in,
         }
 
         if (safe_blocks_in < num_blocks) {
-            p->memcpy_first |= in->linesize[idx] < 0;
-            p->memcpy_last  |= in->linesize[idx] > 0;
+            p->memcpy_in = true;
             safe_blocks = FFMIN(safe_blocks, safe_blocks_in);
         }
 
@@ -281,7 +278,7 @@ static int op_pass_setup(const SwsFrame *out, const 
SwsFrame *in,
         exec->in_stride[1] = exec->in_bump[1] = 0;
     }
 
-    const bool memcpy_in = p->memcpy_first || p->memcpy_last;
+    const bool memcpy_in = p->memcpy_in;
     if (!memcpy_in && !p->memcpy_out) {
         av_assert0(safe_blocks == num_blocks);
         return 0;
@@ -391,10 +388,10 @@ static void op_pass_run(const SwsFrame *out, const 
SwsFrame *in, const int y,
      *  To ensure safety, we need to consider the following:
      *
      * 1. We can overread the input, unless this is the last line of an
-     *    unpadded buffer. All defined operations can handle arbitrary pixel
-     *    input, so overread of arbitrary data is fine. For flipped images,
-     *    this condition is actually *inverted* to where the first line is
-     *    the one at the end of the buffer.
+     *    unpadded buffer (the first line for flipped images). A vertical
+     *    filter lets any slice read that line, so every slice of an
+     *    unpadded input takes the memcpy path. All defined operations can
+     *    handle arbitrary pixel input, so overread of arbitrary data is fine.
      *
      * 2. We can overwrite the output, as long as we don't write more than the
      *    amount of pixels that fit into one linesize. So we always need to
@@ -402,10 +399,7 @@ static void op_pass_run(const SwsFrame *out, const 
SwsFrame *in, const int y,
      */
 
     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_in  = p->memcpy_in;
     const bool memcpy_out = p->memcpy_out;
     const size_t num_blocks  = p->num_blocks;
     const size_t tail_blocks = p->tail_blocks;
-- 
2.52.0

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

Reply via email to