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

# Summary of changes

This adds optional `-fbounds-safety` support (Apple clang's bounds-checking 
extension), enabled one file at a time, and converts a first set of libavutil 
files. This is currently meant as a hardening and fuzzing configuration, rather 
than a default.

Upstream LLVM's support currently doesn't match Apple's feature-set, so 
experimental flags on that side cannot be used quite yet.

Currently objects have to be opted into it via `BOUNDS_SAFETY_OBJS` in the 
library's; this could be derived automatically later.

Without the flag enabled, the generated code is identical for every converted 
file except for hmac which now passes the digest length to its internal final() 
callbacks; crc differs only in `__LINE__` values.

A definition can't add an annotation its prototype lacks, so the installed 
headers are annotated too; this means that callers built with `-fbounds-safety` 
will need correctly sized buffers for annotated functions, but this is intended.

A softer alternative is to enable the annotations only inside FFmpeg's own 
build (i.e. via `HAVE_AV_CONFIG_H`), with an opt-in macro for applications, but 
I think if you're opting into bounds-checking then you should be using it on 
the application side too.

For the next step (codec buffers), the plan is for bounds to include 
`AV_INPUT_BUFFER_PADDING_SIZE`, since reads into the padding are intentional 
and expected behaviour.

Tested on macOS AArch64 and x86 (via Rosetta2).


>From 22409f62f1f7c62444547aaa1bfc42b198ed6a7a Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 01/14] avutil/attributes: add bounds annotation macros

For -fbounds-safety; they expand to nothing otherwise.

Signed-off-by: J. Dekker <[email protected]>
---
 doc/APIchanges                  |  3 +++
 libavutil/attributes.h          | 20 ++++++++++++++++++++
 libavutil/attributes_internal.h | 12 ++++++++++++
 libavutil/version.h             |  2 +-
 4 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index 8ce9c22d68e..cf71ed36bfb 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2026-06-23.
 
 API changes, most recent first:
 
+2026-10-xx - xxxxxxxxxx - lavu 61.21.100 - attributes.h
+  Add av_counted_by(), av_sized_by() and av_counted_by_or_null().
+
 2026-10-xx - d19f86ff4c4 - lavf 63.14.102 - avformat.h
   After avformat_seek_file(), the first packet of an audio stream carries
   AV_PKT_DATA_SKIP_SAMPLES for the larger of the rest of the priming and the
diff --git a/libavutil/attributes.h b/libavutil/attributes.h
index cd4c4338c7f..195c862a009 100644
--- a/libavutil/attributes.h
+++ b/libavutil/attributes.h
@@ -46,6 +46,26 @@
 #    define AV_HAS_ATTRIBUTE(x) 0
 #endif
 
+#ifdef __has_feature
+#    if __has_feature(bounds_safety)
+#        define AV_HAS_BOUNDS_SAFETY 1
+#    endif
+#endif
+#ifndef AV_HAS_BOUNDS_SAFETY
+#    define AV_HAS_BOUNDS_SAFETY 0
+#endif
+
+/* Bounds annotations for -fbounds-safety, empty otherwise. */
+#if AV_HAS_BOUNDS_SAFETY
+#    define av_counted_by(n) __attribute__((__counted_by__(n)))
+#    define av_sized_by(n)   __attribute__((__sized_by__(n)))
+#    define av_counted_by_or_null(n) __attribute__((__counted_by_or_null__(n)))
+#else
+#    define av_counted_by(n)
+#    define av_sized_by(n)
+#    define av_counted_by_or_null(n)
+#endif
+
 #if defined(__cplusplus) && \
     defined(__has_cpp_attribute) && \
     __cplusplus >= 201103L
diff --git a/libavutil/attributes_internal.h b/libavutil/attributes_internal.h
index b946d0c5d45..74422aa1b19 100644
--- a/libavutil/attributes_internal.h
+++ b/libavutil/attributes_internal.h
@@ -42,4 +42,16 @@
 #define attribute_nonstring
 #endif
 
+#if AV_HAS_BOUNDS_SAFETY
+#include <ptrcheck.h>
+#define FF_FORGE_BIDI(type, ptr, size) __unsafe_forge_bidi_indexable(type, 
ptr, size)
+/* Bounds cover the string including its terminator. */
+#define FF_CSTR_TO_INDEXABLE(str)      
__unsafe_null_terminated_to_indexable(str)
+#define FF_CSTR_FROM_ARRAY(arr)        
__unsafe_null_terminated_from_indexable(arr)
+#else
+#define FF_FORGE_BIDI(type, ptr, size) ((type)(ptr))
+#define FF_CSTR_TO_INDEXABLE(str)      (str)
+#define FF_CSTR_FROM_ARRAY(arr)        (arr)
+#endif
+
 #endif /* AVUTIL_ATTRIBUTES_INTERNAL_H */
diff --git a/libavutil/version.h b/libavutil/version.h
index cbdd0ad029e..3a39a0b47cb 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  61
-#define LIBAVUTIL_VERSION_MINOR  20
+#define LIBAVUTIL_VERSION_MINOR  21
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
-- 
2.54.0


>From bbabf2804745cf912eb0a76440e6626d55295d27 Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 02/14] configure: add --enable-bounds-safety

Builds the objects listed in BOUNDS_SAFETY_OBJS with -fbounds-safety.
-Wbounds-safety-single-to-count is an error, as such calls trap at
runtime for any count above one.

Signed-off-by: J. Dekker <[email protected]>
---
 Makefile           |  1 +
 configure          | 11 +++++++++++
 ffbuild/common.mak |  2 ++
 3 files changed, 14 insertions(+)

diff --git a/Makefile b/Makefile
index 745131c75f8..4a400c368bb 100644
--- a/Makefile
+++ b/Makefile
@@ -126,6 +126,7 @@ SUBDIR_VARS := CLEANFILES FFLIBS DEVPROGS HOSTPROGS 
TESTPROGS TOOLS      \
                MIPSFPU-OBJS MIPSDSPR2-OBJS MIPSDSP-OBJS MSA-OBJS         \
                MMI-OBJS LSX-OBJS LASX-OBJS RV-OBJS RVV-OBJS RVVB-OBJS    \
                OBJS SHLIBOBJS STLIBOBJS HOSTOBJS TESTOBJS SIMD128-OBJS   \
+               BOUNDS_SAFETY_OBJS                                        \
                SVE-OBJS SVE2-OBJS SME-OBJS SME2-OBJS
 
 define RESET
diff --git a/configure b/configure
index 083bd652c49..0f87a090854 100755
--- a/configure
+++ b/configure
@@ -515,6 +515,7 @@ Developer options (useful when working on FFmpeg itself):
                            leaks and errors, using the specified valgrind 
binary.
                            Cannot be combined with --target-exec
   --enable-ftrapv          Trap arithmetic overflows
