Skip to content

Fix unsigned wrap in bzdecompress() output realloc at source_len UINT_MAX#22432

Open
iliaal wants to merge 1 commit into
php:masterfrom
iliaal:bz2-decompress-uintmax-wrap
Open

Fix unsigned wrap in bzdecompress() output realloc at source_len UINT_MAX#22432
iliaal wants to merge 1 commit into
php:masterfrom
iliaal:bz2-decompress-uintmax-wrap

Conversation

@iliaal

@iliaal iliaal commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

The input guard rejects only source_len > UINT_MAX, so source_len == UINT_MAX is permitted and assigned to bzs.avail_out (unsigned int). The per-iteration realloc then computed bzs.avail_out+1 in unsigned int arithmetic, which wraps to 0 at UINT_MAX, allocating no headroom while bz2 still believes avail_out bytes are available at next_out, so the next decompress round writes past the buffer. Compute the term as (size_t)bzs.avail_out + 1 so the increment is done in size_t and cannot wrap, matching the (size_t) casts already on the same call. Triggering needs a ~4GB crafted input, so there is no portable red/green test. Follow-up to the input guard added in #22242.

…_MAX

The input guard rejects only source_len > UINT_MAX, so source_len ==
UINT_MAX is permitted and assigned to bzs.avail_out (unsigned int). The
per-iteration realloc then computed bzs.avail_out+1 in unsigned int
arithmetic, which wraps to 0 at UINT_MAX, allocating no headroom while
bz2 still believes avail_out bytes are available at next_out: the next
decompress round writes past the buffer. Compute the term as
(size_t)bzs.avail_out + 1 so the increment is done in size_t and cannot
wrap, matching the (size_t) casts already used on the same call.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant