@@ -67,6 +67,10 @@ struct collection_state {
6767 PyInterpreterState * interp ;
6868 GCState * gcstate ;
6969 _PyGC_Reason reason ;
70+ // GH-129236: If we see an active frame without a valid stack pointer,
71+ // we can't collect objects with deferred references because we may not
72+ // see all references.
73+ int skip_deferred_objects ;
7074 Py_ssize_t collected ;
7175 Py_ssize_t uncollectable ;
7276 Py_ssize_t long_lived_total ;
@@ -413,9 +417,6 @@ gc_visit_heaps(PyInterpreterState *interp, mi_block_visit_fun *visitor,
413417static inline void
414418gc_visit_stackref (_PyStackRef stackref )
415419{
416- // Note: we MUST check that it is deferred before checking the rest.
417- // Otherwise we might read into invalid memory due to non-deferred references
418- // being dead already.
419420 if (PyStackRef_IsDeferred (stackref ) && !PyStackRef_IsNull (stackref )) {
420421 PyObject * obj = PyStackRef_AsPyObjectBorrow (stackref );
421422 if (_PyObject_GC_IS_TRACKED (obj ) && !gc_is_frozen (obj )) {
@@ -426,20 +427,27 @@ gc_visit_stackref(_PyStackRef stackref)
426427
427428// Add 1 to the gc_refs for every deferred reference on each thread's stack.
428429static void
429- gc_visit_thread_stacks (PyInterpreterState * interp )
430+ gc_visit_thread_stacks (PyInterpreterState * interp , struct collection_state * state )
430431{
431432 _Py_FOR_EACH_TSTATE_BEGIN (interp , p ) {
432433 for (_PyInterpreterFrame * f = p -> current_frame ; f != NULL ; f = f -> previous ) {
433- PyObject * executable = PyStackRef_AsPyObjectBorrow (f -> f_executable );
434- if (executable == NULL || !PyCode_Check (executable )) {
434+ if (f -> owner >= FRAME_OWNED_BY_INTERPRETER ) {
435+ continue ;
436+ }
437+
438+ _PyStackRef * top = f -> stackpointer ;
439+ if (top == NULL ) {
440+ // GH-129236: The stackpointer may be NULL in cases where
441+ // the GC is run during a PyStackRef_CLOSE() call. Skip this
442+ // frame and don't collect objects with deferred references.
443+ state -> skip_deferred_objects = 1 ;
435444 continue ;
436445 }
437446
438- PyCodeObject * co = (PyCodeObject * )executable ;
439- int max_stack = co -> co_nlocalsplus + co -> co_stacksize ;
440447 gc_visit_stackref (f -> f_executable );
441- for (int i = 0 ; i < max_stack ; i ++ ) {
442- gc_visit_stackref (f -> localsplus [i ]);
448+ while (top != f -> localsplus ) {
449+ -- top ;
450+ gc_visit_stackref (* top );
443451 }
444452 }
445453 }
@@ -519,10 +527,7 @@ gc_abort_mark_alive(PyInterpreterState *interp,
519527static int
520528gc_visit_stackref_mark_alive (_PyObjectStack * stack , _PyStackRef stackref )
521529{
522- // Note: we MUST check that it is deferred before checking the rest.
523- // Otherwise we might read into invalid memory due to non-deferred references
524- // being dead already.
525- if (PyStackRef_IsDeferred (stackref ) && !PyStackRef_IsNull (stackref )) {
530+ if (!PyStackRef_IsNull (stackref )) {
526531 PyObject * op = PyStackRef_AsPyObjectBorrow (stackref );
527532 if (mark_alive_stack_push (op , stack ) < 0 ) {
528533 return -1 ;
@@ -534,27 +539,37 @@ gc_visit_stackref_mark_alive(_PyObjectStack *stack, _PyStackRef stackref)
534539static int
535540gc_visit_thread_stacks_mark_alive (PyInterpreterState * interp , _PyObjectStack * stack )
536541{
542+ int err = 0 ;
537543 _Py_FOR_EACH_TSTATE_BEGIN (interp , p ) {
538544 for (_PyInterpreterFrame * f = p -> current_frame ; f != NULL ; f = f -> previous ) {
539- PyObject * executable = PyStackRef_AsPyObjectBorrow (f -> f_executable );
540- if (executable == NULL || !PyCode_Check (executable )) {
545+ if (f -> owner >= FRAME_OWNED_BY_INTERPRETER ) {
541546 continue ;
542547 }
543548
544- PyCodeObject * co = (PyCodeObject * )executable ;
545- int max_stack = co -> co_nlocalsplus + co -> co_stacksize ;
549+ if (f -> stackpointer == NULL ) {
550+ // GH-129236: The stackpointer may be NULL in cases where
551+ // the GC is run during a PyStackRef_CLOSE() call. Skip this
552+ // frame for now.
553+ continue ;
554+ }
555+
556+ _PyStackRef * top = f -> stackpointer ;
546557 if (gc_visit_stackref_mark_alive (stack , f -> f_executable ) < 0 ) {
547- return -1 ;
558+ err = -1 ;
559+ goto exit ;
548560 }
549- for (int i = 0 ; i < max_stack ; i ++ ) {
550- if (gc_visit_stackref_mark_alive (stack , f -> localsplus [i ]) < 0 ) {
551- return -1 ;
561+ while (top != f -> localsplus ) {
562+ -- top ;
563+ if (gc_visit_stackref_mark_alive (stack , * top ) < 0 ) {
564+ err = -1 ;
565+ goto exit ;
552566 }
553567 }
554568 }
555569 }
570+ exit :
556571 _Py_FOR_EACH_TSTATE_END (interp );
557- return 0 ;
572+ return err ;
558573}
559574#endif // GC_MARK_ALIVE_STACKS
560575#endif // GC_ENABLE_MARK_ALIVE
@@ -789,14 +804,23 @@ mark_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area,
789804 return true;
790805 }
791806
792- if (gc_is_alive (op )) {
807+ _PyObject_ASSERT_WITH_MSG (op , gc_get_refs (op ) >= 0 ,
808+ "refcount is too small" );
809+
810+ if (gc_is_alive (op ) || !gc_is_unreachable (op )) {
811+ // Object was already marked as reachable.
793812 return true;
794813 }
795814
796- _PyObject_ASSERT_WITH_MSG (op , gc_get_refs (op ) >= 0 ,
797- "refcount is too small" );
815+ // GH-129236: If we've seen an active frame without a valid stack pointer,
816+ // then we can't collect objects with deferred references because we may
817+ // have missed some reference to the object on the stack. In that case,
818+ // treat the object as reachable even if gc_refs is zero.
819+ struct collection_state * state = (struct collection_state * )args ;
820+ int keep_alive = (state -> skip_deferred_objects &&
821+ _PyObject_HasDeferredRefcount (op ));
798822
799- if (gc_is_unreachable ( op ) && gc_get_refs (op ) != 0 ) {
823+ if (gc_get_refs (op ) != 0 || keep_alive ) {
800824 // Object is reachable but currently marked as unreachable.
801825 // Mark it as reachable and traverse its pointers to find
802826 // any other object that may be directly reachable from it.
@@ -985,7 +1009,7 @@ deduce_unreachable_heap(PyInterpreterState *interp,
9851009#endif
9861010
9871011 // Visit the thread stacks to account for any deferred references.
988- gc_visit_thread_stacks (interp );
1012+ gc_visit_thread_stacks (interp , state );
9891013
9901014 // Transitively mark reachable objects by clearing the
9911015 // _PyGC_BITS_UNREACHABLE flag.
0 commit comments