diff options
-rw-r--r-- | Lib/test/test_weakref.py | 20 | ||||
-rw-r--r-- | Misc/NEWS | 6 | ||||
-rw-r--r-- | Objects/typeobject.c | 7 |
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): @@ -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) { |