From 0e41a607d51dd62c0bbb50e6167efd5b41191532 Mon Sep 17 00:00:00 2001 From: Jeff <88594453+LaptopsPlural@users.noreply.github.com> Date: Fri, 11 Sep 2026 16:40:22 +0000 Subject: [PATCH] stream: add optional -fbounds-safety annotation for body_data Introduce inert OGG_COUNTED_BY*_ macros and annotate ogg_stream_state.body_data with OGG_COUNTED_BY_OR_NULL(body_storage). Keep capacity-then-pointer assignment and pointer-null-then-size-0 clear order so a future -fbounds-safety build can enforce the existing size relationship. Default builds are unchanged (macros empty; OGG_ENABLE_FBOUNDS_SAFETY=OFF). --- CMakeLists.txt | 11 ++++++++++ include/ogg/Makefile.am | 2 +- include/ogg/ogg.h | 8 ++++++- include/ogg/ogg_bounds_safety.h | 39 +++++++++++++++++++++++++++++++++ src/framing.c | 5 +++++ 5 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 include/ogg/ogg_bounds_safety.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ee17ba7..4179126f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,16 @@ include(CMakePackageConfigHelpers) # Build options option(BUILD_SHARED_LIBS "Build shared library" OFF) + +# Optional Clang -fbounds-safety. Default OFF: OGG_*COUNTED_BY* macros in +# ogg_bounds_safety.h are inert and the ABI/build is unchanged. When ON, +# requires a Clang that provides -fbounds-safety / . +option(OGG_ENABLE_FBOUNDS_SAFETY + "Enable Clang -fbounds-safety annotations (experimental toolchain)" OFF) +if(OGG_ENABLE_FBOUNDS_SAFETY) + add_definitions(-DOGG_SUPPORT_FBOUNDS_SAFETY) + add_compile_options(-fbounds-safety) +endif() if(APPLE) option(BUILD_FRAMEWORK "Build Framework bundle for OSX" OFF) endif() @@ -69,6 +79,7 @@ configure_file(include/ogg/config_types.h.in include/ogg/config_types.h @ONLY) set(OGG_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/include/ogg/config_types.h include/ogg/ogg.h + include/ogg/ogg_bounds_safety.h include/ogg/os_types.h ) diff --git a/include/ogg/Makefile.am b/include/ogg/Makefile.am index 142699d3..da8faf48 100644 --- a/include/ogg/Makefile.am +++ b/include/ogg/Makefile.am @@ -2,5 +2,5 @@ oggincludedir = $(includedir)/ogg -ogginclude_HEADERS = ogg.h os_types.h +ogginclude_HEADERS = ogg.h ogg_bounds_safety.h os_types.h nodist_ogginclude_HEADERS = config_types.h diff --git a/include/ogg/ogg.h b/include/ogg/ogg.h index c4325aa7..136a51ad 100644 --- a/include/ogg/ogg.h +++ b/include/ogg/ogg.h @@ -22,6 +22,7 @@ extern "C" { #include #include +#include typedef struct { void *iov_base; @@ -50,7 +51,12 @@ typedef struct { Ogg bitstream **********************************************************/ typedef struct { - unsigned char *body_data; /* bytes from packet bodies */ + /* body_storage is the capacity companion for body_data. + * Field order is preserved (pointer before size) for public ABI; + * update sites assign capacity before the pointer so counted-by + * invariants hold under optional -fbounds-safety builds. + */ + unsigned char * OGG_COUNTED_BY_OR_NULL(body_storage) body_data; /* bytes from packet bodies */ long body_storage; /* storage elements allocated */ long body_fill; /* elements stored; fill mark */ long body_returned; /* elements of fill returned */ diff --git a/include/ogg/ogg_bounds_safety.h b/include/ogg/ogg_bounds_safety.h new file mode 100644 index 00000000..75d1b2c1 --- /dev/null +++ b/include/ogg/ogg_bounds_safety.h @@ -0,0 +1,39 @@ +/* ogg_bounds_safety.h - portability macros for optional -fbounds-safety + * + * Copyright (c) 2026 Jeff Bindel + * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. + * + * When OGG_SUPPORT_FBOUNDS_SAFETY is defined (typically via + * -DOGG_SUPPORT_FBOUNDS_SAFETY and a Clang toolchain that implements + * -fbounds-safety), these macros expand to Clang bounds annotations. + * Otherwise they expand to nothing so default builds are unchanged. + * + * Pattern matches libwebp / libpng / giflib inert-macro + * -fbounds-safety adoption: annotations are inert unless explicitly enabled. + */ + +#ifndef OGG_BOUNDS_SAFETY_H +#define OGG_BOUNDS_SAFETY_H + +#ifdef OGG_SUPPORT_FBOUNDS_SAFETY + +# include +/* Non-ABI-breaking counted-by annotations for struct pointer members. + * Prefer OGG_COUNTED_BY_OR_NULL for pointers that may be NULL while the + * companion size field is zero (ogg_stream_state.body_data pattern). + */ +# define OGG_COUNTED_BY(n) __counted_by(n) +# define OGG_COUNTED_BY_OR_NULL(n) __counted_by_or_null(n) + +#else /* !OGG_SUPPORT_FBOUNDS_SAFETY */ + +# define OGG_COUNTED_BY(n) +# define OGG_COUNTED_BY_OR_NULL(n) + +#endif /* OGG_SUPPORT_FBOUNDS_SAFETY */ + +#endif /* OGG_BOUNDS_SAFETY_H */ diff --git a/src/framing.c b/src/framing.c index 4034eb53..f36abe93 100644 --- a/src/framing.c +++ b/src/framing.c @@ -133,6 +133,7 @@ static void _ogg_crc_init(){ int ogg_stream_init(ogg_stream_state *os,int serialno){ if(os){ memset(os,0,sizeof(*os)); + /* counted_by: set capacity before pointer */ os->body_storage=16*1024; os->lacing_storage=1024; @@ -162,6 +163,9 @@ int ogg_stream_check(ogg_stream_state *os){ int ogg_stream_clear(ogg_stream_state *os){ if(os){ if(os->body_data)_ogg_free(os->body_data); + /* counted_by clear order: drop pointer then capacity */ + os->body_data=NULL; + os->body_storage=0; if(os->lacing_vals)_ogg_free(os->lacing_vals); if(os->granule_vals)_ogg_free(os->granule_vals); @@ -196,6 +200,7 @@ static int _os_body_expand(ogg_stream_state *os,long needed){ ogg_stream_clear(os); return -1; } + /* counted_by: set capacity before pointer */ os->body_storage=body_storage; os->body_data=ret; }