diff options
author | Neil Schemenauer <nas-github@arctrix.com> | 2019-09-30 17:06:45 (GMT) |
---|---|---|
committer | Ćukasz Langa <lukasz@langa.pl> | 2019-09-30 17:06:45 (GMT) |
commit | bcda460baf25062ab68622b3f043f52b9db4d21d (patch) | |
tree | 91ef277ecc2096afafd441f2e1e04eef4402e16e /Modules/gcmodule.c | |
parent | c9a413ede47171a224c72dd34122005170caaad4 (diff) | |
download | cpython-bcda460baf25062ab68622b3f043f52b9db4d21d.zip cpython-bcda460baf25062ab68622b3f043f52b9db4d21d.tar.gz cpython-bcda460baf25062ab68622b3f043f52b9db4d21d.tar.bz2 |
Clear weakrefs in garbage found by the GC (#16495)
Fix a bug due to the interaction of weakrefs and the cyclic garbage
collector. We must clear any weakrefs in garbage in order to prevent
their callbacks from executing and causing a crash.
Diffstat (limited to 'Modules/gcmodule.c')
-rw-r--r-- | Modules/gcmodule.c | 17 |
1 files changed, 17 insertions, 0 deletions
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index f6e841d..b95f676 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -678,6 +678,21 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old) op = FROM_GC(gc); next = GC_NEXT(gc); + if (PyWeakref_Check(op)) { + /* A weakref inside the unreachable set must be cleared. If we + * allow its callback to execute inside delete_garbage(), it + * could expose objects that have tp_clear already called on + * them. Or, it could resurrect unreachable objects. One way + * this can happen is if some container objects do not implement + * tp_traverse. Then, wr_object can be outside the unreachable + * set but can be deallocated as a result of breaking the + * reference cycle. If we don't clear the weakref, the callback + * will run and potentially cause a crash. See bpo-38006 for + * one example. + */ + _PyWeakref_ClearRef((PyWeakReference *)op); + } + if (! PyType_SUPPORTS_WEAKREFS(Py_TYPE(op))) continue; @@ -733,6 +748,8 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old) * is moved to wrcb_to_call in this case. */ if (gc_is_collecting(AS_GC(wr))) { + /* it should already have been cleared above */ + assert(wr->wr_object == Py_None); continue; } |