[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