GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-156002: Bound zipfile decompression for bzip2/LZMA/Zstandard by encukou · Pull Request #156003 · python/cpython

Conversation

@encukou

@encukou encukou commented Aug 18, 2026 •
edited by bedevere-app Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Patch by @tonghuaroot

  • Issue: Memory exhaustion via crafted zip file #156002

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

tonghuaroot reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
tonghuaroot and others added 6 commits June 23, 2026 09:34
@tonghuaroot
GHSA-p384-rgv5-vhc2: Bound zipfile decompression for bzip2/LZMA/Zstan…
9097fc1
…dard

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.
@tonghuaroot
GHSA-p384-rgv5-vhc2: Address review: private _needs_input and reword …
d61a324
…bound comment
@tonghuaroot
GHSA-p384-rgv5-vhc2: Rewrite the bound test to call _read1 directly
660522b
Replace the Linux-only subprocess RSS test with a cross-platform check
that _read1() output is bounded by MIN_READ_SIZE for bzip2/LZMA/Zstandard.
@tonghuaroot
GHSA-p384-rgv5-vhc2: Add ZIP_STORED/ZIP_DEFLATED bound tests; trim co…
6135ee6
…mment
@encukou
Merge in the main branch
2335538
@encukou
Add a blurb
da69f5d
@bedevere-app bedevere-app Bot mentioned this pull request Aug 18, 2026
Memory exhaustion via crafted zip file #156002
Closed
@bedevere-app bedevere-app Bot added the awaiting core review label Aug 18, 2026
@encukou
Use previous blurb
5f474f2
StanFromIreland
StanFromIreland reviewed Aug 19, 2026
View reviewed changes
Comment thread Lib/zipfile/__init__.py


def _decompressor_needs_input(decompressor):
# bz2/zstd expose the stdlib decompressor's public needs_input; the LZMA

@StanFromIreland StanFromIreland Aug 19, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LZMA wrapper is private, it's not in __all__ or documented, why not just make it a "public" property and avoid this little dance?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@encukou encukou Aug 19, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's one definition of “private” :)

For the backports, I think it's best to be extra careful. Testing on 3.14.7 and having things break with 3.14.6 is not fun.

Let's make it public (& more maintainable) in 3.16 afterwards.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
StanFromIreland
StanFromIreland reviewed Aug 20, 2026
View reviewed changes
Comment thread Misc/NEWS.d/next/Security/2026-08-18-13-54-05.gh-issue-156002.CcWXPP.rst

@StanFromIreland StanFromIreland Aug 20, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be in Security.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

encukou reacted with heart emoji
All reactions
  • ❤️ 1 reaction
@tonghuaroot

tonghuaroot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Agreed — this bounds a decompression-bomb DoS, so Security is the right blurb category. Thanks for landing it!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou
Move blurb to Security
dc8e61b
@encukou
encukou merged commit f897dbf into python:main Aug 24, 2026
54 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot removed the awaiting core review label Aug 24, 2026
@encukou
encukou deleted the fix-zipfile-decompress-maxlength branch August 24, 2026 11:34
@encukou encukou added needs backport to 3.10 needs backport to 3.11 only security fixes needs backport to 3.12 only security fixes needs backport to 3.13 only security fixes needs backport to 3.14 bugs and security fixes needs backport to 3.15 bugs and security fixes labels Aug 24, 2026
@miss-islington-app

miss-islington-app Bot commented Aug 24, 2026

Copy link
Copy Markdown

Thanks @encukou for the PR 🌮🎉.. I'm working now to backport this PR to: 3.11.
🐍🍒⛏🤖

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@miss-islington-app

miss-islington-app Bot commented Aug 24, 2026

Copy link
Copy Markdown

Thanks @encukou for the PR 🌮🎉.. I'm working now to backport this PR to: 3.10.
🐍🍒⛏🤖

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@miss-islington-app

miss-islington-app Bot commented Aug 24, 2026

