[PATCH] bzlib: don't leak compressor state when abandoning after an I/O error
Mark Wielaard
mark@klomp.org
Sat Jul 11 22:04:13 GMT 2026
Hi Naveed,
On Mon, Jul 06, 2026 at 02:56:41PM +0530, Naveed Khan via Bzip2-devel wrote:
> BZ2_bzWriteClose64() returns early at its entry ferror() check with
> BZ_IO_ERROR before releasing anything, so the EState and its
> arr1/arr2/ftab buffers (about 7.5 MB for a blockSize100k of 9), plus
> the bzFile itself, are never freed.
>
> BZ2_bzclose() is written to cope with a failing close: when the first,
> normal close reports an error it re-invokes BZ2_bzWriteClose() with
> abandon=1 to force the resources to be torn down. But that retry hits
> the very same ferror() early-return and again frees nothing,
Right. This is only for ferror issues. "Normal" bzFile lastErr issues
don't inhibit the abandon logic.
> so any
> write or flush error (a broken pipe, a full filesystem, ...) leaks the
> whole compressor state on every close. In a long-running process that
> uses libbz2 and can be driven to hit output errors this is an
> unbounded, attacker-influenced leak.
I found it hard to trigger, but indeed if there are i/o errors this
does inhibit the abandon logic and does leak the stream buffers.
> Skip the ferror() early-return when abandon is set so the abandon path
> proceeds to BZ2_bzCompressEnd()+free() as intended. The normal
> (abandon==0) close is unchanged, so the "retry with abandon=1" contract
> BZ2_bzclose relies on still frees exactly once and introduces no double
> free.
This looks like the correct way to handle abandon (and follows the
logic in the rest of the function).
> Signed-off-by: Naveed Khan <naveed@digiscrypt.com>
> ---
> diff --git a/bzlib.c b/bzlib.c
> index 100873c..a444d84 100644
> --- a/bzlib.c
> +++ b/bzlib.c
> @@ -1034,7 +1034,10 @@ void BZ_API(BZ2_bzWriteClose64)
> { BZ_SETERR(BZ_OK); return; };
> if (!(bzf->writing))
> { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
> - if (ferror(bzf->handle))
> + /* When abandoning we must still release the compressor state, even
> + if the handle has errored; otherwise the caller (e.g. BZ2_bzclose,
> + which retries with abandon=1 to force teardown) leaks it. */
> + if (!abandon && ferror(bzf->handle))
> { BZ_SETERR(BZ_IO_ERROR); return; };
>
> if (nbytes_in_lo32 != NULL) *nbytes_in_lo32 = 0;
Puhsed as https://sourceware.org/cgit/bzip2/commit/?id=7bfa6b4c2fff
Thanks,
Mark
More information about the Bzip2-devel
mailing list