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
This commit is contained in:
Arman Hasanzadeh
2025-09-17 15:31:57 -07:00
parent 3779daa97f
commit b4dbec562f
2 changed files with 46 additions and 33 deletions

View File

@@ -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;

View File

@@ -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"