summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_weakref.py20
-rw-r--r--Misc/NEWS6
-rw-r--r--Objects/typeobject.c7
3 files changed, 32 insertions, 1 deletions
diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py
index 35ab77f..9149318 100644
--- a/Lib/test/test_weakref.py
+++ b/Lib/test/test_weakref.py
@@ -298,6 +298,26 @@ class ReferencesTestCase(TestBase):
else:
self.fail("exception not properly restored")
+ def test_sf_bug_840829(self):
+ # "weakref callbacks and gc corrupt memory"
+ # subtype_dealloc erroneously exposed a new-style instance
+ # already in the process of getting deallocated to gc,
+ # causing double-deallocation if the instance had a weakref
+ # callback that triggered gc.
+ # If the bug exists, there probably won't be an obvious symptom
+ # in a release build. In a debug build, a segfault will occur
+ # when the second attempt to remove the instance from the "list
+ # of all objects" occurs.
+
+ import gc
+
+ class C(object):
+ pass
+
+ c = C()
+ wr = weakref.ref(c, lambda ignore: gc.collect())
+ del c
+
class Object:
def __init__(self, arg):
diff --git a/Misc/NEWS b/Misc/NEWS
index ff1e426..17e7098 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,12 @@ What's New in Python 2.4 alpha 1?
Core and builtins
-----------------
+- Critical bugfix, for SF bug 840829: if cyclic garbage collection
+ happened to occur during a weakref callback for a new-style class
+ instance, subtle memory corruption was the result (in a release build;
+ in a debug build, a segfault occurred reliably very soon after).
+ This has been repaired.
+
- Added a reversed() builtin function that returns a reverse iterator
over a sequence.
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index b1c822f..bdbabf4 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -639,7 +639,11 @@ subtype_dealloc(PyObject *self)
++_PyTrash_delete_nesting;
Py_TRASHCAN_SAFE_BEGIN(self);
--_PyTrash_delete_nesting;
- _PyObject_GC_TRACK(self); /* We'll untrack for real later */
+ /* DO NOT restore GC tracking at this point. The weakref callback
+ * (if any) may trigger GC, and if self is tracked at that point,
+ * it will look like trash to GC and GC will try to delete it
+ * again. Double-deallocation is a subtle disaster.
+ */
/* Find the nearest base with a different tp_dealloc */
base = type;
@@ -654,6 +658,7 @@ subtype_dealloc(PyObject *self)
if (type->tp_weaklistoffset && !base->tp_weaklistoffset)
PyObject_ClearWeakRefs(self);
+ _PyObject_GC_TRACK(self); /* We'll untrack for real later */
/* Maybe call finalizer; exit early if resurrected */
if (type->tp_del) {