Skip to content

[3.15] gh-156002: Bound zipfile decompression for bzip2/LZMA/Zstandard (GH-156003) - #156362

Open
encukou wants to merge 1 commit into
python:3.15from
encukou:backport-f897dbf-3.15
Open

[3.15] gh-156002: Bound zipfile decompression for bzip2/LZMA/Zstandard (GH-156003)#156362
encukou wants to merge 1 commit into
python:3.15from
encukou:backport-f897dbf-3.15

Conversation

@encukou

@encukou encukou commented Aug 25, 2026

Copy link
Copy Markdown
Member

Patch by @tonghuaroot.

zipfile.ZipExtFile._read1() bounds the output of each decompress() call for DEFLATE members by passing a max_length to zlib, but for bzip2, LZMA, and Zstandard members it called decompress() with no bound. A whole compressed chunk was therefore expanded into a single allocation before the data[:self._left] clip ran, so a consumer that deliberately reads in small chunks to limit memory (for example zf.open(name).read(8192)) was silently unprotected for non-DEFLATE members. A small, spec-conformant archive member declaring a large uncompressed size could drive multi-GB peak memory.

_read1() now passes a per-call bound to the non-DEFLATE decompress() (mirroring the DEFLATE branch) and drains the decompressor's internal buffer across calls by checking needs_input before reading more compressed input. zipfile's LZMADecompressor wrapper forwards max_length and exposes needs_input so the bound also holds for LZMA members.

(cherry picked from commit f897dbf)

…tandard (pythonGH-156003)

Patch by @tonghuaroot.

zipfile.ZipExtFile._read1() bounds the output of each decompress() call
for DEFLATE members by passing a max_length to zlib, but for bzip2, LZMA,
and Zstandard members it called decompress() with no bound. A whole
compressed chunk was therefore expanded into a single allocation before
the data[:self._left] clip ran, so a consumer that deliberately reads in
small chunks to limit memory (for example zf.open(name).read(8192)) was
silently unprotected for non-DEFLATE members. A small, spec-conformant
archive member declaring a large uncompressed size could drive multi-GB
peak memory.

_read1() now passes a per-call bound to the non-DEFLATE decompress()
(mirroring the DEFLATE branch) and drains the decompressor's internal
buffer across calls by checking needs_input before reading more
compressed input. zipfile's LZMADecompressor wrapper forwards max_length
and exposes needs_input so the bound also holds for LZMA members.

Co-authored-by: tonghuaroot <tonghuaroot@gmail.com>
(cherry picked from commit f897dbf)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting core review needs backport to 3.10 only security fixes needs backport to 3.11 only security fixes needs backport to 3.12 only security fixes needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant