From 5bcd67189b533116db5acb5b16597213580a5b76 Mon Sep 17 00:00:00 2001 From: Vamsi-klu Date: Sun, 26 Jul 2026 07:10:52 +0000 Subject: [PATCH 1/3] gh-148395: Add regression tests for decompressor UAF after MemoryError The CVE-2026-6100 fix (next_in = NULL on error) already landed. Add nomemtest coverage for bz2, lzma and zlib._ZlibDecompressor so a regression cannot silently return. --- Lib/test/test_bz2.py | 23 +++++++++++++++++++++++ Lib/test/test_lzma.py | 23 +++++++++++++++++++++++ Lib/test/test_zlib.py | 25 +++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index 64293d757331d75..fc4fcebfbe230a7 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -1060,6 +1060,29 @@ def test_uninitialized_BZ2Decompressor_crash(self): self.assertEqual(BZ2Decompressor.__new__(BZ2Decompressor). decompress(bytes()), b'') + @support.nomemtest + def test_decompress_memoryerror_no_dangling_input(self): + # gh-148395 / CVE-2026-6100: after MemoryError in decompress(), the + # decompressor must not keep a dangling pointer into the input buffer. + import _testcapi + data = bz2.compress(b'x' * 4096) + for start in range(0, 40): + d = bz2.BZ2Decompressor() + try: + _testcapi.set_nomemory(start, start + 1) + try: + d.decompress(bytearray(data), max_length=0) + except MemoryError: + pass + finally: + _testcapi.remove_mem_hooks() + try: + out = d.decompress(bytearray(data)) + except (MemoryError, ValueError, EOFError, OSError): + continue + self.assertIsInstance(out, bytes) + self.assertNotIn(b'\x00' * 64, out) + class CompressDecompressTest(BaseTest): def testCompress(self): diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py index d4f954b34c12526..7ed6b4fbdf471cf 100644 --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -383,6 +383,29 @@ def test_uninitialized_LZMADecompressor_crash(self): self.assertEqual(LZMADecompressor.__new__(LZMADecompressor). decompress(bytes()), b'') + @support.nomemtest + def test_decompress_memoryerror_no_dangling_input(self): + # gh-148395 / CVE-2026-6100: after MemoryError in decompress(), the + # decompressor must not keep a dangling pointer into the input buffer. + import _testcapi + data = lzma.compress(b'x' * 4096) + for start in range(0, 40): + d = lzma.LZMADecompressor() + try: + _testcapi.set_nomemory(start, start + 1) + try: + d.decompress(bytearray(data), max_length=0) + except MemoryError: + pass + finally: + _testcapi.remove_mem_hooks() + try: + out = d.decompress(bytearray(data)) + except (MemoryError, ValueError, EOFError, OSError, LZMAError): + continue + self.assertIsInstance(out, bytes) + self.assertNotIn(b'\x00' * 64, out) + def test_riscv_filter_constant_exists(self): self.assertTrue(lzma.FILTER_RISCV) diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index ed9d85408159b28..9e84486e5cce741 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -1216,6 +1216,31 @@ def test_refleaks_in___init__(self): zlibd.__init__() self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10) + @support.nomemtest + @unittest.skipUnless(hasattr(zlib, '_ZlibDecompressor'), + 'requires zlib._ZlibDecompressor') + def test_decompress_memoryerror_no_dangling_input(self): + # gh-148395 / CVE-2026-6100: after MemoryError in decompress(), the + # decompressor must not keep a dangling pointer into the input buffer. + import _testcapi + data = zlib.compress(b'x' * 4096) + for start in range(0, 40): + d = zlib._ZlibDecompressor() + try: + _testcapi.set_nomemory(start, start + 1) + try: + d.decompress(bytearray(data), max_length=0) + except MemoryError: + pass + finally: + _testcapi.remove_mem_hooks() + try: + out = d.decompress(bytearray(data)) + except (MemoryError, ValueError, EOFError, OSError, zlib.error): + continue + self.assertIsInstance(out, bytes) + self.assertNotIn(b'\x00' * 64, out) + class CustomInt: def __index__(self): From b18096bc9d97c8b608eeb2250891fa31f36d4057 Mon Sep 17 00:00:00 2001 From: Vamsi-klu Date: Sun, 26 Jul 2026 07:17:43 +0000 Subject: [PATCH 2/3] gh-148395: Add Tests NEWS entry for decompressor UAF regression tests Satisfy bedevere/news for the test-only PR (Security NEWS for the CVE fix already shipped). --- .../Tests/2026-07-26-07-17-42.gh-issue-148395.rUUXvU.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Tests/2026-07-26-07-17-42.gh-issue-148395.rUUXvU.rst diff --git a/Misc/NEWS.d/next/Tests/2026-07-26-07-17-42.gh-issue-148395.rUUXvU.rst b/Misc/NEWS.d/next/Tests/2026-07-26-07-17-42.gh-issue-148395.rUUXvU.rst new file mode 100644 index 000000000000000..0a88eef5a352773 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2026-07-26-07-17-42.gh-issue-148395.rUUXvU.rst @@ -0,0 +1,5 @@ +Add regression tests for the CVE-2026-6100 fix so a +:class:`~bz2.BZ2Decompressor`, :class:`~lzma.LZMADecompressor`, or +:class:`zlib._ZlibDecompressor` that raised :exc:`MemoryError` during +:meth:`~bz2.BZ2Decompressor.decompress` cannot silently regain a dangling +input pointer on the next call. From cc8494c61132659e53501d743ff74268baffa51b Mon Sep 17 00:00:00 2001 From: Vamsi-klu Date: Sun, 26 Jul 2026 07:24:11 +0000 Subject: [PATCH 3/3] gh-148395: Avoid Sphinx role nits in Tests NEWS entry zlib._ZlibDecompressor is not a documented public class, so :class: roles in the blurb fail Docs CI (--fail-if-new-news-nit). Use plain double-backticks instead. --- .../2026-07-26-07-17-42.gh-issue-148395.rUUXvU.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Misc/NEWS.d/next/Tests/2026-07-26-07-17-42.gh-issue-148395.rUUXvU.rst b/Misc/NEWS.d/next/Tests/2026-07-26-07-17-42.gh-issue-148395.rUUXvU.rst index 0a88eef5a352773..e46de753f0c1e06 100644 --- a/Misc/NEWS.d/next/Tests/2026-07-26-07-17-42.gh-issue-148395.rUUXvU.rst +++ b/Misc/NEWS.d/next/Tests/2026-07-26-07-17-42.gh-issue-148395.rUUXvU.rst @@ -1,5 +1,5 @@ -Add regression tests for the CVE-2026-6100 fix so a -:class:`~bz2.BZ2Decompressor`, :class:`~lzma.LZMADecompressor`, or -:class:`zlib._ZlibDecompressor` that raised :exc:`MemoryError` during -:meth:`~bz2.BZ2Decompressor.decompress` cannot silently regain a dangling -input pointer on the next call. +Add regression tests for the CVE-2026-6100 decompressor fix so a +``bz2.BZ2Decompressor``, ``lzma.LZMADecompressor``, or +``zlib._ZlibDecompressor`` that raised ``MemoryError`` during +``decompress()`` cannot silently regain a dangling input pointer on +the next call.