Visitar URL original
Segmentation fault, possibly due to a GC issue (tp_subclasses) · Issue #135552 · python/cpython · GitHub
Skip to content

Segmentation fault, possibly due to a GC issue (tp_subclasses) #135552

Description

@fxeqxmulfx

Crash report

What happened?

from typing import Union


class BaseNode:
    next: Union["BaseNode", None] = None

    @staticmethod
    def add(node: "BaseNode") -> None:
        if BaseNode.next is None:
            BaseNode.next = node
            return
        current = BaseNode.next
        while current.next is not None:
            current = current.next
        current.next = node

    @staticmethod
    def remove(node: "BaseNode") -> None:
        if BaseNode.next is None:
            return
        current = BaseNode.next
        prev = BaseNode
        while True:
            if current is None:
                return
            if current == node:
                if current.next is not None:
                    prev.next = current.next
                else:
                    prev.next = None
                return
            prev = current
            current = current.next


class Node(BaseNode):
    def __init__(self) -> None:
        self.next = None
        BaseNode.add(self)

    def __del__(self) -> None:
        BaseNode.remove(self)


def main() -> None:
    Node()
    Node()


if __name__ == "__main__":
    main()

(Edited by @ZeroIntensity) Shortened repro:

class BaseNode:
    next = None


class Node(BaseNode):
    def __init__(self) -> None:
        if BaseNode.next is None:
            BaseNode.next = self
            return
        BaseNode.next.next = self

    def __del__(self) -> None:
        BaseNode.next = BaseNode.next.next


Node()
Node()

(Edited by @fxeqxmulfx) Use after free example:

class BaseNode:
    def __del__(self):
        print("next", BaseNode.next)
        print("del", self)


BaseNode.next = BaseNode()
BaseNode.next.next = BaseNode()
next <__main__.BaseNode object at 0x717fb2d23a10>
del <__main__.BaseNode object at 0x717fb2d23a10>
next <__main__.BaseNode object at 0x717fb2d23a10> use after free
del <__main__.BaseNode object at 0x717fb2d23a50>

CPython versions tested on:

3.14

Operating systems tested on:

Linux

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

Python 3.14.0b2 (main, Jun 12 2025, 12:41:01) [Clang 20.1.4 ]

Linked PRs

