File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -70,7 +70,17 @@ PyAPI_FUNC(Py_ssize_t) _PyWeakref_GetWeakrefCount(PyWeakReference *head);
7070PyAPI_FUNC (void ) _PyWeakref_ClearRef (PyWeakReference * self );
7171#endif
7272
73- #define PyWeakref_GET_OBJECT (ref ) (((PyWeakReference *)(ref))->wr_object)
73+ /* Explanation for the Py_REFCNT() check: when a weakref's target is part
74+ of a long chain of deallocations which triggers the trashcan mechanism,
75+ clearing the weakrefs can be delayed long after the target's refcount
76+ has dropped to zero. In the meantime, code accessing the weakref will
77+ be able to "see" the target object even though it is supposed to be
78+ unreachable. See issue #16602. */
79+
80+ #define PyWeakref_GET_OBJECT (ref ) \
81+ (Py_REFCNT(((PyWeakReference *)(ref))->wr_object) > 0 \
82+ ? ((PyWeakReference *)(ref))->wr_object \
83+ : Py_None)
7484
7585
7686#ifdef __cplusplus
Original file line number Diff line number Diff line change @@ -776,6 +776,27 @@ def test_hashing(self):
776776 self .assertEqual (hash (a ), hash (42 ))
777777 self .assertRaises (TypeError , hash , b )
778778
779+ def test_trashcan_16602 (self ):
780+ # Issue #16602: when a weakref's target was part of a long
781+ # deallocation chain, the trashcan mechanism could delay clearing
782+ # of the weakref and make the target object visible from outside
783+ # code even though its refcount had dropped to 0. A crash ensued.
784+ class C :
785+ def __init__ (self , parent ):
786+ if not parent :
787+ return
788+ wself = weakref .ref (self )
789+ def cb (wparent ):
790+ o = wself ()
791+ self .wparent = weakref .ref (parent , cb )
792+
793+ d = weakref .WeakKeyDictionary ()
794+ root = c = C (None )
795+ for n in range (100 ):
796+ d [c ] = c = C (c )
797+ del root
798+ gc .collect ()
799+
779800
780801class SubclassableWeakrefTestCase (TestBase ):
781802
Original file line number Diff line number Diff line change @@ -12,6 +12,10 @@ What's New in Python 3.3.1?
1212Core and Builtins
1313-----------------
1414
15+ - Issue #16602: When a weakref's target was part of a long deallocation
16+ chain, the object could remain reachable through its weakref even though
17+ its refcount had dropped to zero.
18+
1519- Issue #16416: On Mac OS X, operating system data are now always
1620 encoded/decoded to/from UTF-8/surrogateescape, instead of the locale encoding
1721 (which may be ASCII if no locale environment variable is set), to avoid
Original file line number Diff line number Diff line change @@ -52,9 +52,8 @@ clear_weakref(PyWeakReference *self)
5252{
5353 PyObject * callback = self -> wr_callback ;
5454
55- if (PyWeakref_GET_OBJECT (self ) != Py_None ) {
56- PyWeakReference * * list = GET_WEAKREFS_LISTPTR (
57- PyWeakref_GET_OBJECT (self ));
55+ if (self -> wr_object != Py_None ) {
56+ PyWeakReference * * list = GET_WEAKREFS_LISTPTR (self -> wr_object );
5857
5958 if (* list == self )
6059 /* If 'self' is the end of the list (and thus self->wr_next == NULL)
You can’t perform that action at this time.
0 commit comments