PR #25479 opened by Kilian (lusoris)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25479
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25479.patch

These patches address GCC 14.4.0 and 16.2.1 -Warray-bounds in libavcodec/vlc.c, 
-Wint-in-bool-context in libavcodec/h264dec.c and -Wstringop-overflow in 
libavcodec/wmadec.c, and GCC 14.4.0 -Wstringop-overflow in 
libavcodec/a64multienc.c.

Result: warnings in these files go from 7 to 0 with GCC 14.4.0 and from 6 to 0 
with GCC 16.2.1.

What the patch does:

- `avcodec/a64multienc: fill palette index tables by interval`: fills constant 
palette intervals with memset and adds always-on palette-size and 
interval-bounds assertions.
- `avcodec/vlc: assert multi-symbol table bounds`: adds always-on assertions 
for width selectors, recursive construction levels and the selected array bound 
at each write.
- `avcodec/wmadec: reject invalid block channel counts`: replaces a debug 
assertion with an unconditional invalid-data return for channel counts outside 
1 to MAX_CHANNELS.
- `avcodec/h264dec: compute the missing-field offsets before FF_PTR_ADD`: 
computes the two offsets in local variables before FF_PTR_ADD(); no functional 
change.

Behaviour change: palette and table bounds gain always-on assertions, and 
invalid block channel counts return errors; valid palette, table and channel 
processing is unchanged.

Base: FFmpeg master 46f23be8b1.

Testing:

- GCC 14.4.0 (`--enable-gpl --enable-vulkan`) and GCC 16.2.1 (`--enable-gpl 
--enable-vulkan --enable-vaapi --enable-libdrm --enable-nvenc`), targets all, 
alltools, examples, testprogs and checkasm: master 46f23be8b1 shows, with GCC 
14.4.0, 3 -Warray-bounds warnings in libavcodec/vlc.c; 2 -Wint-in-bool-context 
warnings in libavcodec/h264dec.c; 2 -Wstringop-overflow warnings in 
libavcodec/a64multienc.c, libavcodec/wmadec.c, and with GCC 16.2.1, 3 
-Warray-bounds warnings in libavcodec/vlc.c; 2 -Wint-in-bool-context warnings 
in libavcodec/h264dec.c; 1 -Wstringop-overflow warning in libavcodec/wmadec.c; 
with these patches there are none and no new ones.
- FATE with GCC 16.2.1, tests whose name contains `h264`, `wma` or `a64` (294 
tests): the same results on master and with these patches; 10 fail on both 
sides only because their samples are missing from my local FATE sample copy, 
all others pass.

Split out of #25111 so that each part reaches the reviewers for its area.



>From c68e9ce126feb2a6e46c3c1ce451eb8747a058a5 Mon Sep 17 00:00:00 2001
From: lusoris <[email protected]>
Date: Thu, 15 Oct 2026 22:06:34 +0200
Subject: [PATCH 1/4] avcodec/a64multienc: fill palette index tables by
 interval

GCC 14.4.0 reports -Wstringop-overflow when building the palette lookup
tables. The index tables were filled one luma value at a time, leaving
their store bounds unclear to the compiler. Fill each constant palette
interval with memset and assert the palette size and interval bounds.
For palettes satisfying these invariants, the index values and dithering
calculation are unchanged.

Signed-off-by: lusoris <[email protected]>
---
 libavcodec/a64multienc.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/libavcodec/a64multienc.c b/libavcodec/a64multienc.c
