Skip to content

Commit d3064d7

Browse files
fix unwrap leak with dead proxy operands
1 parent ee2775c commit d3064d7

File tree

2 files changed

+78
-3
lines changed

2 files changed

+78
-3
lines changed

Lib/test/test_weakref.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,58 @@ class MyObj:
538538
with self.assertRaises(TypeError):
539539
hash(weakref.proxy(obj))
540540

541+
def test_proxy_unref_add_refcount(self):
542+
class C:
543+
def __add__(self, o):
544+
return NotImplemented
545+
546+
# create dead proxy
547+
o = C()
548+
dead = weakref.proxy(o)
549+
del o
550+
gc.collect()
551+
552+
# create live proxy
553+
obj = C()
554+
ref = weakref.ref(obj)
555+
proxy = weakref.proxy(obj)
556+
557+
try:
558+
operator.add(proxy, dead)
559+
except ReferenceError:
560+
pass
561+
562+
del proxy, obj, dead
563+
gc.collect()
564+
565+
self.assertIsNone(ref(), "Leaked object in add operation")
566+
567+
def test_proxy_unref_pow_refcount(self):
568+
class C:
569+
def __pow__(self, o, m=None):
570+
return NotImplemented
571+
572+
# create dead proxy
573+
o = C()
574+
dead = weakref.proxy(o)
575+
del o
576+
gc.collect()
577+
578+
# create live proxy
579+
obj = C()
580+
ref = weakref.ref(obj)
581+
proxy = weakref.proxy(obj)
582+
583+
try:
584+
pow(proxy, dead, None)
585+
except ReferenceError:
586+
pass
587+
588+
del proxy, obj, dead
589+
gc.collect()
590+
591+
self.assertIsNone(ref(), "Leaked object in pow operation")
592+
541593
def test_getweakrefcount(self):
542594
o = C()
543595
ref1 = weakref.ref(o)

Objects/weakrefobject.c

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -538,9 +538,7 @@ proxy_check_ref(PyObject *obj)
538538
#define UNWRAP(o) \
539539
if (PyWeakref_CheckProxy(o)) { \
540540
o = _PyWeakref_GET_REF(o); \
541-
if (!proxy_check_ref(o)) { \
542-
return NULL; \
543-
} \
541+
proxy_check_ref(o); \
544542
} \
545543
else { \
546544
Py_INCREF(o); \
@@ -559,7 +557,13 @@ proxy_check_ref(PyObject *obj)
559557
static PyObject * \
560558
method(PyObject *x, PyObject *y) { \
561559
UNWRAP(x); \
560+
if (x == NULL) \
561+
return NULL; \
562562
UNWRAP(y); \
563+
if (y == NULL) { \
564+
Py_XDECREF(x); \
565+
return NULL; \
566+
} \
563567
PyObject* res = generic(x, y); \
564568
Py_DECREF(x); \
565569
Py_DECREF(y); \
@@ -573,9 +577,20 @@ proxy_check_ref(PyObject *obj)
573577
static PyObject * \
574578
method(PyObject *proxy, PyObject *v, PyObject *w) { \
575579
UNWRAP(proxy); \
580+
if (proxy == NULL) \
581+
return NULL; \
576582
UNWRAP(v); \
583+
if (v == NULL) { \
584+
Py_XDECREF(proxy); \
585+
return NULL; \
586+
} \
577587
if (w != NULL) { \
578588
UNWRAP(w); \
589+
if (w == NULL) { \
590+
Py_XDECREF(proxy); \
591+
Py_XDECREF(v); \
592+
return NULL; \
593+
} \
579594
} \
580595
PyObject* res = generic(proxy, v, w); \
581596
Py_DECREF(proxy); \
@@ -588,6 +603,8 @@ proxy_check_ref(PyObject *obj)
588603
static PyObject * \
589604
method(PyObject *proxy, PyObject *Py_UNUSED(ignored)) { \
590605
UNWRAP(proxy); \
606+
if (proxy == NULL) \
607+
return NULL; \
591608
PyObject* res = PyObject_CallMethodNoArgs(proxy, &_Py_ID(SPECIAL)); \
592609
Py_DECREF(proxy); \
593610
return res; \
@@ -636,7 +653,13 @@ static PyObject *
636653
proxy_richcompare(PyObject *proxy, PyObject *v, int op)
637654
{
638655
UNWRAP(proxy);
656+
if (proxy == NULL)
657+
return NULL;
639658
UNWRAP(v);
659+
if (v == NULL) {
660+
Py_XDECREF(proxy);
661+
return NULL;
662+
}
640663
PyObject* res = PyObject_RichCompare(proxy, v, op);
641664
Py_DECREF(proxy);
642665
Py_DECREF(v);

0 commit comments

Comments
 (0)