Commit fd1e21e06e for ffmpeg
commit fd1e21e06e8c4e4bda7917f64af8cb3f4c1cb21d
Author: Michael Niedermayer <michael@niedermayer.cc>
Date: Sat Aug 15 14:01:55 2026 +0200
avfilter/vf_xfade: fix out of bounds reads at the transition endpoints
Fixes: #22273
Fixes: #21622
Found-by: scriptituk (#22273), EthanSK (#21622), Qriist (squeezev crash)
diff --git a/libavfilter/vf_xfade.c b/libavfilter/vf_xfade.c
index 8371fdf5ad..8de1f2f0d9 100644
--- a/libavfilter/vf_xfade.c
+++ b/libavfilter/vf_xfade.c
@@ -296,6 +296,11 @@ static inline float smoothstep(float edge0, float edge1, float x)
return t * t * (3.f - 2.f * t);
}
+static inline int wrap_once(int index, int size)
+{
+ return index < 0 ? index + size : index >= size ? index - size : index;
+}
+
#define FADE_TRANSITION(name, type, div) \
static void fade##name##_transition(AVFilterContext *ctx, \
const AVFrame *a, const AVFrame *b, AVFrame *out, \
@@ -1631,7 +1636,8 @@ static void squeezeh##name##_transition(AVFilterContext *ctx,
type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
\
for (int y = 0; y < height; y++) { \
- const float z = .5f + ((slice_start + y) / h - .5f) / progress; \
+ const float z = progress > 0.f ? \
+ .5f + ((slice_start + y) / h - .5f) / progress : -1.f; \
\
if (z < 0.f || z > 1.f) { \
for (int x = 0; x < width; x++) \
@@ -1671,7 +1677,8 @@ static void squeezev##name##_transition(AVFilterContext *ctx,
\
for (int y = 0; y < height; y++) { \
for (int x = 0; x < width; x++) { \
- const float z = .5f + (x / w - .5f) / progress; \
+ const float z = progress > 0.f ? \
+ .5f + (x / w - .5f) / progress : -1.f; \
\
if (z < 0.f || z > 1.f) { \
dst[x] = xf1[x]; \
@@ -1884,7 +1891,7 @@ static void cover##dir##name##_transition(AVFilterContext *ctx,
for (int y = 0; y < height; y++) { \
for (int x = 0; x < width; x++) { \
const int zx = z + x; \
- const int zz = zx % width + width * (zx < 0); \
+ const int zz = wrap_once(zx, width); \
dst[x] = (zx >= 0) && (zx < width) ? xf1[zz] : xf0[x]; \
} \
\
@@ -1916,7 +1923,7 @@ static void cover##dir##name##_transition(AVFilterContext *ctx,
\
for (int y = slice_start; y < slice_end; y++) { \
const int zy = z + y; \
- const int zz = zy % height + height * (zy < 0); \
+ const int zz = wrap_once(zy, height); \
const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \
\
@@ -1952,7 +1959,7 @@ static void reveal##dir##name##_transition(AVFilterContext *ctx,
for (int y = 0; y < height; y++) { \
for (int x = 0; x < width; x++) { \
const int zx = z + x; \
- const int zz = zx % width + width * (zx < 0); \
+ const int zz = wrap_once(zx, width); \
dst[x] = (zx >= 0) && (zx < width) ? xf1[x] : xf0[zz]; \
} \
\
@@ -1984,7 +1991,7 @@ static void reveal##dir##name##_transition(AVFilterContext *ctx,
\
for (int y = slice_start; y < slice_end; y++) { \
const int zy = z + y; \
- const int zz = zy % height + height * (zy < 0); \
+ const int zz = wrap_once(zy, height); \
const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \
const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
\