Skip to content

Crash (Sigsev) in JIT optimizer _GUARD_LOAD_SUPER_ATTR_METHOD #148618

Description

@DarkaMaul

Bug report

Bug description:

The JIT optimizer crashes with a NULL pointer dereference (SIGSEGV at address 0x8) when tracing a polymorphic super().method() call site that has been observed with multiple different class types.

How to reproduce

Save the following as poc_stale_super_ptr.py and run with JIT stress mode:

./python.exe --version
Python 3.15.0a8+
PYTHON_JIT=0 ./python.exe poc_stale_super_ptr.py
# -> exit 0, no crash
PYTHON_JIT_STRESS=1 ./python.exe poc_stale_super_ptr.py
zsh: segmentation fault PYTHON_JIT_STRESS=1 ./python.exe poc_stale_super_ptr.py
poc_stale_super_ptr.py

Disclaimer: The PoC has been generated by an AI assistant, and I verified it worked on both my computer and a remote VM

classBase:
defmethod(self):
return1# 30 sibling classes each overriding method() with super().method()classC0(Base):
defmethod(self): returnsuper().method() +1classC1(Base):
defmethod(self): returnsuper().method() +1classC2(Base):
defmethod(self): returnsuper().method() +1classC3(Base):
defmethod(self): returnsuper().method() +1classC4(Base):
defmethod(self): returnsuper().method() +1classC5(Base):
defmethod(self): returnsuper().method() +1classC6(Base):
defmethod(self): returnsuper().method() +1classC7(Base):
defmethod(self): returnsuper().method() +1classC8(Base):
defmethod(self): returnsuper().method() +1classC9(Base):
defmethod(self): returnsuper().method() +1classC10(Base):
defmethod(self): returnsuper().method() +1classC11(Base):
defmethod(self): returnsuper().method() +1classC12(Base):
defmethod(self): returnsuper().method() +1classC13(Base):
defmethod(self): returnsuper().method() +1classC14(Base):
defmethod(self): returnsuper().method() +1classC15(Base):
defmethod(self): returnsuper().method() +1classC16(Base):
defmethod(self): returnsuper().method() +1classC17(Base):
defmethod(self): returnsuper().method() +1classC18(Base):
defmethod(self): returnsuper().method() +1classC19(Base):
defmethod(self): returnsuper().method() +1classC20(Base):
defmethod(self): returnsuper().method() +1classC21(Base):
defmethod(self): returnsuper().method() +1classC22(Base):
defmethod(self): returnsuper().method() +1classC23(Base):
defmethod(self): returnsuper().method() +1classC24(Base):
defmethod(self): returnsuper().method() +1classC25(Base):
defmethod(self): returnsuper().method() +1classC26(Base):
defmethod(self): returnsuper().method() +1classC27(Base):
defmethod(self): returnsuper().method() +1classC28(Base):
defmethod(self): returnsuper().method() +1classC29(Base):
defmethod(self): returnsuper().method() +1# 5-deep inheritance chainclassD0(Base):
defmethod(self): returnsuper().method() +1classD1(D0):
defmethod(self): returnsuper().method() +1classD2(D1):
defmethod(self): returnsuper().method() +1classD3(D2):
defmethod(self): returnsuper().method() +1classD4(D3):
defmethod(self): returnsuper().method() +1ALL= [
C0, C1, C2, C3, C4, C5, C6, C7, C8, C9,
C10, C11, C12, C13, C14, C15, C16, C17, C18, C19,
C20, C21, C22, C23, C24, C25, C26, C27, C28, C29,
D0, D1, D2, D3, D4,
]
# Pass 1 warms specialization; pass 2 triggers JIT tracing.for_inrange(2):
forclsinALL:
cls().method()

Root cause

Commit 458aca9237 added a _GUARD_LOAD_SUPER_ATTR_METHOD optimizer case in Python/optimizer_bytecodes.

