stestagg · GitHub

Crash report

What happened?

import _testcapi
compile("pass", "<warmup>", "single")
_testcapi.set_nomemory(64, 65)
try:
    compile("pass", "<trigger>", "single")
except MemoryError:
    pass
first = bytes(128)
second = bytes(128)
Job 1, './python.exe reproduce_assemble…' terminated by signal SIGSEGV (Address boundary error)

Note, this reproducer only works if the allocation sizes are exactly the same as on my mac! the nomemory values may need tweaking.

The chain is:

  • _PyAssemble_MakeCodeObject is called
  • this calls assemble_emit which calls RETURN_IF_ERROR(assemble_init(a, first_lineno)); passing the assembler by ref as an argument.
  • An allocation failure happens in assemble_init, when allocating the linetable or the except table.
  • assemble_init cleans up by decreffing all members, BUT the assembler fields are left populated with the old pointers, and the caller has the assembler reference.
  • assemble_emit sees the error code and returns to _PyAssemble_MakeCodeObject
  • The result != SUCCESS, so makecode isn't called, but assemble_free IS called, causing a double free of at least a->a_bytecode.

The error manifests itself as heap corruption from the double free.

CPython versions tested on:

CPython main branch

Operating systems tested on:

macOS

Output from running 'python -VV' on the command line:

Python 3.16.0a0 (heads/main-dirty:f051c68923b, Jun 8 2026, 16:26:15) [Clang 21.0.0 (clang-2100.1.1.101)]

Linked PRs

Read the original on github.com ↗