Skip to content

Specializing interpreter may crash if the code object of a class's __init__ method is reassigned #122712

Closed
@mpage

Description

@mpage

Crash report

What happened?

The interpreter will specialize CALL instructions into CALL_ALLOC_AND_ENTER_INIT when it sees that the target of a CALL is a heap type with a simple __init__ method (i.e. no *args, **kwargs, or kwonly args) and the correct number of arguments are provided to the call. The CALL_ALLOC_AND_ENTER_INIT verifies this condition using a weaker check based only on the argument count. It's possible to reassign the code object for a class's __init__ method using a code object that passes the argcount check in CALL_ALLOC_AND_ENTER_INIT, but uses one of the other properties that the specializer guards against (*args in the repro below).

The following repro causes the interpreter to crash (we should construct the *args tuple but do not):

class MyClass:
    def __init__(self):
        pass


def count_args(self, *args):
    print(f"there are {len(args)}")


def instantiate():
    MyClass()


SPECIALIZATION_THRESHOLD = 10


def main():
    # Trigger specialization
    for i in range(SPECIALIZATION_THRESHOLD):
        instantiate()
    # Trigger the bug
    MyClass.__init__.__code__ = count_args.__code__
    instantiate()


if __name__ == "__main__":
    main()

Backtrace:

python: Python/generated_cases.c.h:5011: _PyEval_EvalFrameDefault: Assertion `!PyStackRef_IsNull(GETLOCAL(oparg))' failed.

Program received signal SIGABRT, Aborted.
0x00007ffff7c8b94c in __pthread_kill_implementation () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff7c8b94c in __pthread_kill_implementation () from /lib64/libc.so.6
#1  0x00007ffff7c3e646 in raise () from /lib64/libc.so.6
#2  0x00007ffff7c287f3 in abort () from /lib64/libc.so.6
#3  0x00007ffff7c2871b in __assert_fail_base.cold () from /lib64/libc.so.6
#4  0x00007ffff7c37386 in __assert_fail () from /lib64/libc.so.6
#5  0x000000000065f3d8 in _PyEval_EvalFrameDefault (tstate=tstate@entry=0xa7c008 <_PyRuntime+416008>, frame=0x7ffff7e1d1c0, throwflag=throwflag@entry=0) at Python/generated_cases.c.h:5016
#6  0x00000000006697e2 in _PyEval_EvalFrame (throwflag=0, frame=<optimized out>, tstate=0xa7c008 <_PyRuntime+416008>) at ./Include/internal/pycore_ceval.h:119
#7  _PyEval_Vector (tstate=tstate@entry=0xa7c008 <_PyRuntime+416008>, func=func@entry=0x7ffff7a625d0,
    locals=locals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/mpage/local/scratch/repro_init_issue.py') at remote 0x7ffff7ac3120>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7bbcbf0>, '__file__': '/home/mpage/local/scratch/repro_init_issue.py', '__cached__': None, 'MyClass': <type at remote 0xba9bf0>, 'count_args': <function at remote 0x7ffff7a63590>, 'instantiate': <function at remote 0x7ffff7ae8050>, 'SPECIALIZATION_THRESHOLD': 10, 'main': <function at remote 0x7ffff7ae8110>}, args=args@entry=0x0, argcount=argcount@entry=0,
    kwnames=kwnames@entry=0x0) at Python/ceval.c:1821
#8  0x000000000066989c in PyEval_EvalCode (co=co@entry=<code at remote 0x7ffff7b63480>,
    globals=globals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/mpage/local/scratch/repro_init_issue.py') at remote 0x7ffff7ac3120>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7bbcbf0>, '__file__': '/home/mpage/local/scratch/repro_init_issue.py', '__cached__': None, 'MyClass': <type at remote 0xba9bf0>, 'count_args': <function at remote 0x7ffff7a63590>, 'instantiate': <function at remote 0x7ffff7ae8050>, 'SPECIALIZATION_THRESHOLD': 10, 'main': <function at remote 0x7ffff7ae8110>},
    locals=locals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/mpage/local/scratch/repro_init_issue.py') at remote 0x7ffff7ac3120>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7bbcbf0>, '__file__': '/home/mpage/local/scratch/repro_init_issue.py', '__cached__': None, 'MyClass': <type at remote 0xba9bf0>, 'count_args': <function at remote 0x7ffff7a63590>, 'instantiate': <function at remote 0x7ffff7ae8050>, 'SPECIALIZATION_THRESHOLD': 10, 'main': <function at remote 0x7ffff7ae8110>}) at Python/ceval.c:618
#9  0x00000000006e51c9 in run_eval_code_obj (tstate=tstate@entry=0xa7c008 <_PyRuntime+416008>, co=co@entry=0x7ffff7b63480,
    globals=globals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/mpage/local/scratch/repro_init_issue.py') at remote 0x7ffff7ac3120>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7bbcbf0>, '__file__': '/home/mpage/local/scratch/repro_init_issue.py', '__cached__': None, 'MyClass': <type at remote 0xba9bf0>, 'count_args': <function at remote 0x7ffff7a63590>, 'instantiate': <function at remote 0x7ffff7ae8050>, 'SPECIALIZATION_THRESHOLD': 10, 'main': <function at remote 0x7ffff7ae8110>},
    locals=locals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/mpage/local/scratch/repro_init_issue.py') at remote 0x7ffff7ac3120>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7bbcbf0>, '__file__': '/home/mpage/local/scratch/repro_init_issue.py', '__cached__': None, 'MyClass': <type at remote 0xba9bf0>, 'count_args': <function at remote 0x7ffff7a63590>, 'instantiate': <function at remote 0x7ffff7ae8050>, 'SPECIALIZATION_THRESHOLD': 10, 'main': <function at remote 0x7ffff7ae8110>}) at Python/pythonrun.c:1292
#10 0x00000000006e69aa in run_mod (mod=mod@entry=0xbb8770, filename=filename@entry='/home/mpage/local/scratch/repro_init_issue.py',
    globals=globals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/mpage/local/scratch/repro_init_issue.py') at remote 0x7ffff7ac3120>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7bbcbf0>, '__file__': '/home/mpage/local/scratch/repro_init_issue.py', '__cached__': None, 'MyClass': <type at remote 0xba9bf0>, 'count_args': <function at remote 0x7ffff7a63590>, 'instantiate': <function at remote 0x7ffff7ae8050>, 'SPECIALIZATION_THRESHOLD': 10, 'main': <function at remote 0x7ffff7ae8110>},
    locals=locals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/mpage/local/scratch/repro_init_issue.py') at remote 0x7ffff7ac3120>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7bbcbf0>, '__file__': '/home/mpage/local/scratch/repro_init_issue.py', '__cached__': None, 'MyClass': <type at remote 0xba9bf0>, 'count_args': <function at remote 0x7ffff7a63590>, 'instantiate': <function at remote 0x7ffff7ae8050>, 'SPECIALIZATION_THRESHOLD': 10, 'main': <function at remote 0x7ffff7ae8110>}, flags=flags@entry=0x7fffffffd2b8, arena=arena@entry=0x7ffff7ad96c0,
    interactive_src=0x0, generate_new_source=0) at Python/pythonrun.c:1377
#11 0x00000000006e705b in pyrun_file (fp=fp@entry=0xba0550, filename=filename@entry='/home/mpage/local/scratch/repro_init_issue.py', start=start@entry=257,
    globals=globals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/mpage/local/scratch/repro_init_issue.py') at remote 0x7ffff7ac3120>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7bbcbf0>, '__file__': '/home/mpage/local/scratch/repro_init_issue.py', '__cached__': None, 'MyClass': <type at remote 0xba9bf0>, 'count_args': <function at remote 0x7ffff7a63590>, 'instantiate': <function at remote 0x7ffff7ae8050>, 'SPECIALIZATION_THRESHOLD': 10, 'main': <function at remote 0x7ffff7ae8110>},
    locals=locals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/mpage/local/scratch/repro_init_issue.py') at remote 0x7ffff7ac3120>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7bbcbf0>, '__file__': '/home/mpage/local/scratch/repro_init_issue.py', '__cached__': None, 'MyClass': <type at remote 0xba9bf0>, 'count_args': <function at remote 0x7ffff7a63590>, 'instantiate': <function at remote 0x7ffff7ae8050>, 'SPECIALIZATION_THRESHOLD': 10, 'main': <function at remote 0x7ffff7ae8110>}, closeit=closeit@entry=1, flags=0x7fffffffd2b8)
    at Python/pythonrun.c:1210
#12 0x00000000006e8199 in _PyRun_SimpleFileObject (fp=fp@entry=0xba0550, filename=filename@entry='/home/mpage/local/scratch/repro_init_issue.py', closeit=closeit@entry=1, flags=flags@entry=0x7fffffffd2b8)
    at Python/pythonrun.c:459
#13 0x00000000006e8445 in _PyRun_AnyFileObject (fp=fp@entry=0xba0550, filename=filename@entry='/home/mpage/local/scratch/repro_init_issue.py', closeit=closeit@entry=1, flags=flags@entry=0x7fffffffd2b8)
    at Python/pythonrun.c:77
#14 0x0000000000712c8d in pymain_run_file_obj (program_name=program_name@entry='/data/users/mpage/cpython/python', filename=filename@entry='/home/mpage/local/scratch/repro_init_issue.py', skip_source_first_line=0)
    at Modules/main.c:409
#15 0x0000000000714203 in pymain_run_file (config=config@entry=0xa472d8 <_PyRuntime+199640>) at Modules/main.c:428
#16 0x00000000007146bd in pymain_run_python (exitcode=exitcode@entry=0x7fffffffd444) at Modules/main.c:696
#17 0x0000000000714727 in Py_RunMain () at Modules/main.c:775
#18 0x000000000071479e in pymain_main (args=args@entry=0x7fffffffd4a0) at Modules/main.c:805
#19 0x0000000000714859 in Py_BytesMain (argc=<optimized out>, argv=<optimized out>) at Modules/main.c:829
#20 0x000000000041e86f in main (argc=<optimized out>, argv=<optimized out>) at ./Programs/python.c:15

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

Metadata

Metadata

Assignees

Labels

3.13bugs and security fixesinterpreter-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