@@ -67,6 +67,10 @@ struct collection_state {
67
67
PyInterpreterState * interp ;
68
68
GCState * gcstate ;
69
69
_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 ;
70
74
Py_ssize_t collected ;
71
75
Py_ssize_t uncollectable ;
72
76
Py_ssize_t long_lived_total ;
@@ -413,9 +417,6 @@ gc_visit_heaps(PyInterpreterState *interp, mi_block_visit_fun *visitor,
413
417
static inline void
414
418
gc_visit_stackref (_PyStackRef stackref )
415
419
{
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.
419
420
if (PyStackRef_IsDeferred (stackref ) && !PyStackRef_IsNull (stackref )) {
420
421
PyObject * obj = PyStackRef_AsPyObjectBorrow (stackref );
421
422
if (_PyObject_GC_IS_TRACKED (obj ) && !gc_is_frozen (obj )) {
@@ -426,20 +427,27 @@ gc_visit_stackref(_PyStackRef stackref)
426
427
427
428
// Add 1 to the gc_refs for every deferred reference on each thread's stack.
428
429
static void
429
- gc_visit_thread_stacks (PyInterpreterState * interp )
430
+ gc_visit_thread_stacks (PyInterpreterState * interp , struct collection_state * state )
430
431
{
431
432
_Py_FOR_EACH_TSTATE_BEGIN (interp , p ) {
432
433
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 ;
435
444
continue ;
436
445
}
437
446
438
- PyCodeObject * co = (PyCodeObject * )executable ;
439
- int max_stack = co -> co_nlocalsplus + co -> co_stacksize ;
440
447
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 );
443
451
}
444
452
}
445
453
}
@@ -519,10 +527,7 @@ gc_abort_mark_alive(PyInterpreterState *interp,
519
527
static int
520
528
gc_visit_stackref_mark_alive (_PyObjectStack * stack , _PyStackRef stackref )
521
529
{
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 )) {
526
531
PyObject * op = PyStackRef_AsPyObjectBorrow (stackref );
527
532
if (mark_alive_stack_push (op , stack ) < 0 ) {
528
533
return -1 ;
@@ -534,27 +539,37 @@ gc_visit_stackref_mark_alive(_PyObjectStack *stack, _PyStackRef stackref)
534
539
static int
535
540
gc_visit_thread_stacks_mark_alive (PyInterpreterState * interp , _PyObjectStack * stack )
536
541
{
542
+ int err = 0 ;
537
543
_Py_FOR_EACH_TSTATE_BEGIN (interp , p ) {
538
544
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 ) {
541
546
continue ;
542
547
}
543
548
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 ;
546
557
if (gc_visit_stackref_mark_alive (stack , f -> f_executable ) < 0 ) {
547
- return -1 ;
558
+ err = -1 ;
559
+ goto exit ;
548
560
}
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 ;
552
566
}
553
567
}
554
568
}
555
569
}
570
+ exit :
556
571
_Py_FOR_EACH_TSTATE_END (interp );
557
- return 0 ;
572
+ return err ;
558
573
}
559
574
#endif // GC_MARK_ALIVE_STACKS
560
575
#endif // GC_ENABLE_MARK_ALIVE
@@ -789,14 +804,23 @@ mark_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area,
789
804
return true;
790
805
}
791
806
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.
793
812
return true;
794
813
}
795
814
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 ));
798
822
799
- if (gc_is_unreachable ( op ) && gc_get_refs (op ) != 0 ) {
823
+ if (gc_get_refs (op ) != 0 || keep_alive ) {
800
824
// Object is reachable but currently marked as unreachable.
801
825
// Mark it as reachable and traverse its pointers to find
802
826
// any other object that may be directly reachable from it.
@@ -985,7 +1009,7 @@ deduce_unreachable_heap(PyInterpreterState *interp,
985
1009
#endif
986
1010
987
1011
// Visit the thread stacks to account for any deferred references.
988
- gc_visit_thread_stacks (interp );
1012
+ gc_visit_thread_stacks (interp , state );
989
1013
990
1014
// Transitively mark reachable objects by clearing the
991
1015
// _PyGC_BITS_UNREACHABLE flag.
0 commit comments