Skip to content

Double free corruption when allocation failures occur in assemble_init #151112

@stestagg

Description

@stestagg

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)]

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)type-crashA hard crash of the interpreter, possibly with a core dump
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions