From e753135181fffc9ccd56d0b134bc0dcc1aa3aa5a Mon Sep 17 00:00:00 2001 From: Jeff <88594453+LaptopsPlural@users.noreply.github.com> Date: Sun, 13 Sep 2026 01:22:41 +0000 Subject: [PATCH] inflate: annotate sliding window with sized_by_or_null Tie inflate_state.window to its capacity (wsize) with a native __attribute__((__sized_by_or_null__(wsize))) (or counted_by_or_null fallback) guarded by __has_attribute so unsupported compilers see an ordinary pointer. Keep capacity-before-pointer updates in inflate.c so the relationship stays consistent across allocate, reset, and free. Default builds are unchanged: no new headers, configure options, or CMake toggles. --- inflate.c | 27 +++++++++++++++++++-------- inflate.h | 11 ++++++++++- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/inflate.c b/inflate.c index 5f5d4922b7..ee39fdc9bc 100644 --- a/inflate.c +++ b/inflate.c @@ -127,7 +127,11 @@ int ZEXPORT inflateReset(z_streamp strm) { if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; - state->wsize = 0; + /* Keep wsize when the sliding window allocation is retained so the + window/wsize relationship remains consistent across reset. Clear + wsize only when there is no window. Indices always restart at zero. */ + if (state->window == Z_NULL) + state->wsize = 0; state->whave = 0; state->wnext = 0; return inflateResetKeep(strm); @@ -162,6 +166,7 @@ int ZEXPORT inflateReset2(z_streamp strm, int windowBits) { if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { ZFREE(strm, state->window); state->window = Z_NULL; + state->wsize = 0; } /* update state and reset the rest of it */ @@ -255,16 +260,22 @@ local int updatewindow(z_streamp strm, const Bytef *end, unsigned copy) { state = (struct inflate_state FAR *)strm->state; - /* if it hasn't been done already, allocate space for the window */ + /* if it hasn't been done already, allocate space for the window. + Set capacity (wsize) before binding the pointer so the window/wsize + relationship stays consistent for bounds annotations. */ if (state->window == Z_NULL) { + state->wsize = 1U << state->wbits; + state->wnext = 0; + state->whave = 0; state->window = (unsigned char FAR *) - ZALLOC(strm, 1U << state->wbits, - sizeof(unsigned char)); - if (state->window == Z_NULL) return 1; + ZALLOC(strm, state->wsize, sizeof(unsigned char)); + if (state->window == Z_NULL) { + state->wsize = 0; + return 1; + } } - - /* if window not in use yet, initialize */ - if (state->wsize == 0) { + else if (state->wsize == 0) { + /* Defensive: allocation present but capacity cleared — restore. */ state->wsize = 1U << state->wbits; state->wnext = 0; state->whave = 0; diff --git a/inflate.h b/inflate.h index f758e0dcc1..d63806a838 100644 --- a/inflate.h +++ b/inflate.h @@ -97,7 +97,16 @@ struct inflate_state { unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ unsigned wnext; /* window write index */ - unsigned char FAR *window; /* allocated sliding window, if needed */ + /* window <-> wsize: NULL iff wsize == 0. Attribute is inert when the + compiler does not support it (default builds unchanged). */ +#if defined(__has_attribute) && __has_attribute(__sized_by_or_null__) + unsigned char FAR *window __attribute__((__sized_by_or_null__(wsize))); +#elif defined(__has_attribute) && __has_attribute(__counted_by_or_null__) + unsigned char FAR *window __attribute__((__counted_by_or_null__(wsize))); +#else + unsigned char FAR *window; +#endif + /* allocated sliding window, if needed */ /* bit accumulator */ unsigned long hold; /* input bit accumulator */ unsigned bits; /* number of bits in hold */