zlib._ZlibDecompressor(zdict=...) raises SystemError on a zlib-wrapped stream #160001
Description
Bug description
zlib._ZlibDecompressor accepts a zdict argument, but for a zlib-wrapped stream (positive wbits) compressed with that dictionary, decompress fails with a SystemError instead of decompressing. zlib.decompressobj handles the same input:
>>> import zlib
>>> zd = b"hello world " * 10
>>> c = zlib.compressobj(zdict=zd)
>>> s = c.compress(b"hello world hello") + c.flush()
>>> zlib.decompressobj(zdict=zd).decompress(s)
b'hello world hello'
>>> zlib._ZlibDecompressor(zdict=zd).decompress(s)
Traceback (most recent call last):
File "<python-input-5>", line 1, in <module>
zlib._ZlibDecompressor(zdict=zd).decompress(s)
SystemError: <method 'decompress' of 'zlib._ZlibDecompressor' objects> returned NULL without setting an exceptionRaw deflate works, so this is specific to the wrapped format:
>>> c = zlib.compressobj(wbits=-15, zdict=zd)
>>> s = c.compress(b"hello world hello") + c.flush()
>>> zlib._ZlibDecompressor(-15, zdict=zd).decompress(s)
b'hello world hello'Two things combine here. The constructor only applies the dictionary up front for raw deflate (wbits < 0): Modules/zlibmodule.c lines 1775-1780. For a wrapped stream zlib instead signals Z_NEED_DICT after reading the header, and decompress_buf handles that by jumping to error without setting an exception: Modules/zlibmodule.c lines 1535-1541. zlib.Decompress handles the same return by calling inflateSetDictionary and continuing: Modules/zlibmodule.c lines 932-938, which is presumably what _ZlibDecompressor should do as well (and raise zlib.error when no zdict was given, as Decompress does).
_ZlibDecompressor is private and gzip only uses it with wbits=-15, so the impact is low; but a SystemError from a C method is always a bug, and the argument is accepted for this case, so we may as well fix it.
Found by the Hypothesis tests in #159960, recorded there as ZlibDecompressorZdictTests.test_zlib_wrapper under expectedFailure.
CPython versions tested on
CPython main (4e3d5ec), zlib 1.3.2
Operating systems tested on
Linux
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsNo status
Activity
Fix the known-bug tests on debug builds and zlib-ng
Keep FDICT zlib headers out of the invalid-input test and pin them
pganssle commentedon Oct 21, 2026
Fuzzing the tests in #159960 under Atheris turned up a broader trigger for this: no
zdictis needed. Any zlib-wrapped input whose header has the FDICT flag set makes_ZlibDecompressor.decompressreturn NULL without an exception, since that is exactly theZ_NEED_DICTbranch linked above. Eight bytes are enough:In a debug build that is a fatal C assertion in
ceval.crather than aSystemError, which is what killed the test worker on every CI job when #159960 first ran, so random invalid input reaches it easily.gziponly constructs_ZlibDecompressorwithwbits=-15, where there is no header to carry the flag, so the stdlib's own use is not affected; it is only reachable by calling the private class with a positive or automaticwbits. So the "impact is low" assessment above still holds, but the fix should raisezlib.errorfor the no-zdictcase rather than only wiring upinflateSetDictionary.#159960 now skips such headers in the zlib wrapper's
test_invalid_inputand pins the behaviour intest_fdict_header(release builds only, for the reason above).santhoshkumarselvaraj784-hue commentedon Oct 22, 2026
Hi, I’m a beginner Python developer and would like to make my first open-source contribution. I’m interested in working on this issue.
Could you please let me know if I can work on it? I’ll follow the project’s contribution guidelines and submit a pull request once the changes are ready.
@aisk
aisk commentedon Oct 22, 2026
Hi, we generally don't assign issues to people. If the issue is valid, the proposed fix makes sense, and nobody else is already working on it or has said they plan to, feel free to go ahead and open a PR. Please have a brief read of the devguide before starting.
Fix the known-bug tests on debug builds and zlib-ng
Keep FDICT zlib headers out of the invalid-input test and pin them
Pin the known compression bugs as expected-failure examples