+  --enable-bounds-safety   build files that opted in with -fbounds-safety
   --samples=PATH           location of test samples for FATE, if not set use
                            \$FATE_SAMPLES at make invocation time.
   --enable-neon-clobber-test check NEON registers for clobbering (should be
@@ -2188,6 +2189,7 @@ DOCUMENT_LIST="
 "
 
 FEATURE_LIST="
+    bounds_safety
     checkasm
     ftrapv
     gray
@@ -7979,6 +7981,14 @@ fi
 
 enabled ftrapv && check_allcflags -ftrapv
 
+if enabled bounds_safety; then
+    test_cflags -fbounds-safety ||
+        die "ERROR: --enable-bounds-safety requires a compiler supporting 
-fbounds-safety"
+    CFLAGS_BOUNDS_SAFETY=-fbounds-safety
+    test_cflags -fbounds-safety -Werror=bounds-safety-single-to-count &&
+        append CFLAGS_BOUNDS_SAFETY -Werror=bounds-safety-single-to-count
+fi
+
 if enabled icc; then
     # Just warnings, no remarks
     check_allcflags -w1
@@ -8583,6 +8593,7 @@ TARGET_PATH=$target_path
 TARGET_SAMPLES=${target_samples:-\$(SAMPLES)}
 CFLAGS-ffplay=${sdl2_cflags}
 CFLAGS_HEADERS=$CFLAGS_HEADERS
+CFLAGS_BOUNDS_SAFETY=$CFLAGS_BOUNDS_SAFETY
 LIB_INSTALL_EXTRA_CMD=$LIB_INSTALL_EXTRA_CMD
 EXTRALIBS=$extralibs
 COMPAT_OBJS=$compat_objs
diff --git a/ffbuild/common.mak b/ffbuild/common.mak
index d03764df7da..2ccdc135144 100644
--- a/ffbuild/common.mak
+++ b/ffbuild/common.mak
@@ -217,6 +217,8 @@ FFEXTRALIBS := $(LDLIBS:%=$(LD_LIB)) $(foreach 
lib,EXTRALIBS-$(NAME) $(FFLIBS:%=
 OBJS      := $(sort $(OBJS:%=$(SUBDIR)%))
 SHLIBOBJS := $(sort $(SHLIBOBJS:%=$(SUBDIR)%))
 STLIBOBJS := $(sort $(STLIBOBJS:%=$(SUBDIR)%))
+BOUNDS_SAFETY_OBJS := $(BOUNDS_SAFETY_OBJS:%=$(SUBDIR)%)
+$(BOUNDS_SAFETY_OBJS): CFLAGS += $(CFLAGS_BOUNDS_SAFETY)
 TESTOBJS  := $(TESTOBJS:%=$(SUBDIR)tests/%) $(TESTPROGS:%=$(SUBDIR)tests/%.o) 
$(DEVPROGS:%=$(SUBDIR)%.o)
 DEVPROGS  := $(DEVPROGS:%=$(SUBDIR)%$(EXESUF))
 TESTPROGS := $(TESTPROGS:%=$(SUBDIR)tests/%$(EXESUF))
-- 
2.54.0


>From e4e0f44bde091e4202b47c6873f23bfd5c2fc5ef Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 03/14] avutil: build files that need no changes with
 -fbounds-safety

Signed-off-by: J. Dekker <[email protected]>
---
 libavutil/Makefile | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/libavutil/Makefile b/libavutil/Makefile
index 737d98e56fe..0233d897dbc 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -206,6 +206,16 @@ OBJS = adler32.o                                           
             \
        video_enc_params.o                                               \
        video_hint.o                                                     \
 
+BOUNDS_SAFETY_OBJS = cpu.o                                              \
+                     csp.o                                              \
+                     display.o                                          \
+                     integer.o                                          \
+                     intmath.o                                          \
+                     log2_tab.o                                         \
+                     reverse.o                                          \
+                     time.o                                             \
+                     timecode_internal.o                                \
+                     xga_font_data.o                                    \
 
 OBJS-$(CONFIG_CUDA)                     += hwcontext_cuda.o
 OBJS-$(CONFIG_D3D11VA)                  += hwcontext_d3d11va.o
-- 
2.54.0


>From c08ba7704a440f6549925cff2fe1064863978e3a Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 04/14] avutil/adler32: add bounds annotations

Signed-off-by: J. Dekker <[email protected]>
---
 libavutil/Makefile  |  3 ++-
 libavutil/adler32.c | 22 ++++++++++++----------
 libavutil/adler32.h |  2 +-
 3 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/libavutil/Makefile b/libavutil/Makefile
index 0233d897dbc..ccd4fdf943f 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -206,7 +206,8 @@ OBJS = adler32.o                                            
            \
        video_enc_params.o                                               \
        video_hint.o                                                     \
 
-BOUNDS_SAFETY_OBJS = cpu.o                                              \
+BOUNDS_SAFETY_OBJS = adler32.o                                          \
+                     cpu.o                                              \
                      csp.o                                              \
                      display.o                                          \
                      integer.o                                          \
diff --git a/libavutil/adler32.c b/libavutil/adler32.c
index 7124f188029..3d80f79648c 100644
--- a/libavutil/adler32.c
+++ b/libavutil/adler32.c
@@ -41,29 +41,31 @@
 #define DO4(buf)  DO1(buf); DO1(buf); DO1(buf); DO1(buf);
 #define DO16(buf) DO4(buf); DO4(buf); DO4(buf); DO4(buf);
 
-AVAdler av_adler32_update(AVAdler adler, const uint8_t *buf, size_t len)
+AVAdler av_adler32_update(AVAdler adler, const uint8_t *av_counted_by(len) 
buf, size_t len)
 {
+    const uint8_t *ptr = buf;
+    size_t left = len;
     unsigned long s1 = adler & 0xffff;
     unsigned long s2 = adler >> 16;
 
-    while (len > 0) {
+    while (left > 0) {
 #if HAVE_FAST_64BIT && HAVE_FAST_UNALIGNED && !CONFIG_SMALL
-        unsigned len2 = FFMIN((len-1) & ~7, 23*8);
+        unsigned len2 = FFMIN((left-1) & ~7, 23*8);
         if (len2) {
             uint64_t a1= 0;
             uint64_t a2= 0;
             uint64_t b1= 0;
             uint64_t b2= 0;
-            len -= len2;
+            left -= len2;
             s2 += s1*len2;
             while (len2 >= 8) {
-                uint64_t v = AV_RN64(buf);
+                uint64_t v = AV_RN64(ptr);
                 a2 += a1;
                 b2 += b1;
                 a1 +=  v    &0x00FF00FF00FF00FF;
                 b1 += (v>>8)&0x00FF00FF00FF00FF;
                 len2 -= 8;
-                buf+=8;
+                ptr+=8;
             }
 
             //We combine the 8 interleaved adler32 checksums without overflows
@@ -83,12 +85,12 @@ AVAdler av_adler32_update(AVAdler adler, const uint8_t 
*buf, size_t len)
 #endif
         }
 #else
-        while (len > 4  && s2 < (1U << 31)) {
-            DO4(buf);
-            len -= 4;
+        while (left > 4  && s2 < (1U << 31)) {
+            DO4(ptr);
+            left -= 4;
         }
 #endif
-        DO1(buf); len--;
+        DO1(ptr); left--;
         s1 %= BASE;
         s2 %= BASE;
     }
diff --git a/libavutil/adler32.h b/libavutil/adler32.h
index 232d07f5fe8..dee49abac7c 100644
--- a/libavutil/adler32.h
+++ b/libavutil/adler32.h
@@ -53,7 +53,7 @@ typedef uint32_t AVAdler;
  * @param len   size of input buffer
  * @return      updated checksum
  */
-AVAdler av_adler32_update(AVAdler adler, const uint8_t *buf,
+AVAdler av_adler32_update(AVAdler adler, const uint8_t *av_counted_by(len) buf,
                           size_t len) av_pure;
 
 /**
-- 
2.54.0


>From 5295f2af9946050e3c0339297c613a9e89848dd6 Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 05/14] avutil/base64: add bounds annotations

Signed-off-by: J. Dekker <[email protected]>
---
 libavutil/Makefile          |  1 +
 libavutil/base64.c          |  8 +++++---
 libavutil/base64.h          |  7 +++++--
 libavutil/base64_internal.h | 13 +++++++++----
 4 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/libavutil/Makefile b/libavutil/Makefile
index ccd4fdf943f..12ee5e047bf 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -207,6 +207,7 @@ OBJS = adler32.o                                            
            \
        video_hint.o                                                     \
 
 BOUNDS_SAFETY_OBJS = adler32.o                                          \
+                     base64.o                                           \
                      cpu.o                                              \
                      csp.o                                              \
                      display.o                                          \
diff --git a/libavutil/base64.c b/libavutil/base64.c
index a6c21b05e72..73b5c66eb8b 100644
--- a/libavutil/base64.c
+++ b/libavutil/base64.c
@@ -27,6 +27,7 @@
 #include <limits.h>
 #include <stddef.h>
 
+#include "attributes_internal.h"
 #include "base64.h"
 #include "base64_internal.h"
 #include "error.h"
@@ -83,12 +84,12 @@ static const uint8_t map2[256] =
     v = i ? (v << 6) + bits : bits; \
 } while(0)
 
-int av_base64_decode(uint8_t *out, const char *in_str, int out_size)
+int av_base64_decode(uint8_t *av_counted_by_or_null(out_size) out, const char 
*in_str, int out_size)
 {
     uint8_t *dst = out;
     uint8_t *end;
     // no sign extension
-    const uint8_t *in = in_str;
+    const uint8_t *in = (const uint8_t *)FF_CSTR_TO_INDEXABLE(in_str);
     unsigned bits = 0xff;
     unsigned v;
 
@@ -149,7 +150,8 @@ out0:
 * Fixed edge cases and made it work from data (vs. strings) by Ryan.
 *****************************************************************************/
 
-char *av_base64_encode(char *out, int out_size, const uint8_t *in, int in_size)
+char *av_base64_encode(char *av_counted_by(out_size) out, int out_size,
+                       const uint8_t *av_counted_by(in_size) in, int in_size)
 {
     static const char b64[] =
         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
diff --git a/libavutil/base64.h b/libavutil/base64.h
index 2954c12d427..174aa0e0ce5 100644
--- a/libavutil/base64.h
+++ b/libavutil/base64.h
@@ -23,6 +23,8 @@
 
 #include <stdint.h>
 
+#include "attributes.h"
+
 /**
  * @defgroup lavu_base64 Base64
  * @ingroup lavu_crypto
@@ -39,7 +41,7 @@
  * @return         number of bytes written, or a negative value in case of
  *                 invalid input
  */
-int av_base64_decode(uint8_t *out, const char *in, int out_size);
+int av_base64_decode(uint8_t *av_counted_by_or_null(out_size) out, const char 
*in, int out_size);
 
 /**
  * Calculate the output size in bytes needed to decode a base64 string
@@ -57,7 +59,8 @@ int av_base64_decode(uint8_t *out, const char *in, int 
out_size);
  * @param in_size  size in bytes of the in buffer
  * @return         out or NULL in case of error
  */
-char *av_base64_encode(char *out, int out_size, const uint8_t *in, int 
in_size);
+char *av_base64_encode(char *av_counted_by(out_size) out, int out_size,
+                       const uint8_t *av_counted_by(in_size) in, int in_size);
 
 /**
  * Calculate the output size needed to base64-encode x bytes to a
diff --git a/libavutil/base64_internal.h b/libavutil/base64_internal.h
index 754720f9da9..3d17d3281d4 100644
--- a/libavutil/base64_internal.h
+++ b/libavutil/base64_internal.h
@@ -22,6 +22,8 @@
 #define AVUTIL_BASE64_INTERNAL_H
 
 #include <stdint.h>
+#include "attributes.h"
+#include "base64.h"
 #include "intreadwrite.h"
 
 /*****************************************************************************
@@ -30,8 +32,11 @@
 * Fixed edge cases and made it work from data (vs. strings) by Ryan.
 *****************************************************************************/
 
-static inline char *ff_base64_encode_c(char *out, const uint8_t *in, int 
in_size, const char *b64)
+static inline char *ff_base64_encode_c(char 
*av_counted_by(AV_BASE64_SIZE(in_size)) out,
+                                       const uint8_t *av_counted_by(in_size) 
in, int in_size,
+                                       const char *av_counted_by(64) b64)
 {
+    const uint8_t *ptr = in;
     char *ret, *dst;
     unsigned i_bits = 0;
     int i_shift = 0;
@@ -39,8 +44,8 @@ static inline char *ff_base64_encode_c(char *out, const 
uint8_t *in, int in_size
 
     ret = dst = out;
     while (bytes_remaining > 3) {
-        i_bits = AV_RB32(in);
-        in += 3; bytes_remaining -= 3;
+        i_bits = AV_RB32(ptr);
+        ptr += 3; bytes_remaining -= 3;
         *dst++ = b64[ i_bits>>26        ];
         *dst++ = b64[(i_bits>>20) & 0x3F];
         *dst++ = b64[(i_bits>>14) & 0x3F];
@@ -48,7 +53,7 @@ static inline char *ff_base64_encode_c(char *out, const 
uint8_t *in, int in_size
     }
     i_bits = 0;
     while (bytes_remaining) {
-        i_bits = (i_bits << 8) + *in++;
+        i_bits = (i_bits << 8) + *ptr++;
         bytes_remaining--;
         i_shift += 8;
     }
-- 
2.54.0


>From 4d243be42c71c5df311c68504b3697370233fa74 Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 06/14] avutil/crc: add bounds annotations

Signed-off-by: J. Dekker <[email protected]>
---
 libavutil/Makefile      |  1 +
 libavutil/aarch64/crc.h |  4 ++--
 libavutil/crc.c         | 33 +++++++++++++++++++--------------
 libavutil/crc.h         |  4 ++--
 libavutil/x86/crc.h     |  4 ++--
 5 files changed, 26 insertions(+), 20 deletions(-)

diff --git a/libavutil/Makefile b/libavutil/Makefile
index 12ee5e047bf..9ce65ef692d 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -209,6 +209,7 @@ OBJS = adler32.o                                            
            \
 BOUNDS_SAFETY_OBJS = adler32.o                                          \
                      base64.o                                           \
                      cpu.o                                              \
+                     crc.o                                              \
                      csp.o                                              \
                      display.o                                          \
                      integer.o                                          \
diff --git a/libavutil/aarch64/crc.h b/libavutil/aarch64/crc.h
index 90c7a834d02..c739667f5ae 100644
--- a/libavutil/aarch64/crc.h
+++ b/libavutil/aarch64/crc.h
@@ -115,7 +115,7 @@ static const AVCRC crc_table_pmull[AV_CRC_MAX][17] = {
 };
 
 
-static inline void crc_init_aarch64(AVCRC *ctx, int le, int bits, uint32_t 
poly, int ctx_size)
+static inline void crc_init_aarch64(AVCRC *av_sized_by(ctx_size) ctx, int le, 
int bits, uint32_t poly, int ctx_size)
 {
     uint64_t poly_;
     if (le) {
@@ -170,7 +170,7 @@ static const AVCRC crc_table_crc32_pmull[] = {
 #endif
 #endif
 
-static inline av_cold int ff_crc_init_aarch64(AVCRC *ctx, int le, int bits, 
uint32_t poly, int ctx_size)
+static inline av_cold int ff_crc_init_aarch64(AVCRC *av_sized_by(ctx_size) 
ctx, int le, int bits, uint32_t poly, int ctx_size)
 {
 #if HAVE_PMULL && HAVE_EOR3
     int cpu_flags = av_get_cpu_flags();
diff --git a/libavutil/crc.c b/libavutil/crc.c
index 335448f8db8..9254b2108aa 100644
--- a/libavutil/crc.c
+++ b/libavutil/crc.c
@@ -21,6 +21,7 @@
 #include "config.h"
 
 #include "thread.h"
+#include "attributes_internal.h"
 #include "avassert.h"
 #include "bswap.h"
 #include "crc.h"
@@ -343,7 +344,7 @@ DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_32_IEEE_LE, 1, 32, 
0xEDB88320)
 DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_16_ANSI_LE, 1, 16,     0xA001)
 #endif
 
-int av_crc_init(AVCRC *ctx, int le, int bits, uint32_t poly, int ctx_size)
+int av_crc_init(AVCRC *av_sized_by(ctx_size) ctx, int le, int bits, uint32_t 
poly, int ctx_size)
 {
     unsigned i, j;
     uint32_t c;
@@ -419,7 +420,7 @@ const AVCRC *av_crc_get_table(AVCRCId crc_id)
 }
 
 uint32_t av_crc(const AVCRC *ctx, uint32_t crc,
-                const uint8_t *buffer, size_t length)
+                const uint8_t *av_counted_by(length) buffer, size_t length)
 {
     if (ctx[0]) {
 #if ARCH_AARCH64
@@ -430,24 +431,28 @@ uint32_t av_crc(const AVCRC *ctx, uint32_t crc,
     }
     av_assert2(ctx[0] == 0);
 
-    const uint8_t *end = buffer + length;
+    const uint8_t *ptr = buffer;
+    const uint8_t *end = ptr + length;
 
+    /* Tables from av_crc_init() have 257 entries, or 1024 if ctx[256] == 0. */
+    const AVCRC *tab = FF_FORGE_BIDI(const AVCRC *, ctx, 257 * sizeof(*ctx));
 #if !CONFIG_SMALL
-    if (!ctx[256]) {
-        while (((intptr_t) buffer & 3) && buffer < end)
-            crc = ctx[((uint8_t) crc) ^ *buffer++] ^ (crc >> 8);
+    if (!tab[256]) {
+        tab = FF_FORGE_BIDI(const AVCRC *, ctx, 1024 * sizeof(*ctx));
+        while (((intptr_t) ptr & 3) && ptr < end)
+            crc = tab[((uint8_t) crc) ^ *ptr++] ^ (crc >> 8);
 
-        while (buffer < end - 3) {
-            crc ^= av_le2ne32(*(const uint32_t *) buffer); buffer += 4;
-            crc = ctx[3 * 256 + ( crc        & 0xFF)] ^
-                  ctx[2 * 256 + ((crc >> 8 ) & 0xFF)] ^
-                  ctx[1 * 256 + ((crc >> 16) & 0xFF)] ^
-                  ctx[0 * 256 + ((crc >> 24)       )];
+        while (ptr < end - 3) {
+            crc ^= av_le2ne32(*(const uint32_t *) ptr); ptr += 4;
+            crc = tab[3 * 256 + ( crc        & 0xFF)] ^
+                  tab[2 * 256 + ((crc >> 8 ) & 0xFF)] ^
+                  tab[1 * 256 + ((crc >> 16) & 0xFF)] ^
+                  tab[0 * 256 + ((crc >> 24)       )];
         }
     }
 #endif
-    while (buffer < end)
-        crc = ctx[((uint8_t) crc) ^ *buffer++] ^ (crc >> 8);
+    while (ptr < end)
+        crc = tab[((uint8_t) crc) ^ *ptr++] ^ (crc >> 8);
 
     return crc;
 }
diff --git a/libavutil/crc.h b/libavutil/crc.h
index 7f59812a18c..ce22044969f 100644
--- a/libavutil/crc.h
+++ b/libavutil/crc.h
@@ -73,7 +73,7 @@ typedef enum {
  * @param ctx_size size of ctx in bytes
  * @return <0 on failure
  */
-int av_crc_init(AVCRC *ctx, int le, int bits, uint32_t poly, int ctx_size);
+int av_crc_init(AVCRC *av_sized_by(ctx_size) ctx, int le, int bits, uint32_t 
poly, int ctx_size);
 
 /**
  * Get an initialized standard CRC table.
@@ -93,7 +93,7 @@ const AVCRC *av_crc_get_table(AVCRCId crc_id);
  * @see av_crc_init() "le" parameter
  */
 uint32_t av_crc(const AVCRC *ctx, uint32_t crc,
-                const uint8_t *buffer, size_t length) av_pure;
+                const uint8_t *av_counted_by(length) buffer, size_t length) 
av_pure;
 
 /**
  * @}
diff --git a/libavutil/x86/crc.h b/libavutil/x86/crc.h
index ef98ed318de..911eb6cd59a 100644
--- a/libavutil/x86/crc.h
+++ b/libavutil/x86/crc.h
@@ -105,7 +105,7 @@ static const AVCRC crc_table_clmul[AV_CRC_MAX][17] = {
     },
 };
 
-static inline void crc_init_x86(AVCRC *ctx, int le, int bits, uint32_t poly, 
int ctx_size)
+static inline void crc_init_x86(AVCRC *av_sized_by(ctx_size) ctx, int le, int 
bits, uint32_t poly, int ctx_size)
 {
     uint64_t poly_;
     if (le) {
@@ -154,7 +154,7 @@ static inline const AVCRC *ff_crc_get_table_x86(AVCRCId 
crc_id)
     return NULL;
 }
 
-static inline av_cold int ff_crc_init_x86(AVCRC *ctx, int le, int bits, 
uint32_t poly, int ctx_size)
+static inline av_cold int ff_crc_init_x86(AVCRC *av_sized_by(ctx_size) ctx, 
int le, int bits, uint32_t poly, int ctx_size)
 {
 #if HAVE_CLMUL_EXTERNAL
     int cpu_flags = av_get_cpu_flags();
-- 
2.54.0


>From d31e879a9deb07f5d46d270d148b81e8131984de Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 07/14] avutil/md5: add bounds annotations

Signed-off-by: J. Dekker <[email protected]>
---
 libavutil/Makefile |  1 +
 libavutil/md5.c    | 42 ++++++++++++++++++++++--------------------
 libavutil/md5.h    |  6 +++---
 3 files changed, 26 insertions(+), 23 deletions(-)

diff --git a/libavutil/Makefile b/libavutil/Makefile
index 9ce65ef692d..bc6fcf308ef 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -215,6 +215,7 @@ BOUNDS_SAFETY_OBJS = adler32.o                              
            \
                      integer.o                                          \
                      intmath.o                                          \
                      log2_tab.o                                         \
+                     md5.o                                              \
                      reverse.o                                          \
                      time.o                                             \
                      timecode_internal.o                                \
diff --git a/libavutil/md5.c b/libavutil/md5.c
index c01820da2df..00a9108c278 100644
--- a/libavutil/md5.c
+++ b/libavutil/md5.c
@@ -100,7 +100,7 @@ static const uint32_t T[64] = { // T[i]= fabs(sin(i+1)<<32)
         a = b + (a << t | a >> (32 - t));                               \
     } while (0)
 
-static void body(uint32_t ABCD[4], const uint8_t *src, size_t nblocks)
+static void body(uint32_t ABCD[4], const uint8_t *av_counted_by(nblocks * 64) 
src, size_t nblocks)
 {
     const uint32_t *X;
     uint32_t a, b, c, d, t;
@@ -150,42 +150,44 @@ void av_md5_init(AVMD5 *ctx)
     ctx->ABCD[3] = 0x67452301;
 }
 
-void av_md5_update(AVMD5 *ctx, const uint8_t *src, size_t len)
+void av_md5_update(AVMD5 *ctx, const uint8_t *av_counted_by(len) src, size_t 
len)
 {
+    const uint8_t *ptr = src;
+    size_t left = len;
     const uint8_t *end;
     int j;
 
     j         = ctx->len & 63;
-    ctx->len += len;
+    ctx->len += left;
 
     if (j) {
-        int cnt = FFMIN(len, 64 - j);
-        memcpy(ctx->block + j, src, cnt);
-        src += cnt;
-        len -= cnt;
+        int cnt = FFMIN(left, 64 - j);
+        memcpy(ctx->block + j, ptr, cnt);
+        ptr += cnt;
+        left -= cnt;
         if (j + cnt < 64)
             return;
         body(ctx->ABCD, ctx->block, 1);
     }
 
-    end = src + (len & ~63);
-    if (!HAVE_FAST_UNALIGNED && ((intptr_t)src & 3)) {
-       while (src < end) {
-           memcpy(ctx->block, src, 64);
+    end = ptr + (left & ~63);
+    if (!HAVE_FAST_UNALIGNED && ((intptr_t)ptr & 3)) {
+       while (ptr < end) {
+           memcpy(ctx->block, ptr, 64);
            body(ctx->ABCD, ctx->block, 1);
-           src += 64;
+           ptr += 64;
         }
     } else {
-        size_t nblocks = len / 64;
-        body(ctx->ABCD, src, nblocks);
-        src = end;
+        size_t nblocks = left / 64;
+        body(ctx->ABCD, ptr, nblocks);
+        ptr = end;
     }
-    len &= 63;
-    if (len > 0)
-        memcpy(ctx->block, src, len);
+    left &= 63;
+    if (left > 0)
+        memcpy(ctx->block, ptr, left);
 }
 
-void av_md5_final(AVMD5 *ctx, uint8_t *dst)
+void av_md5_final(AVMD5 *ctx, uint8_t *av_counted_by(16) dst)
 {
     int i;
     uint64_t finalcount = av_le2ne64(ctx->len << 3);
@@ -200,7 +202,7 @@ void av_md5_final(AVMD5 *ctx, uint8_t *dst)
         AV_WL32(dst + 4 * i, ctx->ABCD[3 - i]);
 }
 
-void av_md5_sum(uint8_t *dst, const uint8_t *src, size_t len)
+void av_md5_sum(uint8_t *av_counted_by(16) dst, const uint8_t 
*av_counted_by(len) src, size_t len)
 {
     AVMD5 ctx;
 
diff --git a/libavutil/md5.h b/libavutil/md5.h
index fc2eabdb162..f20927f52c5 100644
--- a/libavutil/md5.h
+++ b/libavutil/md5.h
@@ -63,7 +63,7 @@ void av_md5_init(struct AVMD5 *ctx);
  * @param src input data to update hash with
  * @param len input data length
  */
-void av_md5_update(struct AVMD5 *ctx, const uint8_t *src, size_t len);
+void av_md5_update(struct AVMD5 *ctx, const uint8_t *av_counted_by(len) src, 
size_t len);
 
 /**
  * Finish hashing and output digest value.
@@ -71,7 +71,7 @@ void av_md5_update(struct AVMD5 *ctx, const uint8_t *src, 
size_t len);
  * @param ctx hash function context
  * @param dst buffer where output digest value is stored
  */
-void av_md5_final(struct AVMD5 *ctx, uint8_t *dst);
+void av_md5_final(struct AVMD5 *ctx, uint8_t *av_counted_by(16) dst);
 
 /**
  * Hash an array of data.
@@ -80,7 +80,7 @@ void av_md5_final(struct AVMD5 *ctx, uint8_t *dst);
  * @param src The data to hash
  * @param len The length of the data, in bytes
  */
-void av_md5_sum(uint8_t *dst, const uint8_t *src, size_t len);
+void av_md5_sum(uint8_t *av_counted_by(16) dst, const uint8_t 
*av_counted_by(len) src, size_t len);
 
 /**
  * @}
-- 
2.54.0


>From 9a9696b832589bac294368487fbcec6abb90bd2f Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 08/14] avutil/murmur3: add bounds annotations

Signed-off-by: J. Dekker <[email protected]>
---
 libavutil/Makefile  |  1 +
 libavutil/murmur3.c | 34 ++++++++++++++++++----------------
 libavutil/murmur3.h |  4 +++-
 3 files changed, 22 insertions(+), 17 deletions(-)

diff --git a/libavutil/Makefile b/libavutil/Makefile
index bc6fcf308ef..b4b81d8243a 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -216,6 +216,7 @@ BOUNDS_SAFETY_OBJS = adler32.o                              
            \
                      intmath.o                                          \
                      log2_tab.o                                         \
                      md5.o                                              \
+                     murmur3.o                                          \
                      reverse.o                                          \
                      time.o                                             \
                      timecode_internal.o                                \
diff --git a/libavutil/murmur3.c b/libavutil/murmur3.c
index c88a236a0c2..4568a7127a5 100644
--- a/libavutil/murmur3.c
+++ b/libavutil/murmur3.c
@@ -54,7 +54,7 @@ static const uint64_t c2 = UINT64_C(0x4cf5ad432745937f);
 
 #define ROT(a, b) (((a) << (b)) | ((a) >> (64 - (b))))
 
-static uint64_t inline get_k1(const uint8_t *src)
+static uint64_t inline get_k1(const uint8_t *av_counted_by(16) src)
 {
     uint64_t k = AV_RL64(src);
     k *= c1;
@@ -63,7 +63,7 @@ static uint64_t inline get_k1(const uint8_t *src)
     return k;
 }
 
-static inline uint64_t get_k2(const uint8_t *src)
+static inline uint64_t get_k2(const uint8_t *av_counted_by(16) src)
 {
     uint64_t k = AV_RL64(src + 8);
     k *= c2;
@@ -92,17 +92,19 @@ static inline uint64_t update_h2(uint64_t k, uint64_t h1, 
uint64_t h2)
     return k;
 }
 
-void av_murmur3_update(AVMurMur3 *c, const uint8_t *src, size_t len)
+void av_murmur3_update(AVMurMur3 *c, const uint8_t *av_counted_by(len) src, 
size_t len)
 {
+    const uint8_t *ptr = src;
+    size_t left = len;
     const uint8_t *end;
     uint64_t h1 = c->h1, h2 = c->h2;
     uint64_t k1, k2;
-    if (len <= 0) return;
-    c->len += len;
+    if (left <= 0) return;
+    c->len += left;
     if (c->state_pos > 0) {
         while (c->state_pos < 16) {
-            c->state[c->state_pos++] = *src++;
-            if (--len <= 0) return;
+            c->state[c->state_pos++] = *ptr++;
+            if (--left <= 0) return;
         }
         c->state_pos = 0;
         k1 = get_k1(c->state);
@@ -111,23 +113,23 @@ void av_murmur3_update(AVMurMur3 *c, const uint8_t *src, 
size_t len)
         h2 = update_h2(k2, h1, h2);
     }
 
-    end = src + (len & ~15);
-    while (src < end) {
+    end = ptr + (left & ~15);
+    while (ptr < end) {
         // These could be done sequentially instead
         // of interleaved, but like this is over 10% faster
-        k1 = get_k1(src);
-        k2 = get_k2(src);
+        k1 = get_k1(ptr);
+        k2 = get_k2(ptr);
         h1 = update_h1(k1, h1, h2);
         h2 = update_h2(k2, h1, h2);
-        src += 16;
+        ptr += 16;
     }
     c->h1 = h1;
     c->h2 = h2;
 
-    len &= 15;
-    if (len > 0) {
-        memcpy(c->state, src, len);
-        c->state_pos = len;
+    left &= 15;
+    if (left > 0) {
+        memcpy(c->state, ptr, left);
+        c->state_pos = left;
     }
 }
 
diff --git a/libavutil/murmur3.h b/libavutil/murmur3.h
index d90bc2fcd13..0898578ae98 100644
--- a/libavutil/murmur3.h
+++ b/libavutil/murmur3.h
@@ -30,6 +30,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "attributes.h"
+
 /**
  * @defgroup lavu_murmur3 Murmur3
  * @ingroup lavu_hash
@@ -98,7 +100,7 @@ void av_murmur3_init(struct AVMurMur3 *c);
  * @param[in]  src  Input data to update hash with
  * @param[in]  len  Number of bytes to read from `src`
  */
-void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *src, size_t len);
+void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *av_counted_by(len) 
src, size_t len);
 
 /**
  * Finish hashing and output digest value.
-- 
2.54.0


>From 0539c0bf4de446c1a7bfb4e756b9e0cb194ce498 Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 09/14] avutil/rc4: add bounds annotations

Signed-off-by: J. Dekker <[email protected]>
---
 libavutil/Makefile |  1 +
 libavutil/rc4.c    | 13 +++++++++----
 libavutil/rc4.h    |  8 ++++++--
 3 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/libavutil/Makefile b/libavutil/Makefile
index b4b81d8243a..4dec0531f96 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -217,6 +217,7 @@ BOUNDS_SAFETY_OBJS = adler32.o                              
            \
                      log2_tab.o                                         \
                      md5.o                                              \
                      murmur3.o                                          \
+                     rc4.o                                              \
                      reverse.o                                          \
                      time.o                                             \
                      timecode_internal.o                                \
diff --git a/libavutil/rc4.c b/libavutil/rc4.c
index 74c1f4892e6..e3c5540dd9e 100644
--- a/libavutil/rc4.c
+++ b/libavutil/rc4.c
@@ -31,7 +31,7 @@ AVRC4 *av_rc4_alloc(void)
     return av_mallocz(sizeof(struct AVRC4));
 }
 
-int av_rc4_init(AVRC4 *r, const uint8_t *key, int key_bits, int decrypt) {
+int av_rc4_init(AVRC4 *r, const uint8_t *av_counted_by(key_bits >> 3) key, int 
key_bits, int decrypt) {
     int i, j;
     uint8_t y;
     uint8_t *state = r->state;
@@ -52,13 +52,18 @@ int av_rc4_init(AVRC4 *r, const uint8_t *key, int key_bits, 
int decrypt) {
     return 0;
 }
 
-void av_rc4_crypt(AVRC4 *r, uint8_t *dst, const uint8_t *src, int count, 
uint8_t *iv, int decrypt) {
+void av_rc4_crypt(AVRC4 *r, uint8_t *av_counted_by(count) dst,
+                  const uint8_t *av_counted_by_or_null(count) src, int count,
+                  uint8_t *iv, int decrypt) {
     uint8_t x = r->x, y = r->y;
     uint8_t *state = r->state;
-    while (count-- > 0) {
+    uint8_t *d = dst;
+    const uint8_t *s = src;
+    int n = count;
+    while (n-- > 0) {
         uint8_t sum = state[x] + state[y];
         FFSWAP(uint8_t, state[x], state[y]);
-        *dst++ = src ? *src++ ^ state[sum] : state[sum];
+        *d++ = s ? *s++ ^ state[sum] : state[sum];
         x++;
         y += state[x];
     }
diff --git a/libavutil/rc4.h b/libavutil/rc4.h
index 0c72a88ecd7..9023b596f9a 100644
--- a/libavutil/rc4.h
+++ b/libavutil/rc4.h
@@ -23,6 +23,8 @@
 
 #include <stdint.h>
 
+#include "attributes.h"
+
 /**
  * @defgroup lavu_rc4 RC4
  * @ingroup lavu_crypto
@@ -48,7 +50,7 @@ AVRC4 *av_rc4_alloc(void);
  * @param decrypt 0 for encryption, 1 for decryption, currently has no effect
  * @return zero on success, negative value otherwise
  */
-int av_rc4_init(struct AVRC4 *d, const uint8_t *key, int key_bits, int 
decrypt);
+int av_rc4_init(struct AVRC4 *d, const uint8_t *av_counted_by(key_bits >> 3) 
key, int key_bits, int decrypt);
 
 /**
  * @brief Encrypts / decrypts using the RC4 algorithm.
@@ -60,7 +62,9 @@ int av_rc4_init(struct AVRC4 *d, const uint8_t *key, int 
key_bits, int decrypt);
  * @param iv not (yet) used for RC4, should be NULL
  * @param decrypt 0 for encryption, 1 for decryption, not (yet) used
  */
-void av_rc4_crypt(struct AVRC4 *d, uint8_t *dst, const uint8_t *src, int 
count, uint8_t *iv, int decrypt);
+void av_rc4_crypt(struct AVRC4 *d, uint8_t *av_counted_by(count) dst,
+                  const uint8_t *av_counted_by_or_null(count) src, int count,
+                  uint8_t *iv, int decrypt);
 
 /**
  * @}
-- 
2.54.0


>From 579c9dec0116bc412cd55f5249524908c8012785 Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 10/14] avutil/random_seed: add bounds annotations

Signed-off-by: J. Dekker <[email protected]>
---
 libavutil/Makefile      | 1 +
 libavutil/random_seed.c | 4 ++--
 libavutil/random_seed.h | 4 +++-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/libavutil/Makefile b/libavutil/Makefile
index 4dec0531f96..161a64025ac 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -217,6 +217,7 @@ BOUNDS_SAFETY_OBJS = adler32.o                              
            \
                      log2_tab.o                                         \
                      md5.o                                              \
                      murmur3.o                                          \
+                     random_seed.o                                      \
                      rc4.o                                              \
                      reverse.o                                          \
                      time.o                                             \
diff --git a/libavutil/random_seed.c b/libavutil/random_seed.c
index e08ab7b12e8..90fb3a56ad0 100644
--- a/libavutil/random_seed.c
+++ b/libavutil/random_seed.c
@@ -51,7 +51,7 @@
 #define TEST 0
 #endif
 
-static int read_random(uint8_t *dst, size_t len, const char *file)
+static int read_random(uint8_t *av_counted_by(len) dst, size_t len, const char 
*file)
 {
 #if HAVE_UNISTD_H
     FILE *fp = avpriv_fopen_utf8(file, "r");
@@ -156,7 +156,7 @@ static uint32_t get_generic_seed(void)
     return AV_RB32(digest) + AV_RB32(digest + 16);
 }
 
-int av_random_bytes(uint8_t* buf, size_t len)
+int av_random_bytes(uint8_t *av_counted_by(len) buf, size_t len)
 {
     int err;
 
diff --git a/libavutil/random_seed.h b/libavutil/random_seed.h
index 8a47be96796..012323378d5 100644
--- a/libavutil/random_seed.h
+++ b/libavutil/random_seed.h
@@ -23,6 +23,8 @@
 
 #include <stddef.h>
 #include <stdint.h>
+
+#include "attributes.h"
 /**
  * @addtogroup lavu_crypto
  * @{
@@ -48,7 +50,7 @@ uint32_t av_get_random_seed(void);
  *                                   into buf
  * @retval "a negative AVERROR code" random data could not be generated
  */
-int av_random_bytes(uint8_t *buf, size_t len);
+int av_random_bytes(uint8_t *av_counted_by(len) buf, size_t len);
 
 /**
  * @}
-- 
2.54.0


>From 936aa95869d3876d3a71a6bb6b9cc2a96aef5a5a Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 11/14] avutil/timecode: add bounds annotations

Signed-off-by: J. Dekker <[email protected]>
---
 libavutil/Makefile   | 1 +
 libavutil/timecode.c | 8 ++++----
 libavutil/timecode.h | 9 +++++----
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/libavutil/Makefile b/libavutil/Makefile
index 161a64025ac..c13dc85fd72 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -221,6 +221,7 @@ BOUNDS_SAFETY_OBJS = adler32.o                              
            \
                      rc4.o                                              \
                      reverse.o                                          \
                      time.o                                             \
+                     timecode.o                                         \
                      timecode_internal.o                                \
                      xga_font_data.o                                    \
 
diff --git a/libavutil/timecode.c b/libavutil/timecode.c
index 316100759e0..dc4479b0d2b 100644
--- a/libavutil/timecode.c
+++ b/libavutil/timecode.c
@@ -101,7 +101,7 @@ uint32_t av_timecode_get_smpte(AVRational rate, int drop, 
int hh, int mm, int ss
     return tc;
 }
 
-char *av_timecode_make_string(const AVTimecode *tc, char *buf, int 
framenum_arg)
+char *av_timecode_make_string(const AVTimecode *tc, char 
*av_counted_by(AV_TIMECODE_STR_SIZE) buf, int framenum_arg)
 {
     unsigned fps = tc->fps;
     int drop = tc->flags & AV_TIMECODE_FLAG_DROPFRAME;
@@ -128,7 +128,7 @@ char *av_timecode_make_string(const AVTimecode *tc, char 
*buf, int framenum_arg)
     return buf;
 }
 
-char *av_timecode_make_smpte_tc_string2(char *buf, AVRational rate, uint32_t 
tcsmpte, int prevent_df, int skip_field)
+char *av_timecode_make_smpte_tc_string2(char 
*av_counted_by(AV_TIMECODE_STR_SIZE) buf, AVRational rate, uint32_t tcsmpte, 
int prevent_df, int skip_field)
 {
     unsigned hh, mm, ss, ff, drop;
     ff_timecode_set_smpte(&drop, &hh, &mm, &ss, &ff, rate, tcsmpte, 
prevent_df, skip_field);
@@ -139,12 +139,12 @@ char *av_timecode_make_smpte_tc_string2(char *buf, 
AVRational rate, uint32_t tcs
 
 }
 
-char *av_timecode_make_smpte_tc_string(char *buf, uint32_t tcsmpte, int 
prevent_df)
+char *av_timecode_make_smpte_tc_string(char 
*av_counted_by(AV_TIMECODE_STR_SIZE) buf, uint32_t tcsmpte, int prevent_df)
 {
     return av_timecode_make_smpte_tc_string2(buf, (AVRational){30, 1}, 
tcsmpte, prevent_df, 1);
 }
 
-char *av_timecode_make_mpeg_tc_string(char *buf, uint32_t tc25bit)
+char *av_timecode_make_mpeg_tc_string(char 
*av_counted_by(AV_TIMECODE_STR_SIZE) buf, uint32_t tc25bit)
 {
     snprintf(buf, AV_TIMECODE_STR_SIZE,
              "%02"PRIu32":%02"PRIu32":%02"PRIu32"%c%02"PRIu32,
diff --git a/libavutil/timecode.h b/libavutil/timecode.h
index fe0fc835764..f92ce9cb414 100644
--- a/libavutil/timecode.h
+++ b/libavutil/timecode.h
@@ -28,6 +28,7 @@
 #define AVUTIL_TIMECODE_H
 
 #include <stdint.h>
+#include "attributes.h"
 #include "rational.h"
 
 #define AV_TIMECODE_STR_SIZE 23
@@ -107,7 +108,7 @@ uint32_t av_timecode_get_smpte(AVRational rate, int drop, 
int hh, int mm, int ss
  *       24 hours, but will only be honored if the flags are correctly set.
  * @note The frame number is relative to tc->start.
  */
-char *av_timecode_make_string(const AVTimecode *tc, char *buf, int framenum);
+char *av_timecode_make_string(const AVTimecode *tc, char 
*av_counted_by(AV_TIMECODE_STR_SIZE) buf, int framenum);
 
 /**
  * Get the timecode string from the SMPTE timecode format.
@@ -124,7 +125,7 @@ char *av_timecode_make_string(const AVTimecode *tc, char 
*buf, int framenum);
  *                   bit is arbitrary (e.g. because it is used as PC flag)
  * @return           the buf parameter
  */
-char *av_timecode_make_smpte_tc_string2(char *buf, AVRational rate, uint32_t 
tcsmpte, int prevent_df, int skip_field);
+char *av_timecode_make_smpte_tc_string2(char 
*av_counted_by(AV_TIMECODE_STR_SIZE) buf, AVRational rate, uint32_t tcsmpte, 
int prevent_df, int skip_field);
 
 /**
  * Get the timecode string from the SMPTE timecode format.
@@ -135,7 +136,7 @@ char *av_timecode_make_smpte_tc_string2(char *buf, 
AVRational rate, uint32_t tcs
  *                   is arbitrary
  * @return           the buf parameter
  */
-char *av_timecode_make_smpte_tc_string(char *buf, uint32_t tcsmpte, int 
prevent_df);
+char *av_timecode_make_smpte_tc_string(char 
*av_counted_by(AV_TIMECODE_STR_SIZE) buf, uint32_t tcsmpte, int prevent_df);
 
 /**
  * Get the timecode string from the 25-bit timecode format (MPEG GOP format).
@@ -144,7 +145,7 @@ char *av_timecode_make_smpte_tc_string(char *buf, uint32_t 
tcsmpte, int prevent_
  * @param tc25bit the 25-bits timecode
  * @return        the buf parameter
  */
-char *av_timecode_make_mpeg_tc_string(char *buf, uint32_t tc25bit);
+char *av_timecode_make_mpeg_tc_string(char 
*av_counted_by(AV_TIMECODE_STR_SIZE) buf, uint32_t tc25bit);
 
 /**
  * Init a timecode struct with the passed parameters.
-- 
2.54.0


>From 7443325798e25622450a5925cd99533bf62002a5 Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 12/14] avutil/hmac: add bounds annotations

Pass the digest length to the final() callbacks, as av_md5_final()
requires its size.

Signed-off-by: J. Dekker <[email protected]>
---
 libavutil/Makefile |  1 +
 libavutil/hmac.c   | 29 ++++++++++++++++-------------
 libavutil/hmac.h   | 14 ++++++++------
 3 files changed, 25 insertions(+), 19 deletions(-)

diff --git a/libavutil/Makefile b/libavutil/Makefile
index c13dc85fd72..4db1e2be32b 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -212,6 +212,7 @@ BOUNDS_SAFETY_OBJS = adler32.o                              
            \
                      crc.o                                              \
                      csp.o                                              \
                      display.o                                          \
+                     hmac.o                                             \
                      integer.o                                          \
                      intmath.o                                          \
                      log2_tab.o                                         \
diff --git a/libavutil/hmac.c b/libavutil/hmac.c
index 6daca00b8fe..9ccaa373232 100644
--- a/libavutil/hmac.c
+++ b/libavutil/hmac.c
@@ -33,8 +33,8 @@
 #define MAX_HASHLEN 64
 #define MAX_BLOCKLEN 128
 
-typedef void (*hmac_final)(void *ctx, uint8_t *dst);
-typedef void (*hmac_update)(void *ctx, const uint8_t *src, size_t len);
+typedef void (*hmac_final)(void *ctx, uint8_t *av_counted_by(len) dst, int 
len);
+typedef void (*hmac_update)(void *ctx, const uint8_t *av_counted_by(len) src, 
size_t len);
 typedef void (*hmac_init)(void *ctx);
 
 struct AVHMAC {
@@ -60,11 +60,14 @@ static av_cold void prefix##_init(void *ctx) \
 }
 
 #define DEFINE_ALGO(prefix)                                            \
-static void prefix##_update(void *ctx, const uint8_t *src, size_t len) \
+static void prefix##_update(void *ctx,                                 \
+                            const uint8_t *av_counted_by(len) src,     \
+                            size_t len)                                \
 {                                                                      \
     av_##prefix##_update(ctx, src, len);                               \
 }                                                                      \
-static void prefix##_final(void *ctx, uint8_t *dst)                    \
+static void prefix##_final(void *ctx, uint8_t *av_counted_by(len) dst, \
+                           int len)                                    \
 {                                                                      \
     av_##prefix##_final(ctx, dst);                                     \
 }
@@ -152,14 +155,14 @@ void av_hmac_free(AVHMAC *c)
     av_free(c);
 }
 
-void av_hmac_init(AVHMAC *c, const uint8_t *key, unsigned int keylen)
+void av_hmac_init(AVHMAC *c, const uint8_t *av_counted_by(keylen) key, 
unsigned int keylen)
 {
     int i;
     uint8_t block[MAX_BLOCKLEN];
     if (keylen > c->blocklen) {
         c->init(c->hash);
         c->update(c->hash, key, keylen);
-        c->final(c->hash, c->key);
+        c->final(c->hash, c->key, c->hashlen);
         c->keylen = c->hashlen;
     } else {
         memcpy(c->key, key, keylen);
@@ -173,18 +176,18 @@ void av_hmac_init(AVHMAC *c, const uint8_t *key, unsigned 
int keylen)
     c->update(c->hash, block, c->blocklen);
 }
 
-void av_hmac_update(AVHMAC *c, const uint8_t *data, unsigned int len)
+void av_hmac_update(AVHMAC *c, const uint8_t *av_counted_by(len) data, 
unsigned int len)
 {
     c->update(c->hash, data, len);
 }
 
-int av_hmac_final(AVHMAC *c, uint8_t *out, unsigned int outlen)
+int av_hmac_final(AVHMAC *c, uint8_t *av_counted_by(outlen) out, unsigned int 
outlen)
 {
     uint8_t block[MAX_BLOCKLEN];
     int i;
     if (outlen < c->hashlen)
         return AVERROR(EINVAL);
-    c->final(c->hash, out);
+    c->final(c->hash, out, c->hashlen);
     c->init(c->hash);
     for (i = 0; i < c->keylen; i++)
         block[i] = c->key[i] ^ 0x5C;
@@ -192,13 +195,13 @@ int av_hmac_final(AVHMAC *c, uint8_t *out, unsigned int 
outlen)
         block[i] = 0x5C;
     c->update(c->hash, block, c->blocklen);
     c->update(c->hash, out, c->hashlen);
-    c->final(c->hash, out);
+    c->final(c->hash, out, c->hashlen);
     return c->hashlen;
 }
 
-int av_hmac_calc(AVHMAC *c, const uint8_t *data, unsigned int len,
-                 const uint8_t *key, unsigned int keylen,
-                 uint8_t *out, unsigned int outlen)
+int av_hmac_calc(AVHMAC *c, const uint8_t *av_counted_by(len) data, unsigned 
int len,
+                 const uint8_t *av_counted_by(keylen) key, unsigned int keylen,
+                 uint8_t *av_counted_by(outlen) out, unsigned int outlen)
 {
     av_hmac_init(c, key, keylen);
     av_hmac_update(c, data, len);
diff --git a/libavutil/hmac.h b/libavutil/hmac.h
index ca4da6a689d..05568bba2f7 100644
--- a/libavutil/hmac.h
+++ b/libavutil/hmac.h
@@ -23,6 +23,8 @@
 
 #include <stdint.h>
 
+#include "attributes.h"
+
 /**
  * @defgroup lavu_hmac HMAC
  * @ingroup lavu_crypto
@@ -58,7 +60,7 @@ void av_hmac_free(AVHMAC *ctx);
  * @param key    The authentication key
  * @param keylen The length of the key, in bytes
  */
-void av_hmac_init(AVHMAC *ctx, const uint8_t *key, unsigned int keylen);
+void av_hmac_init(AVHMAC *ctx, const uint8_t *av_counted_by(keylen) key, 
unsigned int keylen);
 
 /**
  * Hash data with the HMAC.
@@ -66,7 +68,7 @@ void av_hmac_init(AVHMAC *ctx, const uint8_t *key, unsigned 
int keylen);
  * @param data The data to hash
  * @param len  The length of the data, in bytes
  */
-void av_hmac_update(AVHMAC *ctx, const uint8_t *data, unsigned int len);
+void av_hmac_update(AVHMAC *ctx, const uint8_t *av_counted_by(len) data, 
unsigned int len);
 
 /**
  * Finish hashing and output the HMAC digest.
@@ -75,7 +77,7 @@ void av_hmac_update(AVHMAC *ctx, const uint8_t *data, 
unsigned int len);
  * @param outlen The length of the out buffer, in bytes
  * @return       The number of bytes written to out, or a negative error code.
  */
-int av_hmac_final(AVHMAC *ctx, uint8_t *out, unsigned int outlen);
+int av_hmac_final(AVHMAC *ctx, uint8_t *av_counted_by(outlen) out, unsigned 
int outlen);
 
 /**
  * Hash an array of data with a key.
@@ -88,9 +90,9 @@ int av_hmac_final(AVHMAC *ctx, uint8_t *out, unsigned int 
outlen);
  * @param outlen The length of the out buffer, in bytes
  * @return       The number of bytes written to out, or a negative error code.
  */
-int av_hmac_calc(AVHMAC *ctx, const uint8_t *data, unsigned int len,
-                 const uint8_t *key, unsigned int keylen,
-                 uint8_t *out, unsigned int outlen);
+int av_hmac_calc(AVHMAC *ctx, const uint8_t *av_counted_by(len) data, unsigned 
int len,
+                 const uint8_t *av_counted_by(keylen) key, unsigned int keylen,
+                 uint8_t *av_counted_by(outlen) out, unsigned int outlen);
 
 /**
  * @}
-- 
2.54.0


>From d97491e459b80390d5ecce9552c8fb4e33b147cd Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 13/14] avutil/avstring: annotate av_strnlen()

Matches the bounds-safe declaration of strnlen().

Signed-off-by: J. Dekker <[email protected]>
---
 libavutil/avstring.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavutil/avstring.h b/libavutil/avstring.h
index 17f7b03db52..f64e4200297 100644
--- a/libavutil/avstring.h
+++ b/libavutil/avstring.h
@@ -138,7 +138,7 @@ size_t av_strlcatf(char *dst, size_t size, const char *fmt, 
...) av_printf_forma
  * @param len maximum number of characters to check in the string, that
  *            is the maximum value which is returned by the function
  */
-static inline size_t av_strnlen(const char *s, size_t len)
+static inline size_t av_strnlen(const char *av_counted_by(len) s, size_t len)
 {
     size_t i;
     for (i = 0; i < len && s[i]; i++)
-- 
2.54.0


>From f200ca3049b349d6b587c63f93ba526ce4dc1137 Mon Sep 17 00:00:00 2001
From: "J. Dekker" <[email protected]>
Date: Sun, 25 Oct 2026 16:54:36 +0100
Subject: [PATCH 14/14] avutil/hash: add bounds annotations

Signed-off-by: J. Dekker <[email protected]>
---
 libavutil/Makefile |  1 +
 libavutil/hash.c   | 31 +++++++++++++++++--------------
 libavutil/hash.h   | 10 ++++++----
 3 files changed, 24 insertions(+), 18 deletions(-)

diff --git a/libavutil/Makefile b/libavutil/Makefile
index 4db1e2be32b..8ba263a45b4 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -212,6 +212,7 @@ BOUNDS_SAFETY_OBJS = adler32.o                              
            \
                      crc.o                                              \
                      csp.o                                              \
                      display.o                                          \
+                     hash.o                                             \
                      hmac.o                                             \
                      integer.o                                          \
                      intmath.o                                          \
diff --git a/libavutil/hash.c b/libavutil/hash.c
index fbc24194dea..a8469c348e8 100644
--- a/libavutil/hash.c
+++ b/libavutil/hash.c
@@ -33,6 +33,7 @@
 #include "sha.h"
 #include "sha512.h"
 
+#include "attributes_internal.h"
 #include "avstring.h"
 #include "base64.h"
 #include "error.h"
@@ -98,12 +99,12 @@ static const struct {
 const char *av_hash_names(int i)
 {
     if (i < 0 || i >= NUM_HASHES) return NULL;
-    return hashdesc[i].name;
+    return FF_CSTR_FROM_ARRAY(hashdesc[i].name);
 }
 
 const char *av_hash_get_name(const AVHashContext *ctx)
 {
-    return hashdesc[ctx->type].name;
+    return FF_CSTR_FROM_ARRAY(hashdesc[ctx->type].name);
 }
 
 int av_hash_get_size(const AVHashContext *ctx)
@@ -117,7 +118,7 @@ int av_hash_alloc(AVHashContext **ctx, const char *name)
     int i;
     *ctx = NULL;
     for (i = 0; i < NUM_HASHES; i++)
-        if (av_strcasecmp(name, hashdesc[i].name) == 0)
+        if (av_strcasecmp(name, FF_CSTR_FROM_ARRAY(hashdesc[i].name)) == 0)
             break;
     if (i >= NUM_HASHES) return AVERROR(EINVAL);
     res = av_mallocz(sizeof(*res));
@@ -169,7 +170,7 @@ void av_hash_init(AVHashContext *ctx)
     }
 }
 
-void av_hash_update(AVHashContext *ctx, const uint8_t *src, size_t len)
+void av_hash_update(AVHashContext *ctx, const uint8_t *av_counted_by(len) src, 
size_t len)
 {
     switch (ctx->type) {
     case MD5:     av_md5_update(ctx->ctx, src, len); break;
@@ -192,26 +193,28 @@ void av_hash_update(AVHashContext *ctx, const uint8_t 
*src, size_t len)
 
 void av_hash_final(AVHashContext *ctx, uint8_t *dst)
 {
+    uint8_t *out = FF_FORGE_BIDI(uint8_t *, dst, av_hash_get_size(ctx));
+
     switch (ctx->type) {
-    case MD5:     av_md5_final(ctx->ctx, dst); break;
-    case MURMUR3: av_murmur3_final(ctx->ctx, dst); break;
+    case MD5:     av_md5_final(ctx->ctx, out); break;
+    case MURMUR3: av_murmur3_final(ctx->ctx, out); break;
     case RIPEMD128:
     case RIPEMD160:
     case RIPEMD256:
-    case RIPEMD320: av_ripemd_final(ctx->ctx, dst); break;
+    case RIPEMD320: av_ripemd_final(ctx->ctx, out); break;
     case SHA160:
     case SHA224:
-    case SHA256:  av_sha_final(ctx->ctx, dst); break;
+    case SHA256:  av_sha_final(ctx->ctx, out); break;
     case SHA512_224:
     case SHA512_256:
     case SHA384:
-    case SHA512:  av_sha512_final(ctx->ctx, dst); break;
-    case CRC32:   AV_WB32(dst, ctx->crc ^ UINT32_MAX); break;
-    case ADLER32: AV_WB32(dst, ctx->crc); break;
+    case SHA512:  av_sha512_final(ctx->ctx, out); break;
+    case CRC32:   AV_WB32(out, ctx->crc ^ UINT32_MAX); break;
+    case ADLER32: AV_WB32(out, ctx->crc); break;
     }
 }
 
-void av_hash_final_bin(struct AVHashContext *ctx, uint8_t *dst, int size)
+void av_hash_final_bin(struct AVHashContext *ctx, uint8_t *av_counted_by(size) 
dst, int size)
 {
     uint8_t buf[AV_HASH_MAX_SIZE];
     unsigned rsize = av_hash_get_size(ctx);
@@ -222,7 +225,7 @@ void av_hash_final_bin(struct AVHashContext *ctx, uint8_t 
*dst, int size)
         memset(dst + rsize, 0, size - rsize);
 }
 
-void av_hash_final_hex(struct AVHashContext *ctx, uint8_t *dst, int size)
+void av_hash_final_hex(struct AVHashContext *ctx, uint8_t *av_counted_by(size) 
dst, int size)
 {
     uint8_t buf[AV_HASH_MAX_SIZE];
     unsigned rsize = av_hash_get_size(ctx), i;
@@ -232,7 +235,7 @@ void av_hash_final_hex(struct AVHashContext *ctx, uint8_t 
*dst, int size)
         snprintf(dst + i * 2, size - i * 2, "%02x", buf[i]);
 }
 
-void av_hash_final_b64(struct AVHashContext *ctx, uint8_t *dst, int size)
+void av_hash_final_b64(struct AVHashContext *ctx, uint8_t *av_counted_by(size) 
dst, int size)
 {
     uint8_t buf[AV_HASH_MAX_SIZE], b64[AV_BASE64_SIZE(AV_HASH_MAX_SIZE)];
     unsigned rsize = av_hash_get_size(ctx), osize;
diff --git a/libavutil/hash.h b/libavutil/hash.h
index 94151ded7cb..4578d84e29b 100644
--- a/libavutil/hash.h
+++ b/libavutil/hash.h
@@ -30,6 +30,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "attributes.h"
+
 /**
  * @defgroup lavu_hash Hash Functions
  * @ingroup lavu_crypto
@@ -180,7 +182,7 @@ void av_hash_init(struct AVHashContext *ctx);
  * @param[in]     src Data to be added to the hash context
  * @param[in]     len Size of the additional data
  */
-void av_hash_update(struct AVHashContext *ctx, const uint8_t *src, size_t len);
+void av_hash_update(struct AVHashContext *ctx, const uint8_t 
*av_counted_by(len) src, size_t len);
 
 /**
  * Finalize a hash context and compute the actual hash value.
@@ -211,7 +213,7 @@ void av_hash_final(struct AVHashContext *ctx, uint8_t *dst);
  * @param[out]    dst  Where the final hash value will be stored
  * @param[in]     size Number of bytes to write to `dst`
  */
-void av_hash_final_bin(struct AVHashContext *ctx, uint8_t *dst, int size);
+void av_hash_final_bin(struct AVHashContext *ctx, uint8_t *av_counted_by(size) 
dst, int size);
 
 /**
  * Finalize a hash context and store the hexadecimal representation of the
@@ -229,7 +231,7 @@ void av_hash_final_bin(struct AVHashContext *ctx, uint8_t 
*dst, int size);
  * @param[out]    dst  Where the string will be stored
  * @param[in]     size Maximum number of bytes to write to `dst`
  */
-void av_hash_final_hex(struct AVHashContext *ctx, uint8_t *dst, int size);
+void av_hash_final_hex(struct AVHashContext *ctx, uint8_t *av_counted_by(size) 
dst, int size);
 
 /**
  * Finalize a hash context and store the Base64 representation of the
@@ -247,7 +249,7 @@ void av_hash_final_hex(struct AVHashContext *ctx, uint8_t 
*dst, int size);
  * @param[out]    dst  Where the final hash value will be stored
  * @param[in]     size Maximum number of bytes to write to `dst`
  */
-void av_hash_final_b64(struct AVHashContext *ctx, uint8_t *dst, int size);
+void av_hash_final_b64(struct AVHashContext *ctx, uint8_t *av_counted_by(size) 
dst, int size);
 
 /**
  * Free hash context and set hash context pointer to `NULL`.
-- 
2.54.0

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

Reply via email to