This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eltoder
Recipients eltoder, pitrou
Date 2012-12-04.06:50:26
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1354603828.49.0.407579608192.issue16602@psf.upfronthosting.co.za>
In-reply-to
Content
An interaction between weakrefs and trashcan can cause weakref to return the object it's pointing to after object's refcount is already 0. Given that the object is usually increfed and decrefed, this leads to double dealloc and crashing or hanging.
Tested 2.6.6, 2.7.2 and 3.3.0 on win7.

In more details. The documentation for Py_TRASHCAN_SAFE_BEGIN tells to put it right after PyObject_GC_UnTrack. This means that PyObject_ClearWeakRefs goes after it, and, for example, in subtype_dealloc of Objects/typeobject.c this is indeed the case. This means that if we get into a long chain of deallocations and the trashcan kicks in, we get an object with 0 refcount and without cleared weakrefs lying in trash_delete_later until we go PyTrash_UNWIND_LEVEL levels up. During that time we can execute arbitrary python code, so all we need is some code with an access to the weakref to dereference it.
The current recommendation of clearing weakrefs before clearing attributes makes this less likely to happen, but it's still easy enough:

import weakref

class C:
    def __init__(self, parent):
        if not parent:
            return
        wself = weakref.ref(self)
        def cb(wparent):
            o = wself()
        self.wparent = weakref.ref(parent, cb)

d = weakref.WeakKeyDictionary()
root = c = C(None)
for n in range(100):
    d[c] = c = C(c)

print('deleting')
del root
print('done')

In this case weakref callback in WeakKeyDictionary deletes the reference to the next object, causing the next level of destruction, until trashcan kicks in. Trashcan delays clearing of weakrefs, allowing the second weakref's (wparent) callback to see the dead object via wself that it captured.

Attached is a similar example with less weakrefs using __del__.
History
Date User Action Args
2012-12-04 06:50:28eltodersetrecipients: + eltoder, pitrou
2012-12-04 06:50:28eltodersetmessageid: <1354603828.49.0.407579608192.issue16602@psf.upfronthosting.co.za>
2012-12-04 06:50:28eltoderlinkissue16602 messages
2012-12-04 06:50:27eltodercreate