Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions Lib/test/test_weakref.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,42 @@ def check(proxy):
self.assertRaises(ReferenceError, bool, ref3)
self.assertEqual(self.cbcalled, 2)

@support.refcount_test
def test_proxy_dead_arg_refcount(self):
class C:
def __add__(self, other):
return NotImplemented
def __radd__(self, other):
return NotImplemented
def __getitem__(self, item):
return None
def __eq__(self, other):
return NotImplemented
def __pow__(self, other, modulo=None):
return NotImplemented

dead_obj = C()
dead = weakref.proxy(dead_obj)
del dead_obj
gc_collect()

cases = [
lambda live: live + dead,
lambda live: live[dead],
lambda live: live == dead,
lambda live: pow(live, dead),
lambda live: pow(live, 2, dead),
]
for case in cases:
with self.subTest(case=case):
live_obj = C()
live = weakref.proxy(live_obj)
refcount = sys.getrefcount(live_obj)
for _ in range(100):
with self.assertRaises(ReferenceError):
case(live)
self.assertEqual(sys.getrefcount(live_obj), refcount)

@support.cpython_only
def test_proxy_repr(self):
obj = C()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix reference leaks in :func:`weakref.proxy` when binary, ternary, or
comparison operations fail because one of the operands is a dead proxy.
77 changes: 55 additions & 22 deletions Objects/weakrefobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -541,23 +541,28 @@ proxy_check_ref(PyObject *obj)

/* If a parameter is a proxy, check that it is still "live" and wrap it,
* replacing the original value with the raw object. Raises ReferenceError
* if the param is a dead proxy.
* if the param is a dead proxy. Returns a new reference.
*/
#define UNWRAP(o) \
if (PyWeakref_CheckProxy(o)) { \
o = _PyWeakref_GET_REF(o); \
if (!proxy_check_ref(o)) { \
return NULL; \
Comment thread
sergey-miryanov marked this conversation as resolved.
} \
} \
else { \
Py_INCREF(o); \
static inline PyObject *
proxy_unwrap(PyObject *obj)
{
if (PyWeakref_CheckProxy(obj)) {
obj = _PyWeakref_GET_REF(obj);
if (!proxy_check_ref(obj)) {
return NULL;
}
return obj;
}
return Py_NewRef(obj);
}

#define WRAP_UNARY(method, generic) \
static PyObject * \
method(PyObject *proxy) { \
UNWRAP(proxy); \
proxy = proxy_unwrap(proxy); \
if (proxy == NULL) { \
return NULL; \
} \
PyObject* res = generic(proxy); \
Py_DECREF(proxy); \
return res; \
Expand All @@ -566,8 +571,15 @@ proxy_check_ref(PyObject *obj)
#define WRAP_BINARY(method, generic) \
static PyObject * \
method(PyObject *x, PyObject *y) { \
UNWRAP(x); \
UNWRAP(y); \
x = proxy_unwrap(x); \
if (x == NULL) { \
return NULL; \
} \
y = proxy_unwrap(y); \
if (y == NULL) { \
Py_DECREF(x); \
return NULL; \
} \
PyObject* res = generic(x, y); \
Py_DECREF(x); \
Py_DECREF(y); \
Expand All @@ -580,22 +592,36 @@ proxy_check_ref(PyObject *obj)
#define WRAP_TERNARY(method, generic) \
static PyObject * \
method(PyObject *proxy, PyObject *v, PyObject *w) { \
UNWRAP(proxy); \
UNWRAP(v); \
PyObject* res = NULL; \
proxy = proxy_unwrap(proxy); \
if (proxy == NULL) { \
return NULL; \
} \
v = proxy_unwrap(v); \
if (v == NULL) { \
goto fail; \
} \
if (w != NULL) { \
UNWRAP(w); \
w = proxy_unwrap(w); \
if (w == NULL) { \
goto fail; \
} \
} \
PyObject* res = generic(proxy, v, w); \
Py_DECREF(proxy); \
Py_DECREF(v); \
res = generic(proxy, v, w); \
Py_XDECREF(w); \
fail: \
Py_XDECREF(v); \
Py_XDECREF(proxy); \
return res; \
}

#define WRAP_METHOD(method, SPECIAL) \
static PyObject * \
method(PyObject *proxy, PyObject *Py_UNUSED(ignored)) { \
UNWRAP(proxy); \
proxy = proxy_unwrap(proxy); \
if (proxy == NULL) { \
return NULL; \
} \
PyObject* res = PyObject_CallMethodNoArgs(proxy, &_Py_ID(SPECIAL)); \
Py_DECREF(proxy); \
return res; \
Expand Down Expand Up @@ -643,8 +669,15 @@ proxy_setattr(PyObject *proxy, PyObject *name, PyObject *value)
static PyObject *
proxy_richcompare(PyObject *proxy, PyObject *v, int op)
{
UNWRAP(proxy);
UNWRAP(v);
proxy = proxy_unwrap(proxy);
if (proxy == NULL) {
return NULL;
}
v = proxy_unwrap(v);
if (v == NULL) {
Py_DECREF(proxy);
return NULL;
}
PyObject* res = PyObject_RichCompare(proxy, v, op);
Py_DECREF(proxy);
Py_DECREF(v);
Expand Down
Loading