@@ -2174,6 +2174,13 @@ _PyGC_DumpShutdownStats(PyInterpreterState *interp)
21742174 }
21752175}
21762176
2177+ static void
2178+ finalize_unlink_gc_head (PyGC_Head * gc ) {
2179+ PyGC_Head * prev = GC_PREV (gc );
2180+ PyGC_Head * next = GC_NEXT (gc );
2181+ _PyGCHead_SET_NEXT (prev , next );
2182+ _PyGCHead_SET_PREV (next , prev );
2183+ }
21772184
21782185void
21792186_PyGC_Fini (PyInterpreterState * interp )
@@ -2182,9 +2189,25 @@ _PyGC_Fini(PyInterpreterState *interp)
21822189 Py_CLEAR (gcstate -> garbage );
21832190 Py_CLEAR (gcstate -> callbacks );
21842191
2185- /* We expect that none of this interpreters objects are shared
2186- with other interpreters.
2187- See https://github.com/python/cpython/issues/90228. */
2192+ /* Prevent a subtle bug that affects sub-interpreters that use basic
2193+ * single-phase init extensions (m_size == -1). Those extensions cause objects
2194+ * to be shared between interpreters, via the PyDict_Update(mdict, m_copy) call
2195+ * in import_find_extension().
2196+ *
2197+ * If they are GC objects, their GC head next or prev links could refer to
2198+ * the interpreter _gc_runtime_state PyGC_Head nodes. Those nodes go away
2199+ * when the interpreter structure is freed and so pointers to them become
2200+ * invalid. If those objects are still used by another interpreter and
2201+ * UNTRACK is called on them, a crash will happen. We untrack the nodes
2202+ * here to avoid that.
2203+ *
2204+ * This bug was originally fixed when reported as gh-90228. The bug was
2205+ * re-introduced in gh-94673.
2206+ */
2207+ for (int i = 0 ; i < NUM_GENERATIONS ; i ++ ) {
2208+ finalize_unlink_gc_head (& gcstate -> generations [i ].head );
2209+ }
2210+ finalize_unlink_gc_head (& gcstate -> permanent_generation .head );
21882211}
21892212
21902213/* for debugging */
0 commit comments