gh-148395: Add regression tests for decompressor UAF after MemoryError (CVE-2026-6100)#154713
gh-148395: Add regression tests for decompressor UAF after MemoryError (CVE-2026-6100)#154713Vamsi-klu wants to merge 3 commits into
Conversation
…ryError 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.
|
Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool. If this change has little impact on Python users, wait for a maintainer to apply the |
… tests Satisfy bedevere/news for the test-only PR (Security NEWS for the CVE fix already shipped).
|
Added a For pure test-only changes, |
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.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cc8494c611
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| except (MemoryError, ValueError, EOFError, OSError): | ||
| continue |
There was a problem hiding this comment.
Make the bz2 test detect the missing pointer reset
When the injected failure reaches the tail-buffer allocation (start == 2 on a --with-pydebug build), removing both bzs->next_in = NULL assignments from _bz2module.c makes the follow-up call raise OSError: Invalid data stream; this handler accepts that result, so the test still passes with the CVE fix reverted. Use a follow-up operation and explicit expected outcome that distinguish a cleared pointer from the dangling-pointer state rather than treating this decompression error as success.
Useful? React with 👍 / 👎.
| except (MemoryError, ValueError, EOFError, OSError, zlib.error): | ||
| continue |
There was a problem hiding this comment.
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 👍 / 👎.
|
We donnot add tests for that as it may depend on many other factors and is CPython specific. |
|
Also avoid just letting an agent review everytjing and write everything. This is against our AI policy explained in the devguide. |
Summary
Adds the missing regression tests for CVE-2026-6100 (gh-148395). The security fix itself (
next_in = NULLat theerror:labels in bz2 / lzma / zlib decompressors) is already merged on main and most release branches. No production code changes in this PR.Closes the test gap for gh-148395 (does not replace the still-open 3.12 backport #148503, which is an RM action).
What the issue is
CVE-2026-6100: after a
MemoryErrorinside{BZ2,LZMA,_Zlib}Decompressor.decompress(), the static helper leftnext_inpointing into the caller's releasedPy_buffer. The nextdecompress()call couldmemcpythrough a freed pointer (attacker-controlled length and content) — CVSS 9.1.The fix (8fc66ae and backports) nulls
next_inon the error path. No branch shipped a regression test. A future refactor can reintroduce the UAF with a green CI.Why I solved it that way
{LZMA,BZ2,_Zlib}Decompressor(GH-148396) #148503 or re-patching the C modules would be wrong — the fix is done; coverage is not.support.nomemtest, not bare_testcapi.set_nomemory, soPy_TRACE_REFSbuilds skip cleanly (they fatal inside_PyRefchain_Traceon allocation failure).startinrange(0, 40)rather than a hard-coded allocation index — indices are unstable across pymalloc/ASAN/debug/bit-width.bytearray(data)+max_length=0—bytescan stay alive and hide the UAF;max_length=0forces the!input_buffer_in_usetail-copy path that historically hit the failingPyMem_Malloc.remove_mem_hooks()infinally— the hook is process-global across RAW/MEM/OBJ; leaking it destroys the rest of the suite.test_free_threading(global allocator hooks + workers = flaky).hasattr(zlib, '_ZlibDecompressor')for older-branch cherry-picks.I did not fold in the optional sticky-error / success-path hardening from the issue discussion — that is a behavior change for a follow-up.
How I did it
Lib/test/test_bz2.pyBZ2DecompressorTest.test_decompress_memoryerror_no_dangling_inputLib/test/test_lzma.pyCompressorDecompressorTestCase.test_decompress_memoryerror_no_dangling_inputLib/test/test_zlib.pyZlibDecompressorTest.test_decompress_memoryerror_no_dangling_inputShape (bz2; others mirror):
No NEWS entry (test-only; Security NEWS for the CVE already released).
Impact
{LZMA,BZ2,_Zlib}Decompressor(GH-148396) #148503).Testing plan
./python -m test test_bz2 test_lzma test_zlibon a debug build — all three new tests PASS with the fix present.next_in = NULLlines, rebuild with ASAN (--with-address-sanitizer --without-pymalloc), confirmheap-use-after-freeon thememcpypath, restore fix. I did not block this PR on an ASAN cycle; happy to attach traces if a reviewer wants them.Everything else
{LZMA,BZ2,_Zlib}Decompressor#148396 (main fix, merged), [3.12] gh-148395: Fix a possible UAF in{LZMA,BZ2,_Zlib}Decompressor(GH-148396) #148503 (3.12 backport, RM-blocked — please do not apply the stale NEWS suggestions that drop_ZlibDecompressor).needs backport to 3.15,3.14,3.13after merge if desired.Requested reviewers (subject-matter experts)
Could the following SMEs take a look when convenient (I cannot formally request reviews from this fork account):
Thank you!