If you created a weakref in an object's __del__ method to itself it would
segfault the interpreter during weakref clean up. Now any new weakrefs created
after __del__ is run are removed silently.
Fixes bug #1377858 and the weakref_in_del crasher for new-style classes.
Classic classes are still affected.
diff --git a/Lib/test/crashers/weakref_in_del.py b/Lib/test/crashers/weakref_in_del.py
index 3bbcfc3..2e9b186 100644
--- a/Lib/test/crashers/weakref_in_del.py
+++ b/Lib/test/crashers/weakref_in_del.py
@@ -1,11 +1,12 @@
import weakref
# http://python.org/sf/1377858
+# Fixed for new-style classes in 2.5c1.
ref = None
def test_weakref_in_del():
- class Target(object):
+ class Target():
def __del__(self):
global ref
ref = weakref.ref(self)
diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py
index 18ab401..c669109 100644
--- a/Lib/test/test_weakref.py
+++ b/Lib/test/test_weakref.py
@@ -6,6 +6,8 @@
from test import test_support
+# Used in ReferencesTestCase.test_ref_created_during_del() .
+ref_from_del = None
class C:
def method(self):
@@ -630,6 +632,18 @@
finally:
gc.set_threshold(*thresholds)
+ def test_ref_created_during_del(self):
+ # Bug #1377858
+ # A weakref created in an object's __del__() would crash the
+ # interpreter when the weakref was cleaned up since it would refer to
+ # non-existent memory. This test should not segfault the interpreter.
+ class Target(object):
+ def __del__(self):
+ global ref_from_del
+ ref_from_del = weakref.ref(self)
+
+ w = Target()
+
class SubclassableWeakrefTestCase(unittest.TestCase):
diff --git a/Misc/NEWS b/Misc/NEWS
index fc5dd66..fccc50d 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@
Core and builtins
-----------------
+- Bug #1377858: Fix the segfaulting of the interpreter when an object created
+ a weakref on itself during a __del__ call.
+
- Bug #1579370: Make PyTraceBack_Here use the current thread, not the
frame's thread state.
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index d4a46c3..a8395ef 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -666,6 +666,17 @@
goto endlabel; /* resurrected */
else
_PyObject_GC_UNTRACK(self);
+ /* New weakrefs could be created during the finalizer call.
+ If this occurs, clear them out without calling their
+ finalizers since they might rely on part of the object
+ being finalized that has already been destroyed. */
+ if (type->tp_weaklistoffset && !base->tp_weaklistoffset) {
+ /* Modeled after GET_WEAKREFS_LISTPTR() */
+ PyWeakReference **list = (PyWeakReference **) \
+ PyObject_GET_WEAKREFS_LISTPTR(self);
+ while (*list)
+ _PyWeakref_ClearRef(*list);
+ }
}
/* Clear slots up to the nearest base with a different tp_dealloc */
diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c
index 826f571..a404f29 100644
--- a/Objects/weakrefobject.c
+++ b/Objects/weakrefobject.c
@@ -57,6 +57,9 @@
PyWeakref_GET_OBJECT(self));
if (*list == self)
+ /* If 'self' is the end of the list (and thus self->wr_next == NULL)
+ then the weakref list itself (and thus the value of *list) will
+ end up being set to NULL. */
*list = self->wr_next;
self->wr_object = Py_None;
if (self->wr_prev != NULL)