From b4dbec562f0d7e075f26bfa301a5cd8475875fa6 Mon Sep 17 00:00:00 2001 From: Arman Hasanzadeh Date: Wed, 17 Sep 2025 15:31:57 -0700 Subject: [PATCH] Add fbounds-safety annotations in `quant_levels_dec_utils.c/.h`. Reasoning: Image Data Buffers: The `data` parameter of `WebPDequantizeLevels` (in both .c and .h) and `InitParams` (src/utils/quant_levels_dec_utils.c:232) is annotated with `WEBP_SIZED_BY((long)stride * height)`, as it points to the start of the image buffer. The `src` and `dst` fields in `SmoothParams` (src/utils/quant_levels_dec_utils.c:54) are annotated as `WEBP_INDEXABLE`. They are initialized from `data` in `InitParams` (L266) and are advanced row by row using pointer arithmetic (e.g., `p->src += p->stride` in `VFilter` L111, `p->dst += p->stride` in `ApplyFilter` L165). `WEBP_INDEXABLE` is used because the pointers iterate within the buffer and are only accessed with positive indices. Scratch Buffers (`SmoothParams`): Scratch buffers are allocated in `InitParams` via `WebPSafeMalloc`. The local variable `mem` holding this allocation (L245) is explicitly annotated as `WEBP_BIDI_INDEXABLE` to ensure safety when compiling with error suppression. - `start`, `cur`, `top`: These pointers are used for iteration and pointer arithmetic within the circular scratch buffer. They are annotated as `WEBP_INDEXABLE`. - `end`: This pointer is annotated as `WEBP_BIDI_INDEXABLE` because it is used in subtraction (`p->end - width`) in `InitParams` (L257) to calculate `p->top`. - `average`: This buffer is accessed sequentially up to `width`. It is annotated as `WEBP_COUNTED_BY(width)`. Initialization in `InitParams` is reordered (L261) to ensure `p->width` is set before `p->average`. - `correction`: This lookup table requires negative indexing. To avoid using `WEBP_BIDI_INDEXABLE` in the struct, it is annotated as `WEBP_COUNTED_BY_OR_NULL(CORRECTION_LUT_SIZE)` (L75), pointing to the start of the buffer. `CORRECTION_LUT_SIZE` is defined (L33). `InitCorrectionLUT` (L188) and `ApplyFilter` (L147) calculate a local middle pointer which is explicitly annotated as `WEBP_BIDI_INDEXABLE` to allow safe negative indexing. Local Pointers: To ensure safety when compiling with error suppression (where locals default to unsafe), explicit annotations are added to local pointers derived from safe struct members: - `VFilter` (L87): `src`, `cur`, `top`, `out` are `WEBP_INDEXABLE`. - `HFilter` (L121): `in`, `out` are `WEBP_INDEXABLE`. - `ApplyFilter` (L145): `average`, `dst` are `WEBP_INDEXABLE`. - `CountLevels` (L214): `data` is `WEBP_INDEXABLE`. Bug: 432511821 Change-Id: I6bdf86f80c94a5b182c5aef7e4092fe4ea24afb8 --- src/utils/quant_levels_dec_utils.c | 74 +++++++++++++++++------------- src/utils/quant_levels_dec_utils.h | 5 +- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/src/utils/quant_levels_dec_utils.c b/src/utils/quant_levels_dec_utils.c index 0dc53625..f7b5c57a 100644 --- a/src/utils/quant_levels_dec_utils.c +++ b/src/utils/quant_levels_dec_utils.c @@ -29,6 +29,7 @@ WEBP_ASSUME_UNSAFE_INDEXABLE_ABI #define FIX 16 // fix-point precision for averaging #define LFIX 2 // extra precision for look-up table #define LUT_SIZE ((1 << (8 + LFIX)) - 1) // look-up table size +#define CORRECTION_LUT_SIZE (1 + 2 * LUT_SIZE) #if defined(USE_DITHERING) @@ -46,11 +47,11 @@ static const uint8_t kOrderedDither[DSIZE][DSIZE] = { #endif typedef struct { - int width, height; // dimension - int stride; // stride in bytes - int row; // current input row being processed - uint8_t* src; // input pointer - uint8_t* dst; // output pointer + int width, height; // dimension + int stride; // stride in bytes + int row; // current input row being processed + uint8_t* WEBP_INDEXABLE src; // input pointer + uint8_t* WEBP_INDEXABLE dst; // output pointer int radius; // filter radius (=delay) int scale; // normalization factor, in FIX bits precision @@ -58,18 +59,19 @@ typedef struct { void* mem; // all memory // various scratch buffers - uint16_t* start; - uint16_t* cur; - uint16_t* end; - uint16_t* top; - uint16_t* average; + uint16_t* WEBP_INDEXABLE start; + uint16_t* WEBP_INDEXABLE cur; + uint16_t* WEBP_BIDI_INDEXABLE end; + uint16_t* WEBP_INDEXABLE top; + uint16_t* WEBP_COUNTED_BY(width) average; // input levels distribution int num_levels; // number of quantized levels int min, max; // min and max level values int min_level_dist; // smallest distance between two consecutive levels - int16_t* correction; // size = 1 + 2*LUT_SIZE -> ~4k memory + // size = 1 + 2*LUT_SIZE -> ~4k memory + int16_t* WEBP_COUNTED_BY_OR_NULL(CORRECTION_LUT_SIZE) correction; } SmoothParams; //------------------------------------------------------------------------------ @@ -82,11 +84,11 @@ static WEBP_INLINE uint8_t clip_8b(int v) { // vertical accumulation static void VFilter(SmoothParams* const p) { - const uint8_t* src = p->src; + const uint8_t* WEBP_INDEXABLE src = p->src; const int w = p->width; - uint16_t* const cur = p->cur; - const uint16_t* const top = p->top; - uint16_t* const out = p->end; + uint16_t* const WEBP_INDEXABLE cur = p->cur; + const uint16_t* const WEBP_INDEXABLE top = p->top; + uint16_t* const WEBP_INDEXABLE out = p->end; uint16_t sum = 0; // all arithmetic is modulo 16bit int x; @@ -111,8 +113,8 @@ static void VFilter(SmoothParams* const p) { // horizontal accumulation. We use mirror replication of missing pixels, as it's // a little easier to implement (surprisingly). static void HFilter(SmoothParams* const p) { - const uint16_t* const in = p->end; - uint16_t* const out = p->average; + const uint16_t* const WEBP_INDEXABLE in = p->end; + uint16_t* const WEBP_INDEXABLE out = p->average; const uint32_t scale = p->scale; const int w = p->width; const int r = p->radius; @@ -135,13 +137,16 @@ static void HFilter(SmoothParams* const p) { // emit one filtered output row static void ApplyFilter(SmoothParams* const p) { - const uint16_t* const average = p->average; + const uint16_t* const WEBP_INDEXABLE average = p->average; const int w = p->width; - const int16_t* const correction = p->correction; + // correction is WEBP_COUNTED_BY, pointing to the start of the LUT. + // We need the middle pointer for negative indexing. + const int16_t* const WEBP_BIDI_INDEXABLE correction = + p->correction + LUT_SIZE; #if defined(USE_DITHERING) const uint8_t* const dither = kOrderedDither[p->row % DSIZE]; #endif - uint8_t* const dst = p->dst; + uint8_t* const WEBP_INDEXABLE dst = p->dst; int x; for (x = 0; x < w; ++x) { const int v = dst[x]; @@ -160,7 +165,8 @@ static void ApplyFilter(SmoothParams* const p) { //------------------------------------------------------------------------------ // Initialize correction table -static void InitCorrectionLUT(int16_t* const lut, int min_dist) { +static void InitCorrectionLUT( + int16_t* const WEBP_COUNTED_BY(CORRECTION_LUT_SIZE) lut_ptr, int min_dist) { // The correction curve is: // f(x) = x for x <= threshold2 // f(x) = 0 for x >= threshold1 @@ -171,6 +177,9 @@ static void InitCorrectionLUT(int16_t* const lut, int min_dist) { const int threshold2 = (3 * threshold1) >> 2; const int max_threshold = threshold2 << DFIX; const int delta = threshold1 - threshold2; + // lut_ptr is WEBP_COUNTED_BY, pointing to the start of the LUT. + // We need the middle pointer (lut) for negative indexing. + int16_t* const WEBP_BIDI_INDEXABLE lut = lut_ptr + LUT_SIZE; int i; for (i = 1; i <= LUT_SIZE; ++i) { int c = (i <= threshold2) ? (i << DFIX) @@ -186,7 +195,7 @@ static void InitCorrectionLUT(int16_t* const lut, int min_dist) { static void CountLevels(SmoothParams* const p) { int i, j, last_level; uint8_t used_levels[256] = {0}; - const uint8_t* data = p->src; + const uint8_t* WEBP_INDEXABLE data = p->src; p->min = 255; p->max = 0; for (j = 0; j < p->height; ++j) { @@ -216,15 +225,16 @@ static void CountLevels(SmoothParams* const p) { } // Initialize all params. -static int InitParams(uint8_t* const data, int width, int height, int stride, - int radius, SmoothParams* const p) { +static int InitParams(uint8_t* WEBP_SIZED_BY((size_t)stride* height) const data, + int width, int height, int stride, int radius, + SmoothParams* const p) { const int R = 2 * radius + 1; // total size of the kernel const size_t size_scratch_m = (R + 1) * width * sizeof(*p->start); const size_t size_m = width * sizeof(*p->average); - const size_t size_lut = (1 + 2 * LUT_SIZE) * sizeof(*p->correction); + const size_t size_lut = CORRECTION_LUT_SIZE * sizeof(*p->correction); const size_t total_size = size_scratch_m + size_m + size_lut; - uint8_t* mem = (uint8_t*)WebPSafeMalloc(1U, total_size); + uint8_t* WEBP_BIDI_INDEXABLE mem = (uint8_t*)WebPSafeMalloc(1U, total_size); if (mem == NULL) return 0; p->mem = (void*)mem; @@ -236,10 +246,10 @@ static int InitParams(uint8_t* const data, int width, int height, int stride, WEBP_UNSAFE_MEMSET(p->top, 0, width * sizeof(*p->top)); mem += size_scratch_m; + p->width = width; p->average = (uint16_t*)mem; mem += size_m; - p->width = width; p->height = height; p->stride = stride; p->src = data; @@ -251,8 +261,9 @@ static int InitParams(uint8_t* const data, int width, int height, int stride, // analyze the input distribution so we can best-fit the threshold CountLevels(p); - // correction table - p->correction = ((int16_t*)mem) + LUT_SIZE; + // correction table. p->correction is WEBP_COUNTED_BY(CORRECTION_LUT_SIZE). + // It points to the start of the buffer. + p->correction = ((int16_t*)mem); InitCorrectionLUT(p->correction, p->min_level_dist); return 1; @@ -260,8 +271,9 @@ static int InitParams(uint8_t* const data, int width, int height, int stride, static void CleanupParams(SmoothParams* const p) { WebPSafeFree(p->mem); } -int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride, - int strength) { +int WebPDequantizeLevels(uint8_t* WEBP_SIZED_BY((size_t)stride* height) + const data, + int width, int height, int stride, int strength) { int radius = 4 * strength / 100; if (strength < 0 || strength > 100) return 0; diff --git a/src/utils/quant_levels_dec_utils.h b/src/utils/quant_levels_dec_utils.h index 3dcf4db6..0944a36c 100644 --- a/src/utils/quant_levels_dec_utils.h +++ b/src/utils/quant_levels_dec_utils.h @@ -28,8 +28,9 @@ extern "C" { // Strength is in [0..100] and controls the amount of dithering applied. // Returns false in case of error (data is NULL, invalid parameters, // malloc failure, ...). -int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride, - int strength); +int WebPDequantizeLevels(uint8_t* WEBP_SIZED_BY((size_t)stride* height) + const data, + int width, int height, int stride, int strength); #ifdef __cplusplus } // extern "C"