You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
zlib._ZlibDecompressor(zdict=...) raises SystemError on a zlib-wrapped stream #160001
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:
>>>importzlib>>>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 (mostrecentcalllast):
File"<python-input-5>", line1, in<module>zlib._ZlibDecompressor(zdict=zd).decompress(s)
SystemError: <method'decompress'of'zlib._ZlibDecompressor'objects>returnedNULLwithoutsettinganexception
Raw 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.
Fuzzing the tests in #159960 under Atheris turned up a broader trigger for this: no zdict is needed. Any zlib-wrapped input whose header has the FDICT flag set makes _ZlibDecompressor.decompress return NULL without an exception, since that is exactly the Z_NEED_DICT branch linked above. Eight bytes are enough:
In a debug build that is a fatal C assertion in ceval.c rather than a SystemError, which is what killed the test worker on every CI job when #159960 first ran, so random invalid input reaches it easily. gzip only constructs _ZlibDecompressor with wbits=-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 automatic wbits. So the "impact is low" assessment above still holds, but the fix should raise zlib.error for the no-zdict case rather than only wiring up inflateSetDictionary.
#159960 now skips such headers in the zlib wrapper's test_invalid_input and pins the behaviour in test_fdict_header (release builds only, for the reason above).
santhoshkumarselvaraj784-hue commented on 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
8. 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
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.
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.