Skip to content

Commit c4653c9

Browse files
bpo-33622: Add checks for exceptions leaks in the garbage collector. (GH-7126)
* Failure in adding to gc.garbage is no longer fatal. * An exception in tp_clear() no longer lead to crash (though tp_clear() should not leave exceptions).
1 parent a9cab43 commit c4653c9

File tree

1 file changed

+23
-4
lines changed

1 file changed

+23
-4
lines changed

Modules/gcmodule.c

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,7 @@ handle_legacy_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
654654
{
655655
PyGC_Head *gc = finalizers->gc.gc_next;
656656

657+
assert(!PyErr_Occurred());
657658
if (_PyRuntime.gc.garbage == NULL) {
658659
_PyRuntime.gc.garbage = PyList_New(0);
659660
if (_PyRuntime.gc.garbage == NULL)
@@ -663,8 +664,10 @@ handle_legacy_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
663664
PyObject *op = FROM_GC(gc);
664665

665666
if ((_PyRuntime.gc.debug & DEBUG_SAVEALL) || has_legacy_finalizer(op)) {
666-
if (PyList_Append(_PyRuntime.gc.garbage, op) < 0)
667+
if (PyList_Append(_PyRuntime.gc.garbage, op) < 0) {
668+
PyErr_Clear();
667669
break;
670+
}
668671
}
669672
}
670673

@@ -701,6 +704,7 @@ finalize_garbage(PyGC_Head *collectable)
701704
_PyGCHead_SET_FINALIZED(gc, 1);
702705
Py_INCREF(op);
703706
finalize(op);
707+
assert(!PyErr_Occurred());
704708
Py_DECREF(op);
705709
}
706710
}
@@ -748,17 +752,26 @@ delete_garbage(PyGC_Head *collectable, PyGC_Head *old)
748752
{
749753
inquiry clear;
750754

755+
assert(!PyErr_Occurred());
751756
while (!gc_list_is_empty(collectable)) {
752757
PyGC_Head *gc = collectable->gc.gc_next;
753758
PyObject *op = FROM_GC(gc);
754759

755760
if (_PyRuntime.gc.debug & DEBUG_SAVEALL) {
756-
PyList_Append(_PyRuntime.gc.garbage, op);
761+
assert(_PyRuntime.gc.garbage != NULL);
762+
if (PyList_Append(_PyRuntime.gc.garbage, op) < 0) {
763+
PyErr_Clear();
764+
}
757765
}
758766
else {
759767
if ((clear = Py_TYPE(op)->tp_clear) != NULL) {
760768
Py_INCREF(op);
761-
clear(op);
769+
(void) clear(op);
770+
if (PyErr_Occurred()) {
771+
PySys_WriteStderr("Exception ignored in tp_clear of "
772+
"%.50s\n", Py_TYPE(op)->tp_name);
773+
PyErr_WriteUnraisable(NULL);
774+
}
762775
Py_DECREF(op);
763776
}
764777
}
@@ -974,6 +987,7 @@ collect(int generation, Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable,
974987
if (PyDTrace_GC_DONE_ENABLED())
975988
PyDTrace_GC_DONE(n+m);
976989

990+
assert(!PyErr_Occurred());
977991
return n+m;
978992
}
979993

@@ -987,11 +1001,12 @@ invoke_gc_callback(const char *phase, int generation,
9871001
Py_ssize_t i;
9881002
PyObject *info = NULL;
9891003

1004+
assert(!PyErr_Occurred());
9901005
/* we may get called very early */
9911006
if (_PyRuntime.gc.callbacks == NULL)
9921007
return;
9931008
/* The local variable cannot be rebound, check it for sanity */
994-
assert(_PyRuntime.gc.callbacks != NULL && PyList_CheckExact(_PyRuntime.gc.callbacks));
1009+
assert(PyList_CheckExact(_PyRuntime.gc.callbacks));
9951010
if (PyList_GET_SIZE(_PyRuntime.gc.callbacks) != 0) {
9961011
info = Py_BuildValue("{sisnsn}",
9971012
"generation", generation,
@@ -1015,6 +1030,7 @@ invoke_gc_callback(const char *phase, int generation,
10151030
Py_DECREF(cb);
10161031
}
10171032
Py_XDECREF(info);
1033+
assert(!PyErr_Occurred());
10181034
}
10191035

10201036
/* Perform garbage collection of a generation and invoke
@@ -1024,9 +1040,11 @@ static Py_ssize_t
10241040
collect_with_callback(int generation)
10251041
{
10261042
Py_ssize_t result, collected, uncollectable;
1043+
assert(!PyErr_Occurred());
10271044
invoke_gc_callback("start", generation, 0, 0);
10281045
result = collect(generation, &collected, &uncollectable, 0);
10291046
invoke_gc_callback("stop", generation, collected, uncollectable);
1047+
assert(!PyErr_Occurred());
10301048
return result;
10311049
}
10321050

@@ -1592,6 +1610,7 @@ _PyGC_CollectNoFail(void)
15921610
{
15931611
Py_ssize_t n;
15941612

1613+
assert(!PyErr_Occurred());
15951614
/* Ideally, this function is only called on interpreter shutdown,
15961615
and therefore not recursively. Unfortunately, when there are daemon
15971616
threads, a daemon thread can start a cyclic garbage collection

0 commit comments

Comments
 (0)