Skip to content

push_cold_blocks_to_end() does not check the return value of basicblock_addop() #159054

Description

@lpyu001

Crash report

Summary

push_cold_blocks_to_end() does not check the return value of basicblock_addop() when adding the explicit jump block:

cpython/Python/flowgraph.c

Lines 3549 to 3550 in 2adc8b5

basicblock_addop(explicit_jump, JUMP_NO_INTERRUPT, b->b_next->b_label.id,
NO_LOCATION);

basicblock_addop() can fail with ERROR if allocation of the instruction array fails. In that case, explicit_jump remains empty, but the code
continues and assumes that the instruction was successfully added:

cpython/Python/flowgraph.c

Lines 3557 to 3558 in 2adc8b5

cfg_instr *last = basicblock_last_instr(explicit_jump);
last->i_target = explicit_jump->b_next;

basicblock_last_instr(explicit_jump) then returns NULL, and last->i_target dereferences it, causing a segmentation fault instead of
propagating the MemoryError.

The return value should be checked, for example with RETURN_IF_ERROR().

Reproduction Code

Requires _testcapi. Reproduced on a release build and on a --with-pydebug --disable-gil build of the main branch.

import _testcapi

src = "async def f():\n    await x\n"
compile(src, "<s>", "exec")  # warm up before injecting failures
for n in range(1, 500):
    _testcapi.set_nomemory(n, n + 1)
    try:
        compile(src, "<s>", "exec")
    except MemoryError:
        pass
    finally:
        _testcapi.remove_mem_hooks()
print("no crash")

The same crash occurs with these sources in place of src:

"def g():\n    yield from h()\n"
"async def a():\n    async for i in x:\n        pass\n"
"async def a():\n    return [i async for i in y]\n"

Actual Behavior

The process is killed by SIGSEGV (exit code 139) and no crash is never printed. With -X faulthandler:

Fatal Python error: Segmentation fault
Current thread 0x000075b5d80ec740 [python] (most recent call first):
  File "repro.py", line 8 in <module>

In gdb (debug build):

Program received signal SIGSEGV, Segmentation fault.
3558	            last->i_target = explicit_jump->b_next;
#0  push_cold_blocks_to_end at Python/flowgraph.c:3558
#1  _PyCfg_OptimizeCodeUnit at Python/flowgraph.c:3834
#2  optimize_and_assemble_code_unit at Python/compile.c:1487
#3  _PyCompile_OptimizeAndAssemble at Python/compile.c:1526
#4  codegen_function_body at Python/codegen.c:1474
#5  codegen_function at Python/codegen.c:1565
#6  codegen_visit_stmt at Python/codegen.c:3217
(gdb) p last
$1 = (cfg_instr *) 0x0
(gdb) p explicit_jump->b_iused
$2 = 0
(gdb) p explicit_jump->b_instr
$3 = (cfg_instr *) 0x0

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

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

No response

Linked PRs

Activity

  1. added
    type-crashA hard crash of the interpreter, possibly with a core dump
    on Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions