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,
};