PR #25656 opened by ffmpeg-devel
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25656
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25656.patch

**Backport:** https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25523

Hi,

the size checks in the cbs slice/OBU writers multiply the data size by 8 before 
comparing it against the space left in the write buffer. On a 32 bit build that 
multiplication wraps at 512 MiB, so the check can pass for data that does not 
fit and the copy then runs past the end of the buffer.

cbs_mpeg2 is reachable: a generated 512 MiB slice unit passed through 
mpeg2_metadata makes the memcpy run off the 1 MiB write buffer (I hit this on 
an i486 build under qemu). cbs_h2645 and cbs_av1 have the same broken check, 
but their units are capped earlier (h2645_parse rejects anything over 
INT_MAX/8, and cbs_av1_split_fragment() does the same), so today they are 
defensive only. I fixed all three anyway, because a size check should not be 
correct only thanks to a cap in another module.

This is the same rewrite that already landed for cbs_jpeg and cbs_vp9 earlier 
this month (compare the byte count against put_bits_left() / 8 instead). The 
slice writers keep the byte reserved for the rbsp_stop_one_bit by comparing 
data_size + 1; the two forms are the same for every non-wrapping size.

What I ran: i486 static build under qemu - old code dies with SIGSEGV on the 
generated slice, patched code refuses cleanly with "Out of memory", small 
inputs round-trip fine on both. 64 bit: behaves the same before and after (the 
multiplication cannot wrap there).

Thanks for taking a look.


>From f8e017db6bdac7e1d40ce24a286cf91149513877 Mon Sep 17 00:00:00 2001
From: Altaf Khan <[email protected]>
Date: Tue, 27 Oct 2026 17:54:18 +0530
Subject: [PATCH] avcodec/cbs: check slice and OBU data sizes without
 overflowing size_t

The checks that compare a data size with the space left in the write
buffer multiply the size by 8 first.  That multiplication is done in
size_t, so with a 32 bit size_t a data size of 2^29 - 1 bytes or more
wraps and the check can pass for data that does not fit; the slice data
is then copied past the end of the write buffer, which
cbs_write_unit_data() caps at INT_MAX / 8 bytes.

cbs_mpeg2 is reachable: the demuxer passes an oversized slice unit
straight through to the metadata bitstream filter.  On a 32 bit build a
512 MiB slice makes the check compare 16 against a non-zero
put_bits_left() and the memcpy() runs off the 1 MiB buffer.  cbs_h2645
and cbs_av1 cannot be reached today because h2645_parse and
cbs_av1_split_fragment() reject units larger than INT_MAX / 8, but
their checks are wrong in the same way and should not rely on that.

Compare the byte count against put_bits_left() / 8 instead, as was
already done for cbs_jpeg and cbs_vp9.  The slice writers keep the byte
reserved for the rbsp_stop_one_bit by comparing data_size + 1; the two
forms are equivalent for every non-wrapping size.

Signed-off-by: Altaf Khan <[email protected]>
(cherry picked from commit a88838af482100fa22dd22c94be592c35642de46)
---
 libavcodec/cbs_av1.c   | 2 +-
 libavcodec/cbs_h2645.c | 2 +-
 libavcodec/cbs_mpeg2.c | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libavcodec/cbs_av1.c b/libavcodec/cbs_av1.c
index 4bacebf0865..e4b275199ba 100644
--- a/libavcodec/cbs_av1.c
+++ b/libavcodec/cbs_av1.c
@@ -1199,7 +1199,7 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx,
     flush_put_bits(pbc);
     av_assert0(data_pos <= start_pos);
 
-    if (8 * obu->obu_size > put_bits_left(pbc)) {
+    if (obu->obu_size > put_bits_left(pbc) / 8) {
         av_refstruct_unref(&priv->sequence_header_ref);
         av_buffer_unref(&priv->frame_header_ref);
         *priv = av1ctx;
diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c
index 61b70f41184..9329554cab9 100644
--- a/libavcodec/cbs_h2645.c
+++ b/libavcodec/cbs_h2645.c
@@ -272,7 +272,7 @@ int ff_cbs_h2645_write_slice_data(CodedBitstreamContext 
*ctx,
     av_assert0(data_bit_start >= 0 &&
                data_size > data_bit_start / 8);
 
-    if (data_size * 8 + 8 > put_bits_left(pbc))
+    if (data_size + 1 > put_bits_left(pbc) / 8)
         return AVERROR(ENOSPC);
 
     if (!rest)
diff --git a/libavcodec/cbs_mpeg2.c b/libavcodec/cbs_mpeg2.c
index 87401d15436..57c22053938 100644
--- a/libavcodec/cbs_mpeg2.c
+++ b/libavcodec/cbs_mpeg2.c
@@ -319,7 +319,7 @@ static int cbs_mpeg2_write_slice(CodedBitstreamContext *ctx,
         av_assert0(slice->data_bit_start >= 0 &&
                    slice->data_size > slice->data_bit_start / 8);
 
-        if (slice->data_size * 8 + 8 > put_bits_left(pbc))
+        if (slice->data_size + 1 > put_bits_left(pbc) / 8)
             return AVERROR(ENOSPC);
 
         // First copy the remaining bits of the first byte
-- 
2.54.0

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

Reply via email to