https://github.com/python/cpython/commit/54426877bd7ea4e532f5d531a2958d5aa1c29f7c
commit: 54426877bd7ea4e532f5d531a2958d5aa1c29f7c
branch: main
author: Stan Ulbrych <[email protected]>
committer: StanFromIreland <[email protected]>
date: 2026-08-23T10:18:03+01:00
summary:

gh-156173: Fix `zlib.Decompress.flush()` silently returning corrupted output 
instead of raising `zlib.error` (#156176)

files:
A Misc/NEWS.d/next/Library/2026-08-21-11-56-28.gh-issue-156173.mhZa8a.rst
M Lib/test/test_zlib.py
M Modules/zlibmodule.c

diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py
index 70d1cd81ac6c46..024c58e7cabd79 100644
--- a/Lib/test/test_zlib.py
+++ b/Lib/test/test_zlib.py
@@ -722,6 +722,20 @@ def test_decompress_eof_incomplete_stream(self):
         dco.flush()
         self.assertFalse(dco.eof)
 
+    def test_decompress_flush_corrupt_stream(self):
+        x = b'x\x9cK\xcb\xcf\x07\x00\x02\x82\x01E'  # 'foo'
+        corrupt = x[:-1] + b'\x00'
+        dco = zlib.decompressobj()
+        self.assertEqual(dco.decompress(corrupt, 1), b'f')
+        self.assertRaises(zlib.error, dco.flush)
+
+    def test_decompress_flush_twice(self):
+        x = b'x\x9cK\xcb\xcf\x07\x00\x02\x82\x01E'  # 'foo'
+        dco = zlib.decompressobj()
+        self.assertEqual(dco.decompress(x), b'foo')
+        self.assertEqual(dco.flush(), b'')
+        self.assertEqual(dco.flush(), b'')
+
     def test_decompress_unused_data(self):
         # Repeated calls to decompress() after EOF should accumulate data in
         # dco.unused_data, instead of just storing the arg to the last call.
diff --git 
a/Misc/NEWS.d/next/Library/2026-08-21-11-56-28.gh-issue-156173.mhZa8a.rst 
b/Misc/NEWS.d/next/Library/2026-08-21-11-56-28.gh-issue-156173.mhZa8a.rst
new file mode 100644
index 00000000000000..846931a1cab511
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-21-11-56-28.gh-issue-156173.mhZa8a.rst
@@ -0,0 +1,2 @@
+Calling :meth:`zlib.Decompress.flush` on invalid compressed data now
+raises :exc:`zlib.error` instead of being silently ignored.
diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c
index d06b94d1e83713..a7fefb1fec5de9 100644
--- a/Modules/zlibmodule.c
+++ b/Modules/zlibmodule.c
@@ -1271,6 +1271,13 @@ zlib_Decompress_flush_impl(compobject *self, 
PyTypeObject *cls,
 
     PyMutex_Lock(&self->mutex);
 
+    /* A previous flush() already reached the end of the stream and freed the
+       decompression state, so there is nothing left to process. */
+    if (!self->is_initialised) {
+        PyMutex_Unlock(&self->mutex);
+        return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
+    }
+
     if (PyObject_GetBuffer(self->unconsumed_tail, &data, PyBUF_SIMPLE) == -1) {
         PyMutex_Unlock(&self->mutex);
         return NULL;
@@ -1328,6 +1335,10 @@ zlib_Decompress_flush_impl(compobject *self, 
PyTypeObject *cls,
             goto abort;
         }
     }
+    else if (err != Z_OK && err != Z_BUF_ERROR) {
+        zlib_error(state, self->zst, err, "while decompressing data");
+        goto abort;
+    }
 
     return_value = OutputBuffer_WindowFinish(&buffer, &window, 
self->zst.avail_out);
     if (return_value != NULL) {

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to