Copy link
Copy Markdown

Thanks @encukou for the PR 🌮🎉.. I'm working now to backport this PR to: 3.13.
🐍🍒⛏🤖

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@miss-islington-app

miss-islington-app Bot commented Aug 24, 2026

Copy link
Copy Markdown

Thanks @encukou for the PR 🌮🎉.. I'm working now to backport this PR to: 3.12.
🐍🍒⛏🤖

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@miss-islington-app

miss-islington-app Bot commented Aug 24, 2026

Copy link
Copy Markdown

Thanks @encukou for the PR 🌮🎉.. I'm working now to backport this PR to: 3.14.
🐍🍒⛏🤖

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@miss-islington-app

miss-islington-app Bot commented Aug 24, 2026

Copy link
Copy Markdown

Thanks @encukou for the PR 🌮🎉.. I'm working now to backport this PR to: 3.15.
🐍🍒⛏🤖

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@miss-islington-app

miss-islington-app Bot commented Aug 24, 2026

Copy link
Copy Markdown

Sorry, @encukou, I could not cleanly backport this to 3.11 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker f897dbf2f36a5935700b7c2d94d4681d2136b7d4 3.11
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@miss-islington-app miss-islington-app Bot assigned encukou Aug 24, 2026
@miss-islington-app

miss-islington-app Bot commented Aug 24, 2026

Copy link
Copy Markdown

Sorry, @encukou, I could not cleanly backport this to 3.10 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker f897dbf2f36a5935700b7c2d94d4681d2136b7d4 3.10
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@miss-islington-app

miss-islington-app Bot commented Aug 24, 2026

Copy link
Copy Markdown

Sorry, @encukou, I could not cleanly backport this to 3.13 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker f897dbf2f36a5935700b7c2d94d4681d2136b7d4 3.13
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@miss-islington-app

miss-islington-app Bot commented Aug 24, 2026

Copy link
Copy Markdown

Sorry, @encukou, I could not cleanly backport this to 3.12 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker f897dbf2f36a5935700b7c2d94d4681d2136b7d4 3.12
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@miss-islington-app

miss-islington-app Bot commented Aug 24, 2026

Copy link
Copy Markdown

Sorry, @encukou, I could not cleanly backport this to 3.14 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker f897dbf2f36a5935700b7c2d94d4681d2136b7d4 3.14
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@miss-islington-app

miss-islington-app Bot commented Aug 24, 2026

Copy link
Copy Markdown

Sorry, @encukou, I could not cleanly backport this to 3.15 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker f897dbf2f36a5935700b7c2d94d4681d2136b7d4 3.15
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app

bedevere-app Bot commented Aug 25, 2026

Copy link
Copy Markdown

GH-156362 is a backport of this pull request to the 3.15 branch.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot removed the needs backport to 3.15 bugs and security fixes label Aug 25, 2026
clin1234 pushed a commit to clin1234/cpython that referenced this pull request Aug 27, 2026
@encukou @tonghuaroot @clin1234
pythongh-156002: Bound zipfile decompression for bzip2/LZMA/Zstandard (…
a4f7b54
…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 <[email protected]>
hugovk pushed a commit that referenced this pull request Aug 31, 2026
@encukou @tonghuaroot
[3.15] gh-156002: Bound zipfile decompression for bzip2/LZMA/Zstandard (
1b424c0
GH-156003) (#156362)

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)

Co-authored-by: tonghuaroot <[email protected]>
@encukou

encukou commented Sep 1, 2026

Copy link
Copy Markdown
Member Author

3.10-3.14 backport PRs were made from 3.15.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou encukou removed needs backport to 3.10 needs backport to 3.11 only security fixes needs backport to 3.12 only security fixes needs backport to 3.13 only security fixes needs backport to 3.14 bugs and security fixes labels Sep 1, 2026
encukou added a commit that referenced this pull request Sep 3, 2026
@miss-islington @encukou @tonghuaroot
[3.14] gh-156002: Bound zipfile decompression for bzip2/LZMA/Zstandard (
31980e8
GH-156003) (GH-156362) (GH-156737)

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

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)
(cherry picked from commit 1b424c0)

Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: tonghuaroot <[email protected]>
@rasmusfaber rasmusfaber mentioned this pull request Sep 8, 2026
gh-156002: Keep reading through third-party zipfile decompressors #157180
Merged
encukou added a commit that referenced this pull request Sep 10, 2026
@rasmusfaber @encukou
gh-156002: Keep reading through monkey-patched zipfile decompressors (G…
e2311cf
…H-157180)