index d81718dff5f..880b1547ec5 100644
--- a/libavcodec/a64multienc.c
+++ b/libavcodec/a64multienc.c
@@ -106,7 +106,7 @@ static void render_charset(AVCodecContext *avctx, uint8_t 
*charset,
     A64Context *c = avctx->priv_data;
     uint8_t row1, row2;
     int charpos, x, y;
-    int a, b;
+    int b;
     uint8_t pix;
     int lowdiff, highdiff;
     int *best_cb = c->mc_best_cb;
@@ -116,12 +116,15 @@ static void render_charset(AVCodecContext *avctx, uint8_t 
*charset,
     int i;
     int distance;
 
+    av_assert0(c->mc_pal_size >= 4 &&
+               c->mc_pal_size <= FF_ARRAY_ELEMS(c->mc_luma_vals));
+
     /* Generate lookup-tables for dither and index before looping.
      * This code relies on c->mc_luma_vals[c->mc_pal_size - 1] being
      * the maximum of all the mc_luma_vals values and on the minimum
      * being zero; this ensures that dither is properly initialized. */
     i = 0;
-    for (a=0; a < 256; a++) {
+    for (size_t a = 0; a < FF_ARRAY_ELEMS(index1); a++) {
         if(i < c->mc_pal_size -1 && a == c->mc_luma_vals[i + 1]) {
             distance = c->mc_luma_vals[i + 1] - c->mc_luma_vals[i];
             for(b = 0; b <= distance; b++) {
@@ -130,10 +133,23 @@ static void render_charset(AVCodecContext *avctx, uint8_t 
*charset,
             i++;
         }
         if(i >= c->mc_pal_size - 1) dither[a] = 0;
-        index1[a] = i;
-        index2[a] = FFMIN(i + 1, c->mc_pal_size - 1);
     }
 
+    /* index1 and index2 are constant on each palette interval. Filling them
+     * per interval, rather than one byte per luma value in the loop above,
+     * keeps GCC 14 (seen with 14.4.0 on x86-64) from vectorising the fill
+     * into 16-byte stores that it then reports as overflowing the tables
+     * (-Wstringop-overflow). */
+    for (i = 0; i < c->mc_pal_size - 1; i++) {
+        const int start = i ? c->mc_luma_vals[i] : 0;
+        const int end   = c->mc_luma_vals[i + 1];
+        av_assert0(start < end && end <= FF_ARRAY_ELEMS(index1));
+        memset(index1 + start, i,     end - start);
+        memset(index2 + start, i + 1, end - start);
+    }
+    memset(index1 + c->mc_luma_vals[i], i, FF_ARRAY_ELEMS(index1) - 
c->mc_luma_vals[i]);
+    memset(index2 + c->mc_luma_vals[i], i, FF_ARRAY_ELEMS(index2) - 
c->mc_luma_vals[i]);
+
     /* and render charset */
     for (charpos = 0; charpos < CHARSET_CHARS; charpos++) {
         lowdiff  = 0;
-- 
2.54.0


>From 8815728bb83cb5ea3d956f8a90e9c8ae6b328cbc Mon Sep 17 00:00:00 2001
From: lusoris <[email protected]>
Date: Thu, 15 Oct 2026 22:06:35 +0200
Subject: [PATCH 2/4] avcodec/vlc: assert multi-symbol table bounds

GCC 14.4.0 and 16.2.1 report -Warray-bounds in multi-symbol table
construction. The symbol width and current level determine which
fixed-size array is written. Assert the width selector and level bounds,
including the selected array bound at each write. Table construction for
valid selectors and levels is unchanged.

Signed-off-by: lusoris <[email protected]>
---
 libavcodec/vlc.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/libavcodec/vlc.c b/libavcodec/vlc.c
index 93d88714f22..ed610a1df94 100644
--- a/libavcodec/vlc.c
+++ b/libavcodec/vlc.c
@@ -426,7 +426,11 @@ static void add_level(VLC_MULTI_ELEM *table, const int 
is16bit,
                       const int minlen, const int max,
                       unsigned* levelcnt, VLC_MULTI_ELEM info)
 {
-    int max_symbols = VLC_MULTI_MAX_SYMBOLS >> is16bit;
+    int max_symbols;
+
+    av_assert0(is16bit == 0 || is16bit == 1);
+    max_symbols = VLC_MULTI_MAX_SYMBOLS >> is16bit;
+    av_assert0((unsigned)curlevel < max_symbols);
     for (int i = num-1; i >= max; i--) {
         for (int j = 0; j < 2; j++) {
             int newlimit, sym;
@@ -440,8 +444,13 @@ static void add_level(VLC_MULTI_ELEM *table, const int 
is16bit,
             code = curcode + (buf[t].code >> curlen);
             newlimit = curlimit - l;
             l  += curlen;
-            if (is16bit) info.val16[curlevel] = sym;
-            else info.val8[curlevel] = sym&0xFF;
+            if (is16bit) {
+                av_assert0((unsigned)curlevel < FF_ARRAY_ELEMS(info.val16));
+                info.val16[curlevel] = sym;
+            } else {
+                av_assert0((unsigned)curlevel < FF_ARRAY_ELEMS(info.val8));
+                info.val8[curlevel] = sym & 0xFF;
+            }
 
             if (curlevel) { // let's not add single entries
                 uint32_t val = code >> (32 - numbits);
-- 
2.54.0


>From e340019a5dadb8b43593135d7907442de77d868d Mon Sep 17 00:00:00 2001
From: lusoris <[email protected]>
Date: Thu, 15 Oct 2026 22:06:35 +0200
Subject: [PATCH 3/4] avcodec/wmadec: reject invalid block channel counts

GCC 14.4.0 and 16.2.1 report -Wstringop-overflow in block decoding. The
channel count controls access to fixed-size channel storage, but the
existing debug assertion only checked its upper bound. Replace it with
an error check for counts outside 1 to MAX_CHANNELS. Blocks with
supported channel counts follow the same decoding path.

Signed-off-by: lusoris <[email protected]>
---
 libavcodec/wmadec.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libavcodec/wmadec.c b/libavcodec/wmadec.c
index 65bd3845fd0..0969620d70a 100644
--- a/libavcodec/wmadec.c
+++ b/libavcodec/wmadec.c
@@ -457,7 +457,10 @@ static int wma_decode_block(WMACodecContext *s)
     AVTXContext *mdct;
     av_tx_fn mdct_fn;
 
-    av_assert2(channels <= MAX_CHANNELS);
+    if (channels < 1 || channels > MAX_CHANNELS) {
+        av_log(s->avctx, AV_LOG_ERROR, "Invalid channel count %d\n", channels);
+        return AVERROR_INVALIDDATA;
+    }
 
 #ifdef TRACE
     ff_tlog(s->avctx, "***decode_block: %d:%d\n",
-- 
2.54.0


>From 476640a459372b44f07b0bc210e1454af4e0099c Mon Sep 17 00:00:00 2001
From: lusoris <[email protected]>
Date: Thu, 15 Oct 2026 23:12:46 +0200
Subject: [PATCH 4/4] avcodec/h264dec: compute the missing-field offsets before
 FF_PTR_ADD

GCC 16.2.1 reports -Wint-in-bool-context for the two FF_PTR_ADD() calls
that fill a missing field: the macro tests its offset argument, and the
argument here is a multiplication. The test is intended, it skips adding
0 to NULL plane pointers (4a511dc8eb). Compute the offsets into local
variables first. No functional change.

Signed-off-by: lusoris <[email protected]>
---
 libavcodec/h264dec.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c
index 597bdae548a..392b056c740 100644
--- a/libavcodec/h264dec.c
+++ b/libavcodec/h264dec.c
@@ -1033,8 +1033,11 @@ static int finalize_frame(H264Context *h, AVFrame *dst, 
H264Picture *out, int *g
             av_log(h->avctx, AV_LOG_DEBUG, "Duplicating field %d to fill 
missing\n", field);
 
             for (p = 0; p<4; p++) {
-                dst_data[p] = FF_PTR_ADD(f->data[p], (field^1)*f->linesize[p]);
-                src_data[p] = FF_PTR_ADD(f->data[p],  field   *f->linesize[p]);
+                const int dst_off = (field^1)*f->linesize[p];
+                const int src_off =  field   *f->linesize[p];
+
+                dst_data[p] = FF_PTR_ADD(f->data[p], dst_off);
+                src_data[p] = FF_PTR_ADD(f->data[p], src_off);
                 linesizes[p] = 2*f->linesize[p];
             }
 
-- 
2.54.0

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

Reply via email to