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

libavfilter does not reconfigure a filter when the frames on its input
change size or format after the graph was configured; a scale filter
with eval=frame nevertheless rewrites its outlink per frame, and with
-reinit_filter 0 the ffmpeg tool feeds changed frames into the graph.
Most filters size their buffers from the link at configuration time
and then overrun them.

ff_filter_config_links() now records format and size of each link and
ff_filter_frame() rejects a frame that differs, unless the filter
declares with FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS that it handles such
frames. The flag replaces the list of filter names exempt from the
consistency assertion. idet is not carried over, it reads across
frames of different sizes.

Fixes: out of array write
Fixes: RqgTntCAW2qN
Found-by: mzfr
Replicated through UnModified FFmpeg

The out of array reads in idet (frames of different sizes) and showinfo
(a smaller frame with -reinit_filter 0) were found during triage of
the same report.


>From 4f31f507458f4c8777c34cad20ba555de000d834 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Tue, 29 Sep 2026 04:20:58 +0200
Subject: [PATCH 1/4] avfilter: reject frames whose size or format changed
 after the graph was configured

libavfilter does not reconfigure a filter when the frames on its input
change size or format after the graph was configured; a scale filter
with eval=frame nevertheless rewrites its outlink per frame, and with
-reinit_filter 0 the ffmpeg tool feeds changed frames into the graph.
Most filters size their buffers from the link at configuration time
and then overrun them.

ff_filter_config_links() now records format and size of each link and
ff_filter_frame() rejects a frame that differs, unless the filter
declares with FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS that it handles such
frames. The flag replaces the list of filter names exempt from the
consistency assertion. idet is not carried over, it reads across
frames of different sizes.

Fixes: out of array write
Fixes: RqgTntCAW2qN
Found-by: mzfr
Replicated through UnModified FFmpeg

The out of array reads in idet (frames of different sizes) and showinfo
(a smaller frame with -reinit_filter 0) were found during triage of
the same report.
---
 libavfilter/avfilter.c          | 21 +++++++++++----------
 libavfilter/avfilter_internal.h |  5 +++++
 libavfilter/buffersink.c        |  1 +
 libavfilter/filters.h           |  7 +++++++
 libavfilter/vf_format.c         |  2 ++
 libavfilter/vf_hqdn3d.c         |  1 +
 libavfilter/vf_lcevc.c          |  1 +
 libavfilter/vf_libplacebo.c     |  2 +-
 libavfilter/vf_null.c           |  1 +
 libavfilter/vf_scale.c          |  2 ++
 libavfilter/vf_scale_npp.c      |  4 ++--
 11 files changed, 34 insertions(+), 13 deletions(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index d10836df3b..1605458efb 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -447,6 +447,9 @@ int ff_filter_config_links(AVFilterContext *filter)
                     return ret;
                 }
 
+            li->cfg_format = link->format;
+            li->cfg_w      = link->w;
+            li->cfg_h      = link->h;
             li->init_state = AVLINK_INIT;
         }
     }
