Commit f2bf7a2440 for ffmpeg

commit f2bf7a2440d1e49023e4b3ed0e224a8806d388b8
Author: Michael Niedermayer <michael@niedermayer.cc>
Date:   Tue Oct 6 00:14:50 2026 +0200

    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_frame() now rejects a frame that differs from the format and
    size ff_filter_config_links() recorded for its link, 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.

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 1132ab7026..5be67bbf3f 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -1081,19 +1081,15 @@ 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 (av_pix_fmt_desc_get(link->format)->flags & AV_PIX_FMT_FLAG_ALPHA)
-                av_assert1(frame->alpha_mode == link->alpha_mode);
+        int dynamic_flags = fffilter(link->dst->filter)->flags_internal;
+
+        if ((ret = ff_inlink_check_frame_params(link, frame, dynamic_flags)) < 0) {
+            av_frame_free(&frame);
+            return ret;
         }
+        if (!(dynamic_flags & FF_FILTER_FLAG_DYNAMIC_FRAME_FORMAT) &&
+            av_pix_fmt_desc_get(link->format)->flags & AV_PIX_FMT_FLAG_ALPHA)
+            av_assert1(frame->alpha_mode == link->alpha_mode);
     } else {
         if (frame->format != link->format) {
             av_log(link->dst, AV_LOG_ERROR, "Format change is not supported\n");
diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c
index b198ef363f..97c8ea5654 100644
--- a/libavfilter/buffersink.c
+++ b/libavfilter/buffersink.c
@@ -402,6 +402,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/vf_format.c b/libavfilter/vf_format.c
index 793a482de4..7152922158 100644
--- a/libavfilter/vf_format.c
+++ b/libavfilter/vf_format.c
@@ -217,6 +217,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 */

@@ -237,5 +238,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 d42518d6c7..ac9a56152f 100644
--- a/libavfilter/vf_libplacebo.c
+++ b/libavfilter/vf_libplacebo.c
@@ -1818,5 +1818,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 f925d08690..69e69bb8ea 100644
--- a/libavfilter/vf_scale.c
+++ b/libavfilter/vf_scale.c
@@ -1214,6 +1214,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)
@@ -1281,4 +1282,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,
 };