Skip to content

JIT: segfault from _PyObject_LookupSpecialMethod #149459

@devdanzin

Description

@devdanzin

Crash report

What happened?

A JIT-enabled interpreter will crash by running the following code:

MRE:

def f1():
    class Context:
        def __enter__(self): ...
        def __exit__(self, e, v, t): ...

    with Context():
        pass

for i_f1 in range(4100):
    f1()

Backtrace:

Program received signal SIGSEGV, Segmentation fault.
0x0000555555c9f8d4 in _Py_TYPE_impl (ob=0x0) at ./Include/object.h:234
234         return ob->ob_type;

#0  0x0000555555c9f8d4 in _Py_TYPE_impl (ob=0x0) at ./Include/object.h:234
#1  _PyObject_LookupSpecialMethod (attr=attr@entry=0x555556a2d9d8 <_PyRuntime+80600>, method_and_self=method_and_self@entry=0x7e8ff6de5300) at Objects/typeobject.c:2972
#2  0x0000555555e95f5c in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>) at Python/generated_cases.c.h:9933
#3  0x0000555555e89408 in _PyEval_EvalFrame (tstate=0x555556a72138 <_PyRuntime+361016>, frame=0x7e8ff6de5220, throwflag=0) at ./Include/internal/pycore_ceval.h:122
#4  _PyEval_Vector (tstate=<optimized out>, func=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=0x0) at Python/ceval.c:2124
#5  0x0000555555e88e25 in PyEval_EvalCode (co=<optimized out>, globals=<optimized out>, locals=0x7c7ff6e8a0c0) at Python/ceval.c:686
#6  0x000055555617a090 in run_eval_code_obj (tstate=tstate@entry=0x555556a72138 <_PyRuntime+361016>, co=co@entry=0x7d1ff6e85f50, globals=globals@entry=0x7c7ff6e8a0c0,
    locals=locals@entry=0x7c7ff6e8a0c0) at Python/pythonrun.c:1369
#7  0x000055555617925c in run_mod (mod=<optimized out>, filename=<optimized out>, globals=<optimized out>, locals=<optimized out>, flags=<optimized out>, arena=<optimized out>,
    interactive_src=<optimized out>, generate_new_source=<optimized out>) at Python/pythonrun.c:1472
#8  0x00005555561736ad in pyrun_file (fp=fp@entry=0x7d4ff6e03a80, filename=filename@entry=0x7ccff6e2ccd0, start=start@entry=257, globals=globals@entry=0x7c7ff6e8a0c0,
    locals=locals@entry=0x7c7ff6e8a0c0, closeit=closeit@entry=1, flags=0x7bfff5c77710) at Python/pythonrun.c:1296
#9  0x000055555617120d in _PyRun_SimpleFileObject (fp=<optimized out>, filename=<optimized out>, closeit=<optimized out>, flags=<optimized out>) at Python/pythonrun.c:518
#10 0x000055555617057e in _PyRun_AnyFileObject (fp=fp@entry=0x7d4ff6e03a80, filename=filename@entry=0x7ccff6e2ccd0, closeit=closeit@entry=1, flags=flags@entry=0x7bfff5c77710)
    at Python/pythonrun.c:81
#11 0x00005555561f0cdb in pymain_run_file_obj (program_name=0x7caff6e82500, filename=0x7ccff6e2ccd0, skip_source_first_line=0) at Modules/main.c:411
#12 pymain_run_file (config=0x555556a3d0f0 <_PyRuntime+143856>) at Modules/main.c:430
#13 0x00005555561eef72 in pymain_run_python (exitcode=0x7bfff5c77300) at Modules/main.c:715
#14 Py_RunMain () at Modules/main.c:796
#15 0x00005555561efca7 in pymain_main (args=<optimized out>) at Modules/main.c:826
#16 0x00005555561efe18 in Py_BytesMain (argc=<optimized out>, argv=0x7fffffffdb78) at Modules/main.c:850
#17 0x00007ffff7c2a575 in __libc_start_call_main (main=main@entry=0x555555913c40 <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffdb78) at ../sysdeps/nptl/libc_start_call_main.h:58
#18 0x00007ffff7c2a628 in __libc_start_main_impl (main=0x555555913c40 <main>, argc=2, argv=0x7fffffffdb78, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>,
    stack_end=0x7fffffffdb68) at ../csu/libc-start.c:360
#19 0x000055555582a4f5 in _start ()

