This is an automated email from the git hooks/post-receive script.
Git pushed a commit to branch master
in repository ffmpeg.
The following commit(s) were added to refs/heads/master by this push:
new 182bb23bf8 avutil/hdr_dynamic_metadata: reject oversized HDR10+ counts
182bb23bf8 is described below
commit 182bb23bf84869e8487a5b5c6253fc9e9c26adfc
Author: luoye-cpu <[email protected]>
AuthorDate: Fri Oct 30 06:42:22 2026 +0800
Commit: Marcos Ashton <[email protected]>
CommitDate: Wed Nov 4 19:36:53 2026 +0000
avutil/hdr_dynamic_metadata: reject oversized HDR10+ counts
av_dynamic_hdr_plus_to_t35() uses caller-supplied counts as loop
bounds over fixed-size arrays. A count slightly above an array
length can make the write loop read out of bounds while the
payload remains well within AV_HDR_PLUS_MAX_PAYLOAD_SIZE.
Reject counts above the array bounds with AVERROR(EINVAL), since
these are caller-supplied fields rather than data parsed off the
wire. The checks are gated like the reads they guard: the
targeted-system-display and mastering-display peak-luminance
counts and the bezier anchors are only read when their respective
flags are set. Rejecting oversized counts also keeps the payload
within AV_HDR_PLUS_MAX_PAYLOAD_SIZE, but the size assertion is not
a substitute for validating the array bounds.
FATE cases cover every rejected count and, for gated counts,
out-of-range values with their flags clear, which must still
serialize.
Signed-off-by: luoye-cpu <[email protected]>
---
libavutil/hdr_dynamic_metadata.c | 35 +++++++++++++
libavutil/tests/hdr_dynamic_metadata.c | 92 ++++++++++++++++++++++++++++++++++
tests/ref/fate/hdr_dynamic_metadata | 10 ++++
3 files changed, 137 insertions(+)
diff --git a/libavutil/hdr_dynamic_metadata.c b/libavutil/hdr_dynamic_metadata.c
index 9b46499343..d52470da6d 100644
--- a/libavutil/hdr_dynamic_metadata.c
+++ b/libavutil/hdr_dynamic_metadata.c
@@ -244,6 +244,41 @@ int av_dynamic_hdr_plus_to_t35(const AVDynamicHDRPlus *s,
uint8_t **data, size_t
return AVERROR(EINVAL);
if ((!data || *data) && !size)
return AVERROR(EINVAL);
+ /* The caps put the payload at exactly 907 bytes; the assert cannot fire.
*/
+ if (s->num_windows > FF_ARRAY_ELEMS(s->params))
+ return AVERROR(EINVAL);
+
+ for (int w = 0; w < s->num_windows; w++) {
+ const AVHDRPlusColorTransformParams *p = &s->params[w];
+
+ if (p->num_distribution_maxrgb_percentiles >
+ FF_ARRAY_ELEMS(p->distribution_maxrgb))
+ return AVERROR(EINVAL);
+
+ /* The anchors are only read while tone mapping is enabled. */
+ if (p->tone_mapping_flag &&
+ p->num_bezier_curve_anchors >
+ FF_ARRAY_ELEMS(p->bezier_curve_anchors))
+ return AVERROR(EINVAL);
+ }
+
+ /* The peak-luminance pairs use a row count and a column count, and are
+ * only read when their flag is set, so check them the same way. */
+ if (s->targeted_system_display_actual_peak_luminance_flag) {
+ if (s->num_rows_targeted_system_display_actual_peak_luminance >
+ FF_ARRAY_ELEMS(s->targeted_system_display_actual_peak_luminance) ||
+ s->num_cols_targeted_system_display_actual_peak_luminance >
+
FF_ARRAY_ELEMS(s->targeted_system_display_actual_peak_luminance[0]))
+ return AVERROR(EINVAL);
+ }
+
+ if (s->mastering_display_actual_peak_luminance_flag) {
+ if (s->num_rows_mastering_display_actual_peak_luminance >
+ FF_ARRAY_ELEMS(s->mastering_display_actual_peak_luminance) ||
+ s->num_cols_mastering_display_actual_peak_luminance >
+ FF_ARRAY_ELEMS(s->mastering_display_actual_peak_luminance[0]))
+ return AVERROR(EINVAL);
+ }
/**
* Buffer size per CTA-861-H p.253-254:
diff --git a/libavutil/tests/hdr_dynamic_metadata.c
b/libavutil/tests/hdr_dynamic_metadata.c
index e85604e4f0..dc0501333d 100644
--- a/libavutil/tests/hdr_dynamic_metadata.c
+++ b/libavutil/tests/hdr_dynamic_metadata.c
@@ -486,6 +486,98 @@ static void test_hdr_plus(void)
printf("to_t35 no data no size: ret==AVERROR(EINVAL)=%s\n",
ret == AVERROR(EINVAL) ? "yes" : "no");
+ /* A num_windows above the params[] length must be refused. */
+ hdr->num_windows = FF_ARRAY_ELEMS(hdr->params) + 1;
+ size = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, &size);
+ printf("to_t35 num_windows>3: ret==AVERROR(EINVAL)=%s\n",
+ ret == AVERROR(EINVAL) ? "yes" : "no");
+ hdr->num_windows = 2;
+
+ uint8_t pct = hdr->params[0].num_distribution_maxrgb_percentiles;
+ uint8_t anch = hdr->params[0].num_bezier_curve_anchors;
+ uint8_t trow = hdr->num_rows_targeted_system_display_actual_peak_luminance;
+ uint8_t tcol = hdr->num_cols_targeted_system_display_actual_peak_luminance;
+ uint8_t mrow = hdr->num_rows_mastering_display_actual_peak_luminance;
+ uint8_t mcol = hdr->num_cols_mastering_display_actual_peak_luminance;
+
+ hdr->params[0].num_distribution_maxrgb_percentiles =
+ FF_ARRAY_ELEMS(hdr->params[0].distribution_maxrgb) + 1;
+ size = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, &size);
+ printf("to_t35 percentiles>15: ret==AVERROR(EINVAL)=%s\n",
+ ret == AVERROR(EINVAL) ? "yes" : "no");
+ hdr->params[0].num_distribution_maxrgb_percentiles = pct;
+
+ hdr->params[0].num_bezier_curve_anchors =
+ FF_ARRAY_ELEMS(hdr->params[0].bezier_curve_anchors) + 1;
+ size = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, &size);
+ printf("to_t35 anchors>15: ret==AVERROR(EINVAL)=%s\n",
+ ret == AVERROR(EINVAL) ? "yes" : "no");
+ hdr->params[0].num_bezier_curve_anchors = anch;
+
+ /* The peak-luminance pairs are only checked when their flags are set. */
+ hdr->num_rows_targeted_system_display_actual_peak_luminance =
+ FF_ARRAY_ELEMS(hdr->targeted_system_display_actual_peak_luminance) + 1;
+ size = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, &size);
+ printf("to_t35 targeted rows>25: ret==AVERROR(EINVAL)=%s\n",
+ ret == AVERROR(EINVAL) ? "yes" : "no");
+ hdr->num_rows_targeted_system_display_actual_peak_luminance = trow;
+
+ hdr->num_cols_targeted_system_display_actual_peak_luminance =
+ FF_ARRAY_ELEMS(hdr->targeted_system_display_actual_peak_luminance[0])
+ 1;
+ size = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, &size);
+ printf("to_t35 targeted cols>25: ret==AVERROR(EINVAL)=%s\n",
+ ret == AVERROR(EINVAL) ? "yes" : "no");
+ hdr->num_cols_targeted_system_display_actual_peak_luminance = tcol;
+
+ hdr->num_rows_mastering_display_actual_peak_luminance =
+ FF_ARRAY_ELEMS(hdr->mastering_display_actual_peak_luminance) + 1;
+ size = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, &size);
+ printf("to_t35 mastering rows>25: ret==AVERROR(EINVAL)=%s\n",
+ ret == AVERROR(EINVAL) ? "yes" : "no");
+ hdr->num_rows_mastering_display_actual_peak_luminance = mrow;
+
+ hdr->num_cols_mastering_display_actual_peak_luminance =
+ FF_ARRAY_ELEMS(hdr->mastering_display_actual_peak_luminance[0]) + 1;
+ size = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, &size);
+ printf("to_t35 mastering cols>25: ret==AVERROR(EINVAL)=%s\n",
+ ret == AVERROR(EINVAL) ? "yes" : "no");
+ hdr->num_cols_mastering_display_actual_peak_luminance = mcol;
+
+ /* With the flags clear, oversized counts must still serialize. */
+ hdr->targeted_system_display_actual_peak_luminance_flag = 0;
+ hdr->mastering_display_actual_peak_luminance_flag = 0;
+ hdr->num_rows_targeted_system_display_actual_peak_luminance =
+ FF_ARRAY_ELEMS(hdr->targeted_system_display_actual_peak_luminance) + 1;
+ size = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, &size);
+ printf("to_t35 rows>25 with flag clear: ret==0=%s\n",
+ ret == 0 ? "yes" : "no");
+ hdr->num_rows_targeted_system_display_actual_peak_luminance = trow;
+
+ hdr->num_rows_mastering_display_actual_peak_luminance =
+ FF_ARRAY_ELEMS(hdr->mastering_display_actual_peak_luminance) + 1;
+ size = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, &size);
+ printf("to_t35 mastering rows>25 with flag clear: ret==0=%s\n",
+ ret == 0 ? "yes" : "no");
+ hdr->num_rows_mastering_display_actual_peak_luminance = mrow;
+
+ hdr->params[0].tone_mapping_flag = 0;
+ hdr->params[0].num_bezier_curve_anchors =
+ FF_ARRAY_ELEMS(hdr->params[0].bezier_curve_anchors) + 1;
+ size = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, &size);
+ printf("to_t35 anchors>15 with flag clear: ret==0=%s\n",
+ ret == 0 ? "yes" : "no");
+ hdr->params[0].num_bezier_curve_anchors = anch;
+
ret = av_dynamic_hdr_plus_from_t35(NULL, (const uint8_t *)"", 0);
printf("from_t35 NULL s: ret==AVERROR(EINVAL)=%s\n",
ret == AVERROR(EINVAL) ? "yes" : "no");
diff --git a/tests/ref/fate/hdr_dynamic_metadata
b/tests/ref/fate/hdr_dynamic_metadata
index 118f69d7e5..3ec298fc17 100644
--- a/tests/ref/fate/hdr_dynamic_metadata
+++ b/tests/ref/fate/hdr_dynamic_metadata
@@ -60,6 +60,16 @@ decoded:
Testing error paths
to_t35 NULL s: ret==AVERROR(EINVAL)=yes
to_t35 no data no size: ret==AVERROR(EINVAL)=yes
+to_t35 num_windows>3: ret==AVERROR(EINVAL)=yes
+to_t35 percentiles>15: ret==AVERROR(EINVAL)=yes
+to_t35 anchors>15: ret==AVERROR(EINVAL)=yes
+to_t35 targeted rows>25: ret==AVERROR(EINVAL)=yes
+to_t35 targeted cols>25: ret==AVERROR(EINVAL)=yes
+to_t35 mastering rows>25: ret==AVERROR(EINVAL)=yes
+to_t35 mastering cols>25: ret==AVERROR(EINVAL)=yes
+to_t35 rows>25 with flag clear: ret==0=yes
+to_t35 mastering rows>25 with flag clear: ret==0=yes
+to_t35 anchors>15 with flag clear: ret==0=yes
from_t35 NULL s: ret==AVERROR(EINVAL)=yes
from_t35 oversized: ret==AVERROR(EINVAL)=yes
--
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]