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

The write loop in av_dynamic_hdr_plus_to_t35() nested the
color_saturation_mapping_flag and color_saturation_weight put_bits
calls inside the tone_mapping_flag block, while the size calculation
and the parser treat these fields as unconditional per window. When
tone_mapping_flag is 0 and color_saturation_mapping_flag is 1, fewer
bits than allocated were written, leaving uninitialized heap bytes in
the returned buffer. Move the writes outside the tone_mapping_flag
block to match.

Fixes: read of uninitialized memory
Fixes: Yy1uJcbmyeBp
Fixes: AISLE-2026-0100-00011
Found-by: Joshua Rogers <[email protected]>


From 4705e4d151c6c00c9546da0eb55419801fe7b432 Mon Sep 17 00:00:00 2001
From: Joshua Rogers <[email protected]>
Date: Mon, 31 Aug 2026 15:31:18 +0200
Subject: [PATCH 1/2] avutil/hdr_dynamic_metadata: fix uninitialized bytes when
 color saturation is set without tone mapping

The write loop in av_dynamic_hdr_plus_to_t35() nested the
color_saturation_mapping_flag and color_saturation_weight put_bits
calls inside the tone_mapping_flag block, while the size calculation
and the parser treat these fields as unconditional per window. When
tone_mapping_flag is 0 and color_saturation_mapping_flag is 1, fewer
bits than allocated were written, leaving uninitialized heap bytes in
the returned buffer. Move the writes outside the tone_mapping_flag
block to match.

Fixes: read of uninitialized memory
Fixes: Yy1uJcbmyeBp
Fixes: AISLE-2026-0100-00011
Found-by: Joshua Rogers <[email protected]>
---
 libavutil/hdr_dynamic_metadata.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libavutil/hdr_dynamic_metadata.c b/libavutil/hdr_dynamic_metadata.c
index 4c9ac25970..9b46499343 100644
--- a/libavutil/hdr_dynamic_metadata.c
+++ b/libavutil/hdr_dynamic_metadata.c
@@ -379,11 +379,11 @@ int av_dynamic_hdr_plus_to_t35(const AVDynamicHDRPlus *s, 
uint8_t **data, size_t
             for (int i = 0; i < s->params[w].num_bezier_curve_anchors; i++)
                 put_bits(pb, 10, s->params[w].bezier_curve_anchors[i].num * 
bezier_anchor_den /
                     s->params[w].bezier_curve_anchors[i].den);
-            put_bits(pb, 1, s->params[w].color_saturation_mapping_flag);
-            if (s->params[w].color_saturation_mapping_flag)
-                put_bits(pb, 6, s->params[w].color_saturation_weight.num * 
saturation_weight_den /
-                    s->params[w].color_saturation_weight.den);
         }
+        put_bits(pb, 1, s->params[w].color_saturation_mapping_flag);
+        if (s->params[w].color_saturation_mapping_flag)
+            put_bits(pb, 6, s->params[w].color_saturation_weight.num * 
saturation_weight_den /
+                s->params[w].color_saturation_weight.den);
     }
 
     flush_put_bits(pb);
-- 
2.52.0


From ce77b6f53d655fbfc6a48af5e3a90512da1ddf33 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 19 Sep 2026 23:47:13 +0200
Subject: [PATCH 2/2] tests/hdr_dynamic_metadata: Test tone_mapping_flag

Found during triage/review of the security report
---
 libavutil/tests/hdr_dynamic_metadata.c | 25 ++++++++++---------------
 tests/ref/fate/hdr_dynamic_metadata    |  2 +-
 2 files changed, 11 insertions(+), 16 deletions(-)

diff --git a/libavutil/tests/hdr_dynamic_metadata.c 
b/libavutil/tests/hdr_dynamic_metadata.c
index c2126fb45d..e85604e4f0 100644
--- a/libavutil/tests/hdr_dynamic_metadata.c
+++ b/libavutil/tests/hdr_dynamic_metadata.c
@@ -295,11 +295,7 @@ static void fill_hdr_plus_conforming(AVDynamicHDRPlus *s)
 /* Deliberately NOT a conforming Version 1 payload: it combines a second
  * processing window, both actual-peak-luminance matrices and
  * ColorSaturationWeight, all of which ยง9.4 excludes for Version 1. It exists
- * only to drive the optional serializer/parser branches those fields guard.
- *
- * tone_mapping_flag stays set for every window because the serializer writes
- * color_saturation_mapping_flag inside the tone-mapping block while the parser
- * reads it outside; only tone_mapping_flag=1 round-trips today. */
+ * only to drive the optional serializer/parser branches those fields guard. */
 static void fill_hdr_plus_synthetic(AVDynamicHDRPlus *s)
 {
     memset(s, 0, sizeof(*s));
@@ -362,17 +358,16 @@ static void fill_hdr_plus_synthetic(AVDynamicHDRPlus *s)
             s->mastering_display_actual_peak_luminance[i][j] =
                 (AVRational){ 15 - (i * 2 + j), PEAK_LUMINANCE_DEN };
 
+    s->params[0].tone_mapping_flag = 1;
+    s->params[0].knee_point_x = (AVRational){ 500, KNEE_POINT_DEN };
+    s->params[0].knee_point_y = (AVRational){ 800, KNEE_POINT_DEN };
+    s->params[0].num_bezier_curve_anchors = 3;
+    s->params[0].bezier_curve_anchors[0] = (AVRational){ 100, 
BEZIER_ANCHOR_DEN };
+    s->params[0].bezier_curve_anchors[1] = (AVRational){ 500, 
BEZIER_ANCHOR_DEN };
+    s->params[0].bezier_curve_anchors[2] = (AVRational){ 900, 
BEZIER_ANCHOR_DEN };
+    s->params[1].tone_mapping_flag = 0;
+
     for (int w = 0; w < 2; w++) {
-        s->params[w].tone_mapping_flag = 1;
-        s->params[w].knee_point_x = (AVRational){ 500 + w, KNEE_POINT_DEN };
-        s->params[w].knee_point_y = (AVRational){ 800 + w, KNEE_POINT_DEN };
-        s->params[w].num_bezier_curve_anchors = 3;
-        s->params[w].bezier_curve_anchors[0] =
-            (AVRational){ 100 + w, BEZIER_ANCHOR_DEN };
-        s->params[w].bezier_curve_anchors[1] =
-            (AVRational){ 500 + w, BEZIER_ANCHOR_DEN };
-        s->params[w].bezier_curve_anchors[2] =
-            (AVRational){ 900 + w, BEZIER_ANCHOR_DEN };
         s->params[w].color_saturation_mapping_flag = 1;
         s->params[w].color_saturation_weight =
             (AVRational){ 8 + w, SATURATION_DEN };
diff --git a/tests/ref/fate/hdr_dynamic_metadata 
b/tests/ref/fate/hdr_dynamic_metadata
index fbd10e79e9..118f69d7e5 100644
--- a/tests/ref/fate/hdr_dynamic_metadata
+++ b/tests/ref/fate/hdr_dynamic_metadata
@@ -54,7 +54,7 @@ decoded:
     maxscl: 20000/100000 30000/100000 40000/100000 average_maxrgb=25000/100000
     distribution_maxrgb: n=1 25%=12500/100000
     fraction_bright_pixels=100/1000
-    tone_mapping_flag=1 knee=501/4095,801/4095 anchors=3: 101/1023 501/1023 
901/1023
+    tone_mapping_flag=0
     color_saturation_mapping_flag=1 weight=9/8
 
 Testing error paths
-- 
2.52.0

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

Reply via email to