Skip to content

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

Closed

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

Activity

  1. added
    type-crashA hard crash of the interpreter, possibly with a core dump
    3.13only security fixes
    3.14bugs and security fixes
    interpreter-core(Objects, Python, Grammar, and Parser dirs)
    on Aug 6, 2024
  2. self-assigned this
    on Aug 6, 2024
  3. added a commit that references this issue on Aug 7, 2024
  4. added a commit that references this issue on Aug 19, 2024
  5. removed
    3.14bugs and security fixes
    on Aug 20, 2024
  6. mpage commented on Aug 20, 2024

    @mpage
    ContributorAuthor

    Fixed in 3.14 by #123140

  7. added a commit that references this issue on Aug 21, 2024
  8. added a commit that references this issue on Aug 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

3.13only 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