mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2026-11-06 05:17:42 +00:00
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 <3412829159@qq.com>
This commit is contained in:
1 parent
2ec7bc915e
commit
182bb23bf8
3 files changed
+137
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user