@@ -1073,16 +1076,14 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
 
     /* Consistency checks */
     if (link->type == AVMEDIA_TYPE_VIDEO) {
-        if (strcmp(link->dst->filter->name, "buffersink") &&
-            strcmp(link->dst->filter->name, "format") &&
-            strcmp(link->dst->filter->name, "idet") &&
-            strcmp(link->dst->filter->name, "null") &&
-            strcmp(link->dst->filter->name, "scale") &&
-            strcmp(link->dst->filter->name, "libplacebo") &&
-            strcmp(link->dst->filter->name, "hqdn3d")) {
-            av_assert1(frame->format        == link->format);
-            av_assert1(frame->width         == link->w);
-            av_assert1(frame->height        == link->h);
+        if (!(fffilter(link->dst->filter)->flags_internal & 
FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS)) {
+            if (frame->format != li->cfg_format ||
+                frame->width  != li->cfg_w      ||
+                frame->height != li->cfg_h) {
+                av_log(link->dst, AV_LOG_ERROR,
+                       "Frame size or format changed without filter graph 
reinitialization\n");
+                goto error;
+            }
             if (av_pix_fmt_desc_get(link->format)->flags & 
AV_PIX_FMT_FLAG_ALPHA)
                 av_assert1(frame->alpha_mode == link->alpha_mode);
         }
diff --git a/libavfilter/avfilter_internal.h b/libavfilter/avfilter_internal.h
index 808dd6af38..3056b2edc3 100644
--- a/libavfilter/avfilter_internal.h
+++ b/libavfilter/avfilter_internal.h
@@ -45,6 +45,11 @@ typedef struct FilterLinkInternal {
      */
     FFFrameQueue fifo;
 
+    /**
+     * Format and size of the video frames the link was configured with.
+     */
+    int cfg_format, cfg_w, cfg_h;
+
     /**
      * If set, the source filter can not generate a frame as is.
      * The goal is to avoid repeatedly calling the request_frame() method on
diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c
index e829007557..dcbaccaf5b 100644
--- a/libavfilter/buffersink.c
+++ b/libavfilter/buffersink.c
@@ -390,6 +390,7 @@ const FFFilter ff_vsink_buffer = {
     .activate      = activate,
     FILTER_INPUTS(ff_video_default_filterpad),
     FILTER_QUERY_FUNC2(vsink_query_formats),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 
 static const AVFilterPad inputs_audio[] = {
diff --git a/libavfilter/filters.h b/libavfilter/filters.h
index 57540d7698..283c436aff 100644
--- a/libavfilter/filters.h
+++ b/libavfilter/filters.h
@@ -207,6 +207,13 @@ static inline FilterLink* ff_filter_link(AVFilterLink 
*link)
  */
 #define FF_FILTER_FLAG_HWFRAME_AWARE (1 << 0)
 
+/**
+ * The filter handles input frames whose format or size differ from the ones
+ * its input link was configured with. ff_filter_frame() rejects such frames
+ * for every other filter.
+ */
+#define FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS (1 << 1)
+
 /**
  * Find the index of a link.
  *
diff --git a/libavfilter/vf_format.c b/libavfilter/vf_format.c
index fd3cc4a647..153de006ae 100644
--- a/libavfilter/vf_format.c
+++ b/libavfilter/vf_format.c
@@ -204,6 +204,7 @@ const FFFilter ff_vf_format = {
     FILTER_OUTPUTS(ff_video_default_filterpad),
 
     FILTER_QUERY_FUNC2(query_formats),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif /* CONFIG_FORMAT_FILTER */
 
@@ -224,5 +225,6 @@ const FFFilter ff_vf_noformat = {
     FILTER_OUTPUTS(ff_video_default_filterpad),
 
     FILTER_QUERY_FUNC2(query_formats),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif /* CONFIG_NOFORMAT_FILTER */
diff --git a/libavfilter/vf_hqdn3d.c b/libavfilter/vf_hqdn3d.c
index 98c05e886c..80495aaa27 100644
--- a/libavfilter/vf_hqdn3d.c
+++ b/libavfilter/vf_hqdn3d.c
@@ -417,4 +417,5 @@ const FFFilter ff_vf_hqdn3d = {
     FILTER_OUTPUTS(ff_video_default_filterpad),
     FILTER_PIXFMTS_ARRAY(pix_fmts),
     .process_command = process_command,
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vf_lcevc.c b/libavfilter/vf_lcevc.c
index 79609434b4..4ccd5ba659 100644
--- a/libavfilter/vf_lcevc.c
+++ b/libavfilter/vf_lcevc.c
@@ -456,4 +456,5 @@ const FFFilter ff_vf_lcevc = {
     .priv_size     = sizeof(LCEVCContext),
     .init          = init,
     .uninit        = uninit,
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vf_libplacebo.c b/libavfilter/vf_libplacebo.c
index 5d3807e36d..bb835941e8 100644
--- a/libavfilter/vf_libplacebo.c
+++ b/libavfilter/vf_libplacebo.c
@@ -1809,5 +1809,5 @@ const FFFilter ff_vf_libplacebo = {
     .process_command = &libplacebo_process_command,
     FILTER_OUTPUTS(libplacebo_outputs),
     FILTER_QUERY_FUNC2(libplacebo_query_format),
-    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE | 
FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vf_null.c b/libavfilter/vf_null.c
index 7071cc959f..00114cc79e 100644
--- a/libavfilter/vf_null.c
+++ b/libavfilter/vf_null.c
@@ -32,4 +32,5 @@ const FFFilter ff_vf_null = {
     .p.flags       = AVFILTER_FLAG_METADATA_ONLY,
     FILTER_INPUTS(ff_video_default_filterpad),
     FILTER_OUTPUTS(ff_video_default_filterpad),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vf_scale.c b/libavfilter/vf_scale.c
index f2d3021080..6b1797abc5 100644
--- a/libavfilter/vf_scale.c
+++ b/libavfilter/vf_scale.c
@@ -1200,6 +1200,7 @@ const FFFilter ff_vf_scale = {
     FILTER_QUERY_FUNC2(query_formats),
     .activate        = activate,
     .process_command = process_command,
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 
 static const AVClass *scale2ref_child_class_iterate(void **iter)
@@ -1267,4 +1268,5 @@ const FFFilter ff_vf_scale2ref = {
     FILTER_OUTPUTS(avfilter_vf_scale2ref_outputs),
     FILTER_QUERY_FUNC2(query_formats),
     .process_command = process_command,
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vf_scale_npp.c b/libavfilter/vf_scale_npp.c
index a2a68e72fe..214e750825 100644
--- a/libavfilter/vf_scale_npp.c
+++ b/libavfilter/vf_scale_npp.c
@@ -1032,7 +1032,7 @@ const FFFilter ff_vf_scale_npp = {
 
     FILTER_SINGLE_PIXFMT(AV_PIX_FMT_CUDA),
 
-    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE | 
FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 
 static const AVFilterPad nppscale2ref_inputs[] = {
@@ -1080,5 +1080,5 @@ const FFFilter ff_vf_scale2ref_npp = {
 
     FILTER_SINGLE_PIXFMT(AV_PIX_FMT_CUDA),
 
-    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE | 
FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
-- 
2.52.0


>From 61d138db516ff5dbdf021e51d50958143fdc66c9 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Tue, 29 Sep 2026 04:41:06 +0200
Subject: [PATCH 2/4] avfilter: declare the filters that carry no frame
 parameter state as handling changes

These filters never touch pixel data and keep no state derived from the
size or format of their input link, so frames that change size or
format after the graph was configured pass through them unharmed.
Marking them keeps the common -reinit_filter 0 chains working.

Found during triage of the security report RqgTntCAW2qN.
---
 libavfilter/f_bench.c          | 1 +
 libavfilter/f_cue.c            | 1 +
 libavfilter/f_latency.c        | 1 +
 libavfilter/f_loop.c           | 1 +
 libavfilter/f_metadata.c       | 1 +
 libavfilter/f_perms.c          | 1 +
 libavfilter/f_realtime.c       | 1 +
 libavfilter/f_reverse.c        | 1 +
 libavfilter/f_segment.c        | 1 +
 libavfilter/f_sendcmd.c        | 1 +
 libavfilter/f_sidedata.c       | 1 +
 libavfilter/setpts.c           | 1 +
 libavfilter/settb.c            | 1 +
 libavfilter/split.c            | 1 +
 libavfilter/trim.c             | 1 +
 libavfilter/vf_aspect.c        | 2 ++
 libavfilter/vf_ccrepack.c      | 1 +
 libavfilter/vf_dejudder.c      | 1 +
 libavfilter/vf_fps.c           | 1 +
 libavfilter/vf_framestep.c     | 1 +
 libavfilter/vf_fsync.c         | 1 +
 libavfilter/vf_random.c        | 1 +
 libavfilter/vf_setparams.c     | 3 +++
 libavfilter/vf_shuffleframes.c | 1 +
 libavfilter/vf_tpad.c          | 1 +
 libavfilter/vf_vfrdet.c        | 1 +
 libavfilter/vsink_nullsink.c   | 1 +
 27 files changed, 30 insertions(+)

diff --git a/libavfilter/f_bench.c b/libavfilter/f_bench.c
index 2546adc281..0312a107fb 100644
--- a/libavfilter/f_bench.c
+++ b/libavfilter/f_bench.c
@@ -110,6 +110,7 @@ const FFFilter ff_vf_bench = {
     .init          = init,
     FILTER_INPUTS(bench_inputs),
     FILTER_OUTPUTS(ff_video_default_filterpad),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif /* CONFIG_BENCH_FILTER */
 
diff --git a/libavfilter/f_cue.c b/libavfilter/f_cue.c
index 63027c7c2e..f7badb0ec0 100644
--- a/libavfilter/f_cue.c
+++ b/libavfilter/f_cue.c
@@ -108,6 +108,7 @@ const FFFilter ff_vf_cue = {
     FILTER_INPUTS(ff_video_default_filterpad),
     FILTER_OUTPUTS(ff_video_default_filterpad),
     .activate    = activate,
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif /* CONFIG_CUE_FILTER */
 
diff --git a/libavfilter/f_latency.c b/libavfilter/f_latency.c
index 00b55ddb4b..80d379219d 100644
--- a/libavfilter/f_latency.c
+++ b/libavfilter/f_latency.c
@@ -111,6 +111,7 @@ const FFFilter ff_vf_latency = {
     .activate      = activate,
     FILTER_INPUTS(ff_video_default_filterpad),
     FILTER_OUTPUTS(ff_video_default_filterpad),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 
 #endif // CONFIG_LATENCY_FILTER
diff --git a/libavfilter/f_loop.c b/libavfilter/f_loop.c
index 1984fad70d..bfa4590ac2 100644
--- a/libavfilter/f_loop.c
+++ b/libavfilter/f_loop.c
@@ -466,5 +466,6 @@ const FFFilter ff_vf_loop = {
     .activate    = activate,
     FILTER_INPUTS(ff_video_default_filterpad),
     FILTER_OUTPUTS(ff_video_default_filterpad),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif /* CONFIG_LOOP_FILTER */
diff --git a/libavfilter/f_metadata.c b/libavfilter/f_metadata.c
index c2e0a50e13..218ba5282c 100644
--- a/libavfilter/f_metadata.c
+++ b/libavfilter/f_metadata.c
@@ -410,5 +410,6 @@ const FFFilter ff_vf_metadata = {
     .uninit      = uninit,
     FILTER_INPUTS(inputs),
     FILTER_OUTPUTS(ff_video_default_filterpad),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif /* CONFIG_METADATA_FILTER */
diff --git a/libavfilter/f_perms.c b/libavfilter/f_perms.c
index 28d32e6b94..7c35ceb770 100644
--- a/libavfilter/f_perms.c
+++ b/libavfilter/f_perms.c
@@ -160,5 +160,6 @@ const FFFilter ff_vf_perms = {
     FILTER_INPUTS(perms_inputs),
     FILTER_OUTPUTS(ff_video_default_filterpad),
     .process_command = ff_filter_process_command,
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif /* CONFIG_PERMS_FILTER */
diff --git a/libavfilter/f_realtime.c b/libavfilter/f_realtime.c
index e142e0ca57..3076c99580 100644
--- a/libavfilter/f_realtime.c
+++ b/libavfilter/f_realtime.c
@@ -96,6 +96,7 @@ const FFFilter ff_vf_realtime = {
     FILTER_INPUTS(avfilter_vf_realtime_inputs),
     FILTER_OUTPUTS(ff_video_default_filterpad),
     .process_command = ff_filter_process_command,
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif /* CONFIG_REALTIME_FILTER */
 
diff --git a/libavfilter/f_reverse.c b/libavfilter/f_reverse.c
index 4bd963288f..50b08dc06e 100644
--- a/libavfilter/f_reverse.c
+++ b/libavfilter/f_reverse.c
@@ -155,6 +155,7 @@ const FFFilter ff_vf_reverse = {
     .uninit      = uninit,
     FILTER_INPUTS(reverse_inputs),
     FILTER_OUTPUTS(reverse_outputs),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 
 #endif /* CONFIG_REVERSE_FILTER */
diff --git a/libavfilter/f_segment.c b/libavfilter/f_segment.c
index ece53ae56d..0b67afbb83 100644
--- a/libavfilter/f_segment.c
+++ b/libavfilter/f_segment.c
@@ -299,6 +299,7 @@ const FFFilter ff_vf_segment = {
     .priv_size   = sizeof(SegmentContext),
     .activate    = activate,
     FILTER_INPUTS(segment_inputs),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif // CONFIG_SEGMENT_FILTER
 
diff --git a/libavfilter/f_sendcmd.c b/libavfilter/f_sendcmd.c
index 304658ae3d..27029a0e44 100644
--- a/libavfilter/f_sendcmd.c
+++ b/libavfilter/f_sendcmd.c
@@ -610,6 +610,7 @@ const FFFilter ff_vf_sendcmd = {
     .priv_size   = sizeof(SendCmdContext),
     FILTER_INPUTS(sendcmd_inputs),
     FILTER_OUTPUTS(ff_video_default_filterpad),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 
 #endif
diff --git a/libavfilter/f_sidedata.c b/libavfilter/f_sidedata.c
index 6c81c6b6a5..62dde1306e 100644
--- a/libavfilter/f_sidedata.c
+++ b/libavfilter/f_sidedata.c
@@ -219,5 +219,6 @@ const FFFilter ff_vf_sidedata = {
     .init        = init,
     FILTER_INPUTS(inputs),
     FILTER_OUTPUTS(outputs),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif /* CONFIG_SIDEDATA_FILTER */
diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c
index 12ace0d43b..5648a9ffae 100644
--- a/libavfilter/setpts.c
+++ b/libavfilter/setpts.c
@@ -350,6 +350,7 @@ const FFFilter ff_vf_setpts = {
 
     FILTER_INPUTS(avfilter_vf_setpts_inputs),
     FILTER_OUTPUTS(outputs_video),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif /* CONFIG_SETPTS_FILTER */
 
diff --git a/libavfilter/settb.c b/libavfilter/settb.c
index 6000c9c5a6..c693239d19 100644
--- a/libavfilter/settb.c
+++ b/libavfilter/settb.c
@@ -181,6 +181,7 @@ const FFFilter ff_vf_settb = {
     FILTER_INPUTS(ff_video_default_filterpad),
     FILTER_OUTPUTS(avfilter_vf_settb_outputs),
     .activate    = activate,
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif /* CONFIG_SETTB_FILTER */
 
diff --git a/libavfilter/split.c b/libavfilter/split.c
index d0bd0c3b2a..733f1fa298 100644
--- a/libavfilter/split.c
+++ b/libavfilter/split.c
@@ -133,6 +133,7 @@ const FFFilter ff_vf_split = {
     .init        = split_init,
     .activate    = activate,
     FILTER_INPUTS(ff_video_default_filterpad),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 
 const FFFilter ff_af_asplit = {
diff --git a/libavfilter/trim.c b/libavfilter/trim.c
index 6d1016ac81..1077ff3d1a 100644
--- a/libavfilter/trim.c
+++ b/libavfilter/trim.c
@@ -358,6 +358,7 @@ const FFFilter ff_vf_trim = {
     .priv_size   = sizeof(TrimContext),
     FILTER_INPUTS(trim_inputs),
     FILTER_OUTPUTS(ff_video_default_filterpad),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif // CONFIG_TRIM_FILTER
 
diff --git a/libavfilter/vf_aspect.c b/libavfilter/vf_aspect.c
index 7ec1035336..b35592e946 100644
--- a/libavfilter/vf_aspect.c
+++ b/libavfilter/vf_aspect.c
@@ -195,6 +195,7 @@ const FFFilter ff_vf_setdar = {
     .priv_size   = sizeof(AspectContext),
     FILTER_INPUTS(aspect_inputs),
     FILTER_OUTPUTS(avfilter_vf_setdar_outputs),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 
 #endif /* CONFIG_SETDAR_FILTER */
@@ -250,6 +251,7 @@ const FFFilter ff_vf_setsar = {
     .priv_size   = sizeof(AspectContext),
     FILTER_INPUTS(aspect_inputs),
     FILTER_OUTPUTS(avfilter_vf_setsar_outputs),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 
 #endif /* CONFIG_SETSAR_FILTER */
diff --git a/libavfilter/vf_ccrepack.c b/libavfilter/vf_ccrepack.c
index f3f0c69676..032c655230 100644
--- a/libavfilter/vf_ccrepack.c
+++ b/libavfilter/vf_ccrepack.c
@@ -95,4 +95,5 @@ const FFFilter ff_vf_ccrepack = {
     .priv_size   = sizeof(CCRepackContext),
     FILTER_INPUTS(avfilter_vf_ccrepack_inputs),
     FILTER_OUTPUTS(ff_video_default_filterpad),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vf_dejudder.c b/libavfilter/vf_dejudder.c
index 9d0c4717d2..adb4367846 100644
--- a/libavfilter/vf_dejudder.c
+++ b/libavfilter/vf_dejudder.c
@@ -183,4 +183,5 @@ const FFFilter ff_vf_dejudder = {
     FILTER_OUTPUTS(dejudder_outputs),
     .init        = dejudder_init,
     .uninit      = dejudder_uninit,
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vf_fps.c b/libavfilter/vf_fps.c
index 0257a8ef78..f8f20e3a9f 100644
--- a/libavfilter/vf_fps.c
+++ b/libavfilter/vf_fps.c
@@ -397,4 +397,5 @@ const FFFilter ff_vf_fps = {
     .activate    = activate,
     FILTER_INPUTS(ff_video_default_filterpad),
     FILTER_OUTPUTS(avfilter_vf_fps_outputs),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vf_framestep.c b/libavfilter/vf_framestep.c
index 02beda6cf3..2d9efd5e7b 100644
--- a/libavfilter/vf_framestep.c
+++ b/libavfilter/vf_framestep.c
@@ -98,4 +98,5 @@ const FFFilter ff_vf_framestep = {
     .priv_size   = sizeof(FrameStepContext),
     FILTER_INPUTS(framestep_inputs),
     FILTER_OUTPUTS(framestep_outputs),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vf_fsync.c b/libavfilter/vf_fsync.c
index 02f10a9bd8..3fb65e13c3 100644
--- a/libavfilter/vf_fsync.c
+++ b/libavfilter/vf_fsync.c
@@ -300,4 +300,5 @@ const FFFilter ff_vf_fsync = {
     .formats_state = FF_FILTER_FORMATS_PASSTHROUGH,
     FILTER_INPUTS(ff_video_default_filterpad),
     FILTER_OUTPUTS(fsync_outputs),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vf_random.c b/libavfilter/vf_random.c
index ca95ca2c10..42775abb78 100644
--- a/libavfilter/vf_random.c
+++ b/libavfilter/vf_random.c
@@ -150,4 +150,5 @@ const FFFilter ff_vf_random = {
     .uninit      = uninit,
     FILTER_INPUTS(random_inputs),
     FILTER_OUTPUTS(random_outputs),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vf_setparams.c b/libavfilter/vf_setparams.c
index 17ffa098c2..d97fd20a61 100644
--- a/libavfilter/vf_setparams.c
+++ b/libavfilter/vf_setparams.c
@@ -227,6 +227,7 @@ const FFFilter ff_vf_setparams = {
     FILTER_INPUTS(inputs),
     FILTER_OUTPUTS(ff_video_default_filterpad),
     FILTER_QUERY_FUNC2(query_formats),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 
 #if CONFIG_SETRANGE_FILTER
@@ -270,6 +271,7 @@ const FFFilter ff_vf_setrange = {
     FILTER_INPUTS(inputs),
     FILTER_OUTPUTS(ff_video_default_filterpad),
     FILTER_QUERY_FUNC2(query_formats),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif /* CONFIG_SETRANGE_FILTER */
 
@@ -307,5 +309,6 @@ const FFFilter ff_vf_setfield = {
     .init        = init_setfield,
     FILTER_INPUTS(inputs),
     FILTER_OUTPUTS(ff_video_default_filterpad),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
 #endif /* CONFIG_SETFIELD_FILTER */
diff --git a/libavfilter/vf_shuffleframes.c b/libavfilter/vf_shuffleframes.c
index 7d16fcc497..c7f29517ca 100644
--- a/libavfilter/vf_shuffleframes.c
+++ b/libavfilter/vf_shuffleframes.c
@@ -157,4 +157,5 @@ const FFFilter ff_vf_shuffleframes = {
     .uninit        = uninit,
     FILTER_INPUTS(shuffleframes_inputs),
     FILTER_OUTPUTS(ff_video_default_filterpad),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vf_tpad.c b/libavfilter/vf_tpad.c
index 7580fbaa5b..a1c9aecd35 100644
--- a/libavfilter/vf_tpad.c
+++ b/libavfilter/vf_tpad.c
@@ -250,4 +250,5 @@ const FFFilter ff_vf_tpad = {
     FILTER_INPUTS(tpad_inputs),
     FILTER_OUTPUTS(ff_video_default_filterpad),
     FILTER_QUERY_FUNC2(query_formats),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vf_vfrdet.c b/libavfilter/vf_vfrdet.c
index f99bc71de7..ebd8b6e100 100644
--- a/libavfilter/vf_vfrdet.c
+++ b/libavfilter/vf_vfrdet.c
@@ -106,4 +106,5 @@ const FFFilter ff_vf_vfrdet = {
     .uninit      = uninit,
     FILTER_INPUTS(vfrdet_inputs),
     FILTER_OUTPUTS(ff_video_default_filterpad),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
diff --git a/libavfilter/vsink_nullsink.c b/libavfilter/vsink_nullsink.c
index 2a671e1402..ddbd67a9a3 100644
--- a/libavfilter/vsink_nullsink.c
+++ b/libavfilter/vsink_nullsink.c
@@ -40,4 +40,5 @@ const FFFilter ff_vsink_nullsink = {
     .p.outputs   = NULL,
     .priv_size   = 0,
     FILTER_INPUTS(avfilter_vsink_nullsink_inputs),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
-- 
2.52.0


>From f66c60ae19d2e8e76d286533ac0af6268489d371 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Tue, 29 Sep 2026 04:54:49 +0200
Subject: [PATCH 3/4] avfilter/vf_showinfo: describe the frame it received, not
 the link

Found during triage of the security report RqgTntCAW2qN.
---
 libavfilter/vf_showinfo.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libavfilter/vf_showinfo.c b/libavfilter/vf_showinfo.c
index 4ac3d45dc0..4aca73bc85 100644
--- a/libavfilter/vf_showinfo.c
+++ b/libavfilter/vf_showinfo.c
@@ -738,7 +738,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame 
*frame)
     FilterLink *inl = ff_filter_link(inlink);
     AVFilterContext *ctx = inlink->dst;
     ShowInfoContext *s = ctx->priv;
-    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
     uint32_t plane_checksum[4] = {0}, checksum = 0;
     int64_t sum[4] = {0}, sum2[4] = {0};
     int32_t pixelcount[4] = {0};
@@ -748,7 +748,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame 
*frame)
 
     for (plane = 0; plane < 4 && s->calculate_checksums && frame->data[plane] 
&& frame->linesize[plane]; plane++) {
         uint8_t *data = frame->data[plane];
-        int h = plane == 1 || plane == 2 ? AV_CEIL_RSHIFT(inlink->h, vsub) : 
inlink->h;
+        int h = plane == 1 || plane == 2 ? AV_CEIL_RSHIFT(frame->height, vsub) 
: frame->height;
         int linesize = av_image_get_linesize(frame->format, frame->width, 
plane);
         int width = linesize >> (bitdepth > 8);
 
@@ -945,4 +945,5 @@ const FFFilter ff_vf_showinfo = {
     FILTER_INPUTS(avfilter_vf_showinfo_inputs),
     FILTER_OUTPUTS(avfilter_vf_showinfo_outputs),
     .priv_size   = sizeof(ShowInfoContext),
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
-- 
2.52.0


>From 2cfe570b3597467dac5fb0f8d59ce46b1f12dc66 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Tue, 29 Sep 2026 04:54:49 +0200
Subject: [PATCH 4/4] avfilter/vf_crop: reconfigure when the input frame
 parameters change

Found during triage of the security report RqgTntCAW2qN.
---
 libavfilter/vf_crop.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/libavfilter/vf_crop.c b/libavfilter/vf_crop.c
index 433fcb3c90..e586d25f22 100644
--- a/libavfilter/vf_crop.c
+++ b/libavfilter/vf_crop.c
@@ -77,6 +77,7 @@ typedef struct CropContext {
     int  y;             ///< y offset of the non-cropped area with respect to 
the input area
     int  w;             ///< width of the cropped area
     int  h;             ///< height of the cropped area
+    int in_format, in_w, in_h; ///< input parameters the crop area was 
computed for
 
     AVRational out_sar; ///< output sample aspect ratio
     int keep_aspect;    ///< keep display aspect ratio when cropping
@@ -223,6 +224,9 @@ static int config_input(AVFilterLink *link)
         s->x &= ~((1 << s->hsub) - 1);
         s->y &= ~((1 << s->vsub) - 1);
     }
+    s->in_format = link->format;
+    s->in_w      = link->w;
+    s->in_h      = link->h;
     return 0;
 
 fail_expr:
@@ -252,8 +256,22 @@ static int filter_frame(AVFilterLink *link, AVFrame *frame)
     FilterLink        *l = ff_filter_link(link);
     AVFilterContext *ctx = link->dst;
     CropContext *s = ctx->priv;
-    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format);
-    int i;
+    const AVPixFmtDescriptor *desc;
+    int i, ret;
+
+    if (frame->format != s->in_format ||
+        frame->width  != s->in_w      ||
+        frame->height != s->in_h) {
+        link->format = frame->format;
+        link->w      = frame->width;
+        link->h      = frame->height;
+        if ((ret = config_input(link)) < 0 ||
+            (ret = config_output(ctx->outputs[0])) < 0) {
+            av_frame_free(&frame);
+            return ret;
+        }
+    }
+    desc = av_pix_fmt_desc_get(link->format);
 
     s->var_values[VAR_N] = l->frame_count_out;
     s->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ?
@@ -395,4 +413,5 @@ const FFFilter ff_vf_crop = {
     FILTER_OUTPUTS(avfilter_vf_crop_outputs),
     FILTER_QUERY_FUNC2(query_formats),
     .process_command = process_command,
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
 };
-- 
2.52.0

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

Reply via email to