From dd5e2a9fc5e0463e41b1ace54d9e5abbc5b4b23a Mon Sep 17 00:00:00 2001 From: "T. Wouters" Date: Sun, 12 Nov 2023 01:03:34 +0100 Subject: [PATCH 1/2] gh-111777: Fix assertion errors on incorrectly still-tracked GC object destruction (GH-111778) In PyObject_GC_Del, in Py_DEBUG mode, when warning about GC objects that were not properly untracked before starting destruction, take care to untrack the object _before_ warning, to avoid triggering a GC run and causing the problem the code tries to warn about. Also make sure to save and restore any pending exceptions, which the warning would otherwise clobber or trigger an assertion error on. (cherry picked from commit ce6a533c4bf1afa3775dfcaee5fc7d5c15a4af8c) Co-authored-by: T. Wouters --- Modules/gcmodule.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index dcd46feff0cc48..7397137b464771 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2347,14 +2347,16 @@ PyObject_GC_Del(void *op) size_t presize = _PyType_PreHeaderSize(((PyObject *)op)->ob_type); PyGC_Head *g = AS_GC(op); if (_PyObject_GC_IS_TRACKED(op)) { + gc_list_remove(g); #ifdef Py_DEBUG + PyObject *exc = PyErr_GetRaisedException(); if (PyErr_WarnExplicitFormat(PyExc_ResourceWarning, "gc", 0, "gc", NULL, "Object of type %s is not untracked before destruction", ((PyObject*)op)->ob_type->tp_name)) { PyErr_WriteUnraisable(NULL); } + PyErr_SetRaisedException(exc); #endif - gc_list_remove(g); } GCState *gcstate = get_gc_state(); if (gcstate->generations[0].count > 0) { From 3a1471c17f349224de7bc433906b9736b285e6f5 Mon Sep 17 00:00:00 2001 From: "T. Wouters" Date: Sun, 12 Nov 2023 16:10:41 +0100 Subject: [PATCH 2/2] Update gcmodule.c --- Modules/gcmodule.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 7397137b464771..347ded80b7c18f 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2349,13 +2349,15 @@ PyObject_GC_Del(void *op) if (_PyObject_GC_IS_TRACKED(op)) { gc_list_remove(g); #ifdef Py_DEBUG - PyObject *exc = PyErr_GetRaisedException(); + PyObject *exc, *exc_value, *exc_tb; + PyErr_Fetch(&exc, &exc_value, &exc_tb); if (PyErr_WarnExplicitFormat(PyExc_ResourceWarning, "gc", 0, "gc", NULL, "Object of type %s is not untracked before destruction", ((PyObject*)op)->ob_type->tp_name)) { PyErr_WriteUnraisable(NULL); } - PyErr_SetRaisedException(exc); + if (exc != NULL) + PyErr_Restore(exc, exc_value, exc_tb); #endif } GCState *gcstate = get_gc_state();