GH-156003 made ZipExtFile._read1() call decompress(data, max_length) on
non-deflate decompressors and consult needs_input before reading more. A
decompressor installed by monkey-patching _get_decompressor() (as projects
like zipfile-zstd, zipfile-deflate64, ... do) may support neither, and every read
through it then failed with AttributeError.

- Make LZMADecompressor.needs_input public to simplify implementation.
- Make the needs_input attribute optional.
- If `decompress()` fails with TypeError, try again with one argument.
- Since the fallback to one-argument call is a maintenance burden, raise
  DeprecationWarning.
- Add tests for future changes, so we can make informed decisions
  about breaking monkey-patchers.

Co-authored-by: Petr Viktorin <[email protected]>
@encukou encukou mentioned this pull request Sep 10, 2026
[3.15] gh-156002: Keep reading through monkey-patched zipfile decompressors (GH-157180) #157268
Merged
clin1234 pushed a commit to clin1234/cpython that referenced this pull request Sep 12, 2026
@rasmusfaber @encukou @clin1234
pythongh-156002: Keep reading through monkey-patched zipfile decompre…
0f60973
…ssors (pythonGH-157180)

pythonGH-156003 made ZipExtFile._read1() call decompress(data, max_length) on
non-deflate decompressors and consult needs_input before reading more. A
decompressor installed by monkey-patching _get_decompressor() (as projects
like zipfile-zstd, zipfile-deflate64, ... do) may support neither, and every read
through it then failed with AttributeError.

- Make LZMADecompressor.needs_input public to simplify implementation.
- Make the needs_input attribute optional.
- If `decompress()` fails with TypeError, try again with one argument.
- Since the fallback to one-argument call is a maintenance burden, raise
  DeprecationWarning.
- Add tests for future changes, so we can make informed decisions
  about breaking monkey-patchers.

Co-authored-by: Petr Viktorin <[email protected]>
johnslavik pushed a commit to johnslavik/cpython that referenced this pull request Sep 14, 2026
@rasmusfaber @encukou @johnslavik
pythongh-156002: Keep reading through monkey-patched zipfile decompre…
c931f1a
…ssors (pythonGH-157180)

pythonGH-156003 made ZipExtFile._read1() call decompress(data, max_length) on
non-deflate decompressors and consult needs_input before reading more. A
decompressor installed by monkey-patching _get_decompressor() (as projects
like zipfile-zstd, zipfile-deflate64, ... do) may support neither, and every read
through it then failed with AttributeError.

- Make LZMADecompressor.needs_input public to simplify implementation.
- Make the needs_input attribute optional.
- If `decompress()` fails with TypeError, try again with one argument.
- Since the fallback to one-argument call is a maintenance burden, raise
  DeprecationWarning.
- Add tests for future changes, so we can make informed decisions
  about breaking monkey-patchers.

Co-authored-by: Petr Viktorin <[email protected]>
Yhg1s pushed a commit that referenced this pull request Sep 29, 2026
@miss-islington @encukou
@tonghuaroot @rasmusfaber
[3.13] gh-156002: Bound zipfile decompression for bzip2/LZMA (GH-156003…
dcdd406
…) (GH-156362) (#156738)

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

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)
(cherry picked from commit 1b424c0)

