gh-148395: Add regression tests for decompressor UAF after MemoryError (CVE-2026-6100) by Vamsi-klu · Pull Request #154713 · python/cpython · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Lib/test/test_bz2.py
23 changes: 23 additions & 0 deletions Lib/test/test_lzma.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
25 changes: 25 additions & 0 deletions Lib/test/test_zlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines +1239 to +1240

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Make the zlib test detect the missing pointer reset

When the injected failure reaches the tail-buffer allocation (start == 2 on a --with-pydebug build), removing both self->zst.next_in = NULL assignments from zlibmodule.c makes the follow-up call raise zlib.error for an incorrect data check; this handler accepts that result, so the test still passes with the CVE fix reverted. Exercise a post-error operation with an explicit safe result that differs when the stale pointer remains instead of allowing this error to satisfy the regression test.

Useful? React with 👍 / 👎.

self.assertIsInstance(out, bytes)
self.assertNotIn(b'\x00' * 64, out)


class CustomInt:
def __index__(self):
Expand Down
Loading