Activity

  1. added
    type-crashA hard crash of the interpreter, possibly with a core dump
    on Jun 16, 2025
  2. fxeqxmulfx commented on Jun 16, 2025

    @fxeqxmulfx
    ContributorAuthor

    This also affects:

    • Python 3.9.23 (main, Jun 12 2025, 12:39:23) [Clang 20.1.4 ]
    • Python 3.10.16 (main, Dec 19 2024, 14:33:27) [Clang 18.1.8 ]
    • Python 3.11.11 (main, Dec 19 2024, 14:33:27) [Clang 18.1.8 ]
    • Python 3.12.8 (main, Dec 19 2024, 14:33:20) [Clang 18.1.8 ]
    • Python 3.13.5 (main, Jun 12 2025, 12:40:22) [Clang 20.1.4 ]
  3. ZeroIntensity commented on Jun 16, 2025

    @ZeroIntensity
    Member

    Confirmed on main back to 3.13, but interestingly, this doesn't affect free-threading. Given that this is caused by __del__, this is probably one of those weird bugs where a borrowed reference is cleared. Thanks for the report.

  4. added
    interpreter-core(Objects, Python, Grammar, and Parser dirs)
    3.13only security fixes
    3.14bugs and security fixes
    3.15bugs and security fixes
    and removed on Jun 16, 2025
  5. ZeroIntensity commented on Jun 16, 2025

    @ZeroIntensity
    Member

    (Nevermind, not related to typing.)

  6. ZeroIntensity commented on Jun 16, 2025

    @ZeroIntensity
    Member

    Shortened repro:

    class BaseNode:
        next = None
    
    
    class Node(BaseNode):
        def __init__(self) -> None:
            if BaseNode.next is None:
                BaseNode.next = self
                return
            BaseNode.next.next = self
    
        def __del__(self) -> None:
            BaseNode.next = BaseNode.next.next
    
    
    Node()
    Node()

    Looks like a garbage collection problem.

  7. vstinner commented on Jun 16, 2025

    @vstinner
    Member

    Python 3.14 trace:

    (gdb) run
    
    Program received signal SIGSEGV, Segmentation fault.
    0x00000000005a77b2 in _Py_TYPE (ob=0x0) at ./Include/object.h:277
    277	        return ob->ob_type;
    
    (gdb) frame 4
    #4  0x00000000005ad556 in PyObject_ClearWeakRefs (object=<Node() at remote 0x7fffe9c5c6e0>) at Objects/weakrefobject.c:1060
    1060	            clear_weakref_lock_held(cur, &callback);
    
    (gdb) p cur->wr_object
    $1 = 0x0
    (gdb) p *cur
    $2 = {
      ob_base = {
        {
          ob_refcnt_full = 0,
          {
            ob_refcnt = 0,
            ob_overflow = 0,
            ob_flags = 0
          },
          _aligner = 0 '\000'
        },
        ob_type = 0x0
      },
      wr_object = 0x0,
      wr_callback = 0x0,
      hash = 0,
      wr_prev = 0x0,
      wr_next = 0x0,
      vectorcall = 0x0
    }
    
    
    (gdb) where
    #0  0x00000000005a77b2 in _Py_TYPE (ob=0x0) at ./Include/object.h:277
    #1  0x00000000005a787d in PyType_Check (op=0x0) at ./Include/object.h:805
    #2  0x00000000005a7a88 in _PyObject_GET_WEAKREFS_LISTPTR (op=0x0) at ./Include/internal/pycore_object.h:795
    #3  0x00000000005a7cff in clear_weakref_lock_held (self=0x7fffe9c5c810, callback=0x7fffffff92d8) at Objects/weakrefobject.c:82
    #4  0x00000000005ad556 in PyObject_ClearWeakRefs (object=<Node() at remote 0x7fffe9c5c6e0>) at Objects/weakrefobject.c:1060
    #5  0x000000000053c0d0 in subtype_dealloc (self=<Node() at remote 0x7fffe9c5c6e0>) at Objects/typeobject.c:2756
    #6  0x0000000000508baa in _Py_Dealloc (op=<Node() at remote 0x7fffe9c5c6e0>) at Objects/object.c:3195
    #7  0x00000000006f6b5c in Py_DECREF (op=<Node() at remote 0x7fffe9c5c6e0>) at ./Include/refcount.h:430
    #8  Py_XDECREF (op=<Node() at remote 0x7fffe9c5c6e0>) at ./Include/refcount.h:523
    #9  0x00000000006f80e0 in specialize_instance_load_attr (owner=<Node() at remote 0x7fffe9c74690>, instr=0x7fffe9c48360, name='next')
        at Python/specialize.c:1345
    #10 0x00000000006f81ba in _Py_Specialize_LoadAttr (owner_st=<Node() at remote 0x7fffe9c74690>, instr=0x7fffe9c48360, name='next')
        at Python/specialize.c:1372
    #11 0x00000000006470ed in _PyEval_EvalFrameDefault (tstate=0xa5ed20 <_PyRuntime+315232>, frame=0x7ffff7fb2020, throwflag=0)
        at Python/generated_cases.c.h:7974
    #12 0x000000000062fd02 in _PyEval_EvalFrame (tstate=0xa5ed20 <_PyRuntime+315232>, frame=0x7ffff7fb2020, throwflag=0)
        at ./Include/internal/pycore_ceval.h:119
    #13 0x0000000000654710 in _PyEval_Vector (tstate=0xa5ed20 <_PyRuntime+315232>, func=0x7fffe9c731c0, locals=0x0, args=0x7fffffffd7f8, 
        argcount=1, kwnames=0x0) at Python/ceval.c:1975
    #14 0x000000000048db1b in _PyFunction_Vectorcall (func=<function at remote 0x7fffe9c731c0>, stack=0x7fffffffd7f8, 
        nargsf=9223372036854775809, kwnames=0x0) at Objects/call.c:413
    #15 0x000000000048ccb7 in _PyObject_VectorcallTstate (tstate=0xa5ed20 <_PyRuntime+315232>, callable=<function at remote 0x7fffe9c731c0>, 
        args=0x7fffffffd7f8, nargsf=9223372036854775809, kwnames=0x0) at ./Include/internal/pycore_call.h:169
    #16 0x000000000048daa4 in PyObject_CallOneArg (func=<function at remote 0x7fffe9c731c0>, arg=<Node() at remote 0x7fffe9c74690>)
        at Objects/call.c:395
    #17 0x000000000053c8cd in call_unbound_noarg (unbound=1, func=<function at remote 0x7fffe9c731c0>, 
        self=<Node() at remote 0x7fffe9c74690>) at Objects/typeobject.c:3006
    #18 0x0000000000550057 in slot_tp_finalize (self=<Node() at remote 0x7fffe9c74690>) at Objects/typeobject.c:10764
    #19 0x00000000006a43d6 in finalize_garbage (tstate=0xa5ed20 <_PyRuntime+315232>, collectable=0x7fffffffd970) at Python/gc.c:1106
    #20 0x00000000006a57d2 in gc_collect_region (tstate=0xa5ed20 <_PyRuntime+315232>, from=0xa29bb0 <_PyRuntime+97776>, 
        to=0xa29bb0 <_PyRuntime+97776>, stats=0x7fffffffda00) at Python/gc.c:1746
    #21 0x00000000006a5654 in gc_collect_full (tstate=0xa5ed20 <_PyRuntime+315232>, stats=0x7fffffffda00) at Python/gc.c:1679
    #22 0x00000000006a622f in _PyGC_Collect (tstate=0xa5ed20 <_PyRuntime+315232>, generation=2, reason=_Py_GC_REASON_SHUTDOWN)
        at Python/gc.c:2041
    #23 0x00000000006a62e2 in _PyGC_CollectNoFail (tstate=0xa5ed20 <_PyRuntime+315232>) at Python/gc.c:2082
    #24 0x00000000006e7bf2 in finalize_modules (tstate=0xa5ed20 <_PyRuntime+315232>) at Python/pylifecycle.c:1740
    #25 0x00000000006e8209 in _Py_Finalize (runtime=0xa11dc0 <_PyRuntime>) at Python/pylifecycle.c:2125
    #26 0x00000000006e82a8 in Py_FinalizeEx () at Python/pylifecycle.c:2251
    #27 0x000000000072e0ac in Py_RunMain () at Modules/main.c:774
    #28 0x000000000072e13b in pymain_main (args=0x7fffffffdb80) at Modules/main.c:802
    #29 0x000000000072e1b5 in Py_BytesMain (argc=2, argv=0x7fffffffdce8) at Modules/main.c:826
    #30 0x0000000000401d16 in main (argc=2, argv=0x7fffffffdce8) at ./Programs/python.c:15
    

    Python 3.10 trace:

    (gdb) run
    
    Program received signal SIGSEGV, Segmentation fault.
    _PyObject_GenericGetAttrWithDict (obj=<Node at remote 0x7fffe9ccf0c0>, name='next', dict=0x0, suppress=0) at Objects/object.c:1252
    1252	        f = Py_TYPE(descr)->tp_descr_get;
    
    (gdb) p /x *descr
    $4 = {
      ob_refcnt = 0xddddddddddddddde,
      ob_type = 0xdddddddddddddddd
    }
    
    
    (gdb) where
    #0  _PyObject_GenericGetAttrWithDict (obj=<Node at remote 0x7fffe9ccf0c0>, name='next', dict=0x0, suppress=0) at Objects/object.c:1252
    #1  0x000000000045d331 in PyObject_GenericGetAttr (obj=<Node at remote 0x7fffe9ccf0c0>, name='next') at Objects/object.c:1335
    #2  0x000000000045c537 in PyObject_GetAttr (v=<Node at remote 0x7fffe9ccf0c0>, name='next') at Objects/object.c:932
    #3  0x0000000000500942 in _PyEval_EvalFrameDefault (tstate=0x8ded50, 
        f=Frame 0x7fffe9d9f2f0, for file /home/vstinner/python/3.10/x.py, line 12, in __del__ (self=<Node at remote 0x7fffe9ccf0c0>), 
        throwflag=0) at Python/ceval.c:3592
    #4  0x00000000004f3904 in _PyEval_EvalFrame (tstate=0x8ded50, 
        f=Frame 0x7fffe9d9f2f0, for file /home/vstinner/python/3.10/x.py, line 12, in __del__ (self=<Node at remote 0x7fffe9ccf0c0>), 
        throwflag=0) at ./Include/internal/pycore_ceval.h:46
    #5  0x0000000000507c82 in _PyEval_Vector (tstate=0x8ded50, con=0x7fffe9cd7290, locals=0x0, args=0x7fffffffd8a8, argcount=1, kwnames=0x0)
        at Python/ceval.c:5067
    #6  0x0000000000416cd8 in _PyFunction_Vectorcall (func=<function at remote 0x7fffe9cd7280>, stack=0x7fffffffd8a8, 
        nargsf=9223372036854775809, kwnames=0x0) at Objects/call.c:342
    #7  0x000000000047401f in _PyObject_VectorcallTstate (tstate=0x8ded50, callable=<function at remote 0x7fffe9cd7280>, 
        args=0x7fffffffd8a8, nargsf=9223372036854775809, kwnames=0x0) at ./Include/cpython/abstract.h:114
    #8  0x0000000000474160 in PyObject_CallOneArg (func=<function at remote 0x7fffe9cd7280>, arg=<Node at remote 0x7fffe9ccf0c0>)
        at ./Include/cpython/abstract.h:184
    #9  0x0000000000477b69 in call_unbound_noarg (unbound=1, func=<function at remote 0x7fffe9cd7280>, self=<Node at remote 0x7fffe9ccf0c0>)
        at Objects/typeobject.c:1636
    #10 0x00000000004882b5 in slot_tp_finalize (self=<Node at remote 0x7fffe9ccf0c0>) at Objects/typeobject.c:7783
    #11 0x00000000005895ca in finalize_garbage (tstate=0x8ded50, collectable=0x7fffffffd9f0) at Modules/gcmodule.c:982
    #12 0x0000000000589dc8 in gc_collect_main (tstate=0x8ded50, generation=2, n_collected=0x0, n_uncollectable=0x0, nofail=1)
        at Modules/gcmodule.c:1287
    #13 0x000000000058b696 in _PyGC_CollectNoFail (tstate=0x8ded50) at Modules/gcmodule.c:2123
    #14 0x0000000000555bca in finalize_modules (tstate=0x8ded50) at Python/pylifecycle.c:1525
    #15 0x000000000055600d in Py_FinalizeEx () at Python/pylifecycle.c:1784
    #16 0x0000000000403eac in Py_RunMain () at Modules/main.c:672
    #17 0x0000000000403f3b in pymain_main (args=0x7fffffffdb80) at Modules/main.c:700
    #18 0x0000000000403fb5 in Py_BytesMain (argc=2, argv=0x7fffffffdce8) at Modules/main.c:724
    #19 0x00000000004027f6 in main (argc=2, argv=0x7fffffffdce8) at ./Programs/python.c:15
    
  8. efimov-mikhail commented on Jun 16, 2025

    @efimov-mikhail
    Member
  9. Eclips4 commented on Jun 17, 2025

    @Eclips4
    Member

    FYI, this code still crashes even with the gc.disable() in the beginning of the code

  10. ZeroIntensity commented on Jun 17, 2025

    @ZeroIntensity
    Member

    That won't fully fix it, the GC still runs at some point (such as finalization). gc.freeze() does indeed prevent the crash.

    I suspect this is an issue with the garbage collector itself (and not the object model), because this doesn't crash under free-threading, where there's a different garbage collector.

  11. 18 remaining items

  12. changed the title [-]Segmentation fault, possibly due to a GC issue[/-] [+]Segmentation fault, possibly due to a GC issue (tp_subclasses)[/+] on Jul 1, 2025
  13. nascheme commented on Jul 1, 2025

    @nascheme
    Member

    Not clearing the weakrefs would re-introduce a bug like bpo-38006 (see commit 392a13b for a unit test). I was mistakenly thinking that with PEP 442 we didn't have to worry about finalizers running during the delete_garbage() phase. That's not true. They can run if some objects are missing tp_traverse methods or those methods are not accurate. We need to clear weakrefs so those finalizers cannot access an object that has tp_clear called on it.

  14. added a commit that references this issue on Jul 23, 2025
  15. added 5 commits that reference this issue on Aug 6, 2025
  16. sergey-miryanov commented on Aug 8, 2025

    @sergey-miryanov
    Contributor

    This can be closed now.

  17. added 2 commits that reference this issue on Aug 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.13only security fixes3.14bugs and security fixes3.15bugs 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