Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mpage opened this issue Aug 6, 2024 · 1 comment
Assignees
Labels
3.13 bugs and security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-crash A hard crash of the interpreter, possibly with a core dump

Comments

@mpage
Copy link
Contributor

mpage commented Aug 6, 2024

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

@mpage mpage added type-crash A hard crash of the interpreter, possibly with a core dump 3.13 bugs and security fixes 3.14 bugs and security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) labels Aug 6, 2024
@mpage mpage self-assigned this Aug 6, 2024
mpage added a commit to mpage/cpython that referenced this issue Aug 7, 2024
mpage added a commit to mpage/cpython that referenced this issue Aug 19, 2024
@mpage mpage assigned markshannon and mpage and unassigned mpage and markshannon Aug 20, 2024
@mpage mpage removed the 3.14 bugs and security fixes label Aug 20, 2024
@mpage
Copy link
Contributor Author

mpage commented Aug 20, 2024

Fixed in 3.14 by #123140

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.13 bugs and security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-crash A hard crash of the interpreter, possibly with a core dump
Projects
None yet
Development

No branches or pull requests

2 participants