op(_GUARD_LOAD_SUPER_ATTR_METHOD, (global_super_st, class_st, unused--global_super_st, class_st, unused)) {
if (sym_get_const(ctx, global_super_st) == (PyObject*)&PySuper_Type) {
PyTypeObject*probable= (PyTypeObject*)sym_get_probable_value(class_st);
PyTypeObject*known= (PyTypeObject*)sym_get_const(ctx, class_st);
// not known, but has a probable type, promote the probable type
if (known==NULL&&probable!=NULL&&PyType_Check(probable)) {
ADD_OP(_GUARD_NOS_TYPE_VERSION, 0, probable->tp_version_tag);
known=probable;
}
sym_set_const(class_st, (PyObject*)known);
}
else {
sym_set_const(global_super_st, (PyObject*)&PySuper_Type);
sym_set_type(class_st, &PyType_Type);
}
}

When known == NULL but there are no probable, then sym_set_const is called with (class_st, NULL).

sym_set_const calls make_const, which then calls Py_NewRef(NULL) and finally will at some point try to increase the ref count:

op->ob_refcnt++;

Fix

I think that's enough to do the following check:

// Python/optimizer_bytecodes.c, line ~955if (known!=NULL) {
sym_set_const(class_st, (PyObject*)known);
}

Found by compiling the stencils with ASAN

Stack crash
AddressSanitizer:DEADLYSIGNAL
=================================================================
==418369==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x56043dbaecb7 bp 0x7fffe30dca20 sp 0x7fffe30dca20 T0)
==418369==The signal is caused by a READ memory access.
==418369==Hint: address points to the zero page.
#0 0x56043dbaecb7 in Py_INCREF Include/refcount.h:286#1 0x56043dbaed00 in _Py_NewRef Include/refcount.h:536#2 0x56043dbaed00 in make_const Python/optimizer_symbols.c:187#3 0x56043dbb23b7 in _Py_uop_sym_set_const Python/optimizer_symbols.c:632#4 0x56043dba448f in optimize_uops Python/optimizer_cases.c.h:2371#5 0x56043dbaebd4 in _Py_uop_analyze_and_optimize Python/optimizer_analysis.c:792#6 0x56043db958e5 in uop_optimize Python/optimizer.c:1543#7 0x56043db96e45 in _PyOptimizer_Optimize Python/optimizer.c:166#8 0x56043d95308f in stop_tracing_and_jit Python/ceval.c:1138#9 0x56043d99c583 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:12213#10 0x56043d9a03a2 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:118#11 0x56043d9a06ed in _PyEval_Vector Python/ceval.c:2174#12 0x56043d9a097c in PyEval_EvalCode Python/ceval.c:686#13 0x56043dbde6de in run_eval_code_obj Python/pythonrun.c:1369#14 0x56043dbde9e7 in run_mod Python/pythonrun.c:1472#15 0x56043dbdf841 in pyrun_file Python/pythonrun.c:1296#16 0x56043dbe2424 in _PyRun_SimpleFileObject Python/pythonrun.c:518#17 0x56043dbe26ad in _PyRun_AnyFileObject Python/pythonrun.c:81#18 0x56043dc3582e in pymain_run_file_obj Modules/main.c:411#19 0x56043dc35a83 in pymain_run_file Modules/main.c:430#20 0x56043dc3730a in pymain_run_python Modules/main.c:715#21 0x56043dc3794c in Py_RunMain Modules/main.c:796#22 0x56043dc37b0b in pymain_main Modules/main.c:826#23 0x56043dc37e1c in Py_BytesMain Modules/main.c:850#24 0x56043d56eb71 in main Programs/python.c:15#25 0x7f159c345ca7 (/lib/x86_64-linux-gnu/libc.so.6+0x29ca7) (BuildId: 58749c528985eab03e6700ebc1469fa50aa41219)#26 0x7f159c345d64 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29d64) (BuildId: 58749c528985eab03e6700ebc1469fa50aa41219)#27 0x56043d56eaa0 in _start (/home/.../python+0x326aa0) (BuildId: 0751f7a328fd60f87db90ff375974647e5b13d3a)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV Include/refcount.h:286 in Py_INCREF
==418369==ABORTING

CPython versions tested on:

CPython main branch

Operating systems tested on:

macOS

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