Skip to content

Segfault on cls.__dict__['__new__'].__annotations__ #125017

Description

@AA-Turner

Crash report

What happened?

The following is a reproducer of (another) segfault discovered when trying to test Python 3.14 in CI for Sphinx:

class Spam:
    def __new__(cls, x, y):
        pass


if __name__ == '__main__':
    assert Spam.__new__.__annotations__ == {}
    obj = Spam.__dict__['__new__']
    assert isinstance(obj, staticmethod)
    getattr(obj, '__annotations__', None)  # segfault here
    try:
        obj.__annotations__  # and here (absent the above)
    except AttributeError:
        pass
    print('no segfault!')

Whilst checking __annotations__ directly is somewhat convoluted, it was originally triggered by inspect.isasyncgenfunction(), which calls inspect._signature_is_functionlike() via _has_code_flag().

A similar error occurs with __class_getitem__ and __init_subclass__, but not with user-defined methdods declared with either classmethod or staticmethod. As the reproducer demonstrates, the segfault only happens with the descriptor object from __dict__ -- cls.__new__.__annotations__ is fine.

Bisection showed this to be due to d28afd3 (#119864), cc @JelleZijlstra

d28afd3fa064db10a2eb2a65bba33e8ea77a8fcf is the first bad commit
commit d28afd3fa064db10a2eb2a65bba33e8ea77a8fcf
Author: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Date:   Fri May 31 14:05:51 2024 -0700

    gh-119180: Lazily wrap annotations on classmethod and staticmethod (#119864)

A

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

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

Python 3.14.0a0 (bisect/bad:d28afd3fa06, Oct 6 2024, 01:35:23) [GCC 13.2.0]

Linked PRs

Activity

  1. added
    interpreter-core(Objects, Python, Grammar, and Parser dirs)
    type-crashA hard crash of the interpreter, possibly with a core dump
    3.14bugs and security fixes
    on Oct 6, 2024
  2. self-assigned this
    on Oct 6, 2024
  3. ZeroIntensity commented on Oct 6, 2024

    @ZeroIntensity
    Member

    @JelleZijlstra I took a quick glance at the implementation and PR, and I think this is a matter of forgetting to initialize sm_dict and cm_dict in constructors. IIRC, if tp_dictoffset is used, the dictionary won't get created until someone tries to access __dict__, so here it ends up being NULL, and thus segfaulting.

  4. JelleZijlstra commented on Oct 17, 2024

    @JelleZijlstra
    Member

    Thanks @ZeroIntensity, I agree that's the right diagnosis. Found a related crash too:

    Python 3.14.0a1+ (heads/main-dirty:a5a7f5e16d8, Oct 16 2024, 22:38:43) [Clang 15.0.0 (clang-1500.3.9.4)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> @classmethod
    ... def f(): pass
    ... 
    >>> del f.__annotations__
    Assertion failed: (!_PyErr_Occurred(tstate)), function _PyEval_EvalFrameDefault, file generated_cases.c.h, line 5059.
    Abort trap: 6
    
  5. added 2 commits that reference this issue on Oct 17, 2024
  6. added 2 commits that reference this issue on Oct 17, 2024
  7. added 2 commits that reference this issue on Jan 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

3.14bugs 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