diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2018-05-29 15:50:10 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-29 15:50:10 (GMT) |
commit | c4653c9bf159c3919a50f4ced32eef713e7e764e (patch) | |
tree | 4b05be57029bcd2603964a1b54ccd491df7bc44f /Modules | |
parent | a9cab433bbf02f3a1de59d14dc8f583181ffe2d5 (diff) | |
download | cpython-c4653c9bf159c3919a50f4ced32eef713e7e764e.zip cpython-c4653c9bf159c3919a50f4ced32eef713e7e764e.tar.gz cpython-c4653c9bf159c3919a50f4ced32eef713e7e764e.tar.bz2 |
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).
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/gcmodule.c | 27 |
1 files changed, 23 insertions, 4 deletions
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 4d701cb..7d23fc2 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -654,6 +654,7 @@ handle_legacy_finalizers(PyGC_Head *finalizers, PyGC_Head *old) { PyGC_Head *gc = finalizers->gc.gc_next; + assert(!PyErr_Occurred()); if (_PyRuntime.gc.garbage == NULL) { _PyRuntime.gc.garbage = PyList_New(0); if (_PyRuntime.gc.garbage == NULL) @@ -663,8 +664,10 @@ handle_legacy_finalizers(PyGC_Head *finalizers, PyGC_Head *old) PyObject *op = FROM_GC(gc); if ((_PyRuntime.gc.debug & DEBUG_SAVEALL) || has_legacy_finalizer(op)) { - if (PyList_Append(_PyRuntime.gc.garbage, op) < 0) + if (PyList_Append(_PyRuntime.gc.garbage, op) < 0) { + PyErr_Clear(); break; + } } } @@ -701,6 +704,7 @@ finalize_garbage(PyGC_Head *collectable) _PyGCHead_SET_FINALIZED(gc, 1); Py_INCREF(op); finalize(op); + assert(!PyErr_Occurred()); Py_DECREF(op); } } @@ -748,17 +752,26 @@ delete_garbage(PyGC_Head *collectable, PyGC_Head *old) { inquiry clear; + assert(!PyErr_Occurred()); while (!gc_list_is_empty(collectable)) { PyGC_Head *gc = collectable->gc.gc_next; PyObject *op = FROM_GC(gc); if (_PyRuntime.gc.debug & DEBUG_SAVEALL) { - PyList_Append(_PyRuntime.gc.garbage, op); + assert(_PyRuntime.gc.garbage != NULL); + if (PyList_Append(_PyRuntime.gc.garbage, op) < 0) { + PyErr_Clear(); + } } else { if ((clear = Py_TYPE(op)->tp_clear) != NULL) { Py_INCREF(op); - clear(op); + (void) clear(op); + if (PyErr_Occurred()) { + PySys_WriteStderr("Exception ignored in tp_clear of " + "%.50s\n", Py_TYPE(op)->tp_name); + PyErr_WriteUnraisable(NULL); + } Py_DECREF(op); } } @@ -974,6 +987,7 @@ collect(int generation, Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable, if (PyDTrace_GC_DONE_ENABLED()) PyDTrace_GC_DONE(n+m); + assert(!PyErr_Occurred()); return n+m; } @@ -987,11 +1001,12 @@ invoke_gc_callback(const char *phase, int generation, Py_ssize_t i; PyObject *info = NULL; + assert(!PyErr_Occurred()); /* we may get called very early */ if (_PyRuntime.gc.callbacks == NULL) return; /* The local variable cannot be rebound, check it for sanity */ - assert(_PyRuntime.gc.callbacks != NULL && PyList_CheckExact(_PyRuntime.gc.callbacks)); + assert(PyList_CheckExact(_PyRuntime.gc.callbacks)); if (PyList_GET_SIZE(_PyRuntime.gc.callbacks) != 0) { info = Py_BuildValue("{sisnsn}", "generation", generation, @@ -1015,6 +1030,7 @@ invoke_gc_callback(const char *phase, int generation, Py_DECREF(cb); } Py_XDECREF(info); + assert(!PyErr_Occurred()); } /* Perform garbage collection of a generation and invoke @@ -1024,9 +1040,11 @@ static Py_ssize_t collect_with_callback(int generation) { Py_ssize_t result, collected, uncollectable; + assert(!PyErr_Occurred()); invoke_gc_callback("start", generation, 0, 0); result = collect(generation, &collected, &uncollectable, 0); invoke_gc_callback("stop", generation, collected, uncollectable); + assert(!PyErr_Occurred()); return result; } @@ -1592,6 +1610,7 @@ _PyGC_CollectNoFail(void) { Py_ssize_t n; + assert(!PyErr_Occurred()); /* Ideally, this function is only called on interpreter shutdown, and therefore not recursively. Unfortunately, when there are daemon threads, a daemon thread can start a cyclic garbage collection |