]> granicus.if.org Git - python/commitdiff
Clear weakrefs in garbage found by the GC (#16495)
authorNeil Schemenauer <nas-github@arctrix.com>
Mon, 30 Sep 2019 17:06:45 +0000 (10:06 -0700)
committerŁukasz Langa <lukasz@langa.pl>
Mon, 30 Sep 2019 17:06:45 +0000 (19:06 +0200)
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.

Misc/NEWS.d/next/Core and Builtins/2019-09-30-09-33-21.bpo-38006.UYlJum.rst [new file with mode: 0644]
Modules/gcmodule.c

diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-09-30-09-33-21.bpo-38006.UYlJum.rst b/Misc/NEWS.d/next/Core and Builtins/2019-09-30-09-33-21.bpo-38006.UYlJum.rst
new file mode 100644 (file)
index 0000000..c2dafae
--- /dev/null
@@ -0,0 +1,3 @@
+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.
index f6e841d52ff90a39d94f62cd81cd83ad79997a18..b95f676663a39f508487289ca354fb302f34fadd 100644 (file)
@@ -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;
             }