* gh-156002: Keep reading through monkey-patched zipfile decompressors (GH-157180) (GH-157557)
(cherry picked from commit f507e69)


Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: tonghuaroot <[email protected]>
Co-authored-by: rasmusfaber <[email protected]>
Yhg1s pushed a commit that referenced this pull request Sep 30, 2026
@miss-islington @encukou
@tonghuaroot @rasmusfaber
[3.12] gh-156002: Bound zipfile decompression for bzip2/LZMA/Zstandard (
09a2e7e
GH-156003) (GH-156362) (#156739)

* gh-156002: Bound zipfile decompression for bzip2/LZMA/Zstandard (GH-156003) (GH-156362)

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)
(cherry picked from commit 1b424c0)

* gh-156002: Keep reading through monkey-patched zipfile decompressors (GH-157180) (GH-157557)

(cherry picked from commit f507e69)

Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: tonghuaroot <[email protected]>
Co-authored-by: rasmusfaber <[email protected]>
pablogsal pushed a commit that referenced this pull request Oct 1, 2026
@miss-islington @encukou
@tonghuaroot @rasmusfaber
[3.10] gh-156002: Bound zipfile decompression for bzip2/LZMA/Zstandard (
c2bfbcd
GH-156003) (GH-156362) (#156741)

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

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)
(cherry picked from commit 1b424c0)

Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: tonghuaroot <[email protected]>

* Remove zstd test (3.14+)

* gh-156002: Keep reading through monkey-patched zipfile decompressors (GH-157180) (GH-157557)

(cherry picked from commit f507e69)

Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: rasmusfaber <[email protected]>

* Avoid unittest's enterContext; it doesn't exist yet

* Don't use the :cve: RST role, it doesn't exist yet

---------

Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: tonghuaroot <[email protected]>
Co-authored-by: rasmusfaber <[email protected]>
pablogsal pushed a commit that referenced this pull request Oct 1, 2026
@miss-islington @encukou
@tonghuaroot @rasmusfaber
[3.11] gh-156002: Bound zipfile decompression for bzip2/LZMA/Zstandard (
6257029
GH-156003) (GH-156362) (#156740)

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

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)
(cherry picked from commit 1b424c0)

Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: tonghuaroot <[email protected]>

* Remove zstd test (3.14+)

* gh-156002: Keep reading through monkey-patched zipfile decompressors (GH-157180) (GH-157557)

(cherry picked from commit f507e69)

Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: rasmusfaber <[email protected]>

* Don't use the :cve: RST role, it doesn't exist yet

---------

Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: tonghuaroot <[email protected]>
Co-authored-by: rasmusfaber <[email protected]>
icanhasmath pushed a commit to ActiveState/cpython that referenced this pull request Oct 28, 2026
@miss-islington @encukou
@tonghuaroot @rasmusfaber @icanhasmath
[3.10] pythongh-156002: Bound zipfile decompression for bzip2/LZMA/Zs…
346874c
…tandard (pythonGH-156003) (pythonGH-156362) (python#156741)

* [3.15] pythongh-156002: Bound zipfile decompression for bzip2/LZMA/Zstandard (pythonGH-156003) (pythonGH-156362)

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)
(cherry picked from commit 1b424c0)

Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: tonghuaroot <[email protected]>

* Remove zstd test (3.14+)

* pythongh-156002: Keep reading through monkey-patched zipfile decompressors (pythonGH-157180) (pythonGH-157557)

(cherry picked from commit f507e69)

Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: rasmusfaber <[email protected]>

* Avoid unittest's enterContext; it doesn't exist yet

* Don't use the :cve: RST role, it doesn't exist yet

---------

Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: tonghuaroot <[email protected]>
Co-authored-by: rasmusfaber <[email protected]>
(cherry picked from commit c2bfbcd)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@StanFromIreland StanFromIreland StanFromIreland left review comments

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@encukou encukou

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@encukou @tonghuaroot @StanFromIreland
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.