Output from running with PYTHON_LLTRACE=4 PYTHON_OPT_DEBUG=4:
lltrace_opt_debug.txt
This log file ends in:

[...]
  55 uop: _CHECK_VALIDITY_r11 (0, jump_target=132, operand0=0, operand1=0)
    locals=[<type at 0x6f8dffa7c9a0>]
    stack=[]
    cache=[<Context at 0x6f2dffa3f4b0>, ---, ---]
  56 uop: _COPY_1_r12 (1, target=18, operand0=0, operand1=0)
    locals=[<type at 0x6f8dffa7c9a0>]
    stack=[]
    cache=[<Context at 0x6f2dffa3f4b0>, <Context at 0x6f2dffa3f4b0>, ---]
  57 uop: _SPILL_OR_RELOAD_r21 (0, target=0, operand0=0, operand1=0)
    locals=[<type at 0x6f8dffa7c9a0>]
    stack=[<Context at 0x6f2dffa3f4b0>]
    cache=[<Context at 0x6f2dffa3f4b0>, ---, ---]
  58 uop: _INSERT_NULL_r10 (1, target=19, operand0=0, operand1=0)
    locals=[<type at 0x6f8dffa7c9a0>]
    stack=[<Context at 0x6f2dffa3f4b0>, <NULL>, <Context at 0x6f2dffa3f4b0>]
    cache=[---, ---, ---]
  59 uop: _GUARD_TYPE_VERSION_r01 (0, jump_target=133, operand0=0x20110, operand1=0)
    locals=[<type at 0x6f8dffa7c9a0>]
    stack=[<Context at 0x6f2dffa3f4b0>, <NULL>, <Context at 0x6f2dffa3f4b0>]
    cache=[---, ---, ---]
 133 uop: _EXIT_TRACE_r00 (0, target=19, operand0=0x6ffdff9f3158, operand1=0)
SIDE EXIT: [UOp _EXIT_TRACE_r00 (0, target=19, operand0=0x6ffdff9f3158, operand1=0), exit 5, temp 510, target 19 -> LOAD_SPECIAL, is_control_flow 0]
    locals=[<type at 0x6f8dffa7c9a0>]
    stack=[<Context at 0x6f2dffa3f4b0>, <NULL>, <Context at 0x6f2dffa3f4b0>]
    cache=[---, ---, ---]
   0 uop: _COLD_EXIT_r00 (52685, jump_target=52685, operand0=0xcdcdcdcdcdcdcdcd, operand1=0xcdcdcdcdcdcdcdcd)
AddressSanitizer:DEADLYSIGNAL
=================================================================
==2328978==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x61a5c39808d4 bp 0x7fff81470a50 sp 0x7fff814709f0 T0)
==2328978==The signal is caused by a READ memory access.
==2328978==Hint: address points to the zero page.
    #0 0x61a5c39808d4 in _Py_TYPE_impl /home/danzin/projects/jit_cpython/./Include/object.h:234:16
    #1 0x61a5c39808d4 in _PyObject_LookupSpecialMethod /home/danzin/projects/jit_cpython/Objects/typeobject.c:2972:38
    #2 0x61a5c3b76f5b in _PyEval_EvalFrameDefault /home/danzin/projects/jit_cpython/Python/generated_cases.c.h:9933:27
    #3 0x61a5c3b6a407 in _PyEval_EvalFrame /home/danzin/projects/jit_cpython/./Include/internal/pycore_ceval.h:122:16
[...]

Claude's one sentence explanation:

The JIT's _LOAD_SPECIAL specialisation in Python/optimizer_bytecodes.c:2015–2031 injects _GUARD_TYPE_VERSION after _INSERT_NULL has already been emitted into the output trace, but the guard's deopt target is the LOAD_SPECIAL bytecode, which expects the stack in its pre-_INSERT_NULL state. When the guard fails, tier 1 re-runs the full LOAD_SPECIAL macro on a stack that's already been bumped by _INSERT_NULL, producing one extra slot that propagates through the surrounding SWAP 2/SWAP 3 and lands a NULL as the receiver of the next LOAD_SPECIAL (__enter__), where _PyObject_LookupSpecialMethod does Py_TYPE(self) on it.

Found using lafleur.

CPython versions tested on:

CPython main branch, 3.15

Operating systems tested on:

Linux

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

Python 3.15.0a8+ (heads/main-dirty:0bf6e31941f, May 6 2026, 07:41:51) [Clang 21.1.2 (2ubuntu6)]

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)topic-JITtype-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