Skip to content

Commit d019bc8

Browse files
orenmnmethane
authored andcommitted
bpo-31787: Prevent refleaks when calling __init__() more than once (GH-3995)
1 parent aec7532 commit d019bc8

File tree

13 files changed

+105
-19
lines changed

13 files changed

+105
-19
lines changed

‎Lib/test/test_asyncio/test_tasks.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2373,6 +2373,20 @@ class CTask_CFuture_Tests(BaseTaskTests, SetMethodsTest,
23732373
Task = getattr(tasks, '_CTask', None)
23742374
Future = getattr(futures, '_CFuture', None)
23752375

2376+
@support.refcount_test
2377+
def test_refleaks_in_task___init__(self):
2378+
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
2379+
@asyncio.coroutine
2380+
def coro():
2381+
pass
2382+
task = self.new_task(self.loop, coro())
2383+
self.loop.run_until_complete(task)
2384+
refs_before = gettotalrefcount()
2385+
for i in range(100):
2386+
task.__init__(coro(), loop=self.loop)
2387+
self.loop.run_until_complete(task)
2388+
self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
2389+
23762390

23772391
@unittest.skipUnless(hasattr(futures, '_CFuture') and
23782392
hasattr(tasks, '_CTask'),

‎Lib/test/test_bz2.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import threading
1414
from test.support import unlink
1515
import _compression
16+
import sys
1617

1718

1819
# Skip tests if the bz2 module doesn't exist.
@@ -816,6 +817,16 @@ def test_failure(self):
816817
# Previously, a second call could crash due to internal inconsistency
817818
self.assertRaises(Exception, bzd.decompress, self.BAD_DATA * 30)
818819

820+
@support.refcount_test
821+
def test_refleaks_in___init__(self):
822+
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
823+
bzd = BZ2Decompressor()
824+
refs_before = gettotalrefcount()
825+
for i in range(100):
826+
bzd.__init__()
827+
self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
828+
829+
819830
class CompressDecompressTest(BaseTest):
820831
def testCompress(self):
821832
data = bz2.compress(self.TEXT)

‎Lib/test/test_descr.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,6 +1559,15 @@ def f(cls, arg): return (cls, arg)
15591559
del cm.x
15601560
self.assertNotHasAttr(cm, "x")
15611561

1562+
@support.refcount_test
1563+
def test_refleaks_in_classmethod___init__(self):
1564+
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
1565+
cm = classmethod(None)
1566+
refs_before = gettotalrefcount()
1567+
for i in range(100):
1568+
cm.__init__(None)
1569+
self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
1570+
15621571
@support.impl_detail("the module 'xxsubtype' is internal")
15631572
def test_classmethods_in_c(self):
15641573
# Testing C-based class methods...
@@ -1614,6 +1623,15 @@ class D(C):
16141623
del sm.x
16151624
self.assertNotHasAttr(sm, "x")
16161625

1626+
@support.refcount_test
1627+
def test_refleaks_in_staticmethod___init__(self):
1628+
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
1629+
sm = staticmethod(None)
1630+
refs_before = gettotalrefcount()
1631+
for i in range(100):
1632+
sm.__init__(None)
1633+
self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
1634+
16171635
@support.impl_detail("the module 'xxsubtype' is internal")
16181636
def test_staticmethods_in_c(self):
16191637
# Testing C-based static methods...

‎Lib/test/test_hashlib.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ def hash_constructors(self):
162162
constructors = self.constructors_to_test.values()
163163
return itertools.chain.from_iterable(constructors)
164164

165+
@support.refcount_test
166+
def test_refleaks_in_hash___init__(self):
167+
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
168+
sha1_hash = c_hashlib.new('sha1')
169+
refs_before = gettotalrefcount()
170+
for i in range(100):
171+
sha1_hash.__init__('sha1')
172+
self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
173+
165174
def test_hash_array(self):
166175
a = array.array("b", range(10))
167176
for cons in self.hash_constructors:

‎Lib/test/test_lzma.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import pathlib
55
import pickle
66
import random
7+
import sys
8+
from test import support
79
import unittest
810

911
from test.support import (
@@ -364,6 +366,15 @@ def test_pickle(self):
364366
with self.assertRaises(TypeError):
365367
pickle.dumps(LZMADecompressor(), proto)
366368

369+
@support.refcount_test
370+
def test_refleaks_in_decompressor___init__(self):
371+
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
372+
lzd = LZMADecompressor()
373+
refs_before = gettotalrefcount()
374+
for i in range(100):
375+
lzd.__init__()
376+
self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
377+
367378

368379
class CompressDecompressFunctionTestCase(unittest.TestCase):
369380

‎Lib/test/test_property.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import sys
55
import unittest
6+
from test import support
67

78
class PropertyBase(Exception):
89
pass
@@ -173,6 +174,16 @@ def spam(self):
173174
sub.__class__.spam.__doc__ = 'Spam'
174175
self.assertEqual(sub.__class__.spam.__doc__, 'Spam')
175176

177+
@support.refcount_test
178+
def test_refleaks_in___init__(self):
179+
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
180+
fake_prop = property('fget', 'fset', 'fdel', 'doc')
181+
refs_before = gettotalrefcount()
182+
for i in range(100):
183+
fake_prop.__init__('fget', 'fset', 'fdel', 'doc')
184+
self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
185+
186+
176187
# Issue 5890: subclasses of property do not preserve method __doc__ strings
177188
class PropertySub(property):
178189
"""This is a subclass of property"""
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed refleaks of ``__init__()`` methods in various modules.
2+
(Contributed by Oren Milman)

‎Modules/_asynciomodule.c‎

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -458,13 +458,27 @@ future_schedule_callbacks(FutureObj *fut)
458458
return 0;
459459
}
460460

461+
461462
static int
462463
future_init(FutureObj *fut, PyObject *loop)
463464
{
464465
PyObject *res;
465466
int is_true;
466467
_Py_IDENTIFIER(get_debug);
467468

469+
// Same to FutureObj_clear() but not clearing fut->dict
470+
Py_CLEAR(fut->fut_loop);
471+
Py_CLEAR(fut->fut_callback0);
472+
Py_CLEAR(fut->fut_context0);
473+
Py_CLEAR(fut->fut_callbacks);
474+
Py_CLEAR(fut->fut_result);
475+
Py_CLEAR(fut->fut_exception);
476+
Py_CLEAR(fut->fut_source_tb);
477+
478+
fut->fut_state = STATE_PENDING;
479+
fut->fut_log_tb = 0;
480+
fut->fut_blocking = 0;
481+
468482
if (loop == Py_None) {
469483
loop = get_event_loop();
470484
if (loop == NULL) {
@@ -474,7 +488,7 @@ future_init(FutureObj *fut, PyObject *loop)
474488
else {
475489
Py_INCREF(loop);
476490
}
477-
Py_XSETREF(fut->fut_loop, loop);
491+
fut->fut_loop = loop;
478492

479493
res = _PyObject_CallMethodId(fut->fut_loop, &PyId_get_debug, NULL);
480494
if (res == NULL) {
@@ -486,16 +500,12 @@ future_init(FutureObj *fut, PyObject *loop)
486500
return -1;
487501
}
488502
if (is_true) {
489-
Py_XSETREF(fut->fut_source_tb, _PyObject_CallNoArg(traceback_extract_stack));
503+
fut->fut_source_tb = _PyObject_CallNoArg(traceback_extract_stack);
490504
if (fut->fut_source_tb == NULL) {
491505
return -1;
492506
}
493507
}
494508

495-
fut->fut_callback0 = NULL;
496-
fut->fut_context0 = NULL;
497-
fut->fut_callbacks = NULL;
498-
499509
return 0;
500510
}
501511

@@ -1938,16 +1948,16 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop)
19381948
return -1;
19391949
}
19401950

1941-
self->task_context = PyContext_CopyCurrent();
1951+
Py_XSETREF(self->task_context, PyContext_CopyCurrent());
19421952
if (self->task_context == NULL) {
19431953
return -1;
19441954
}
19451955

1946-
self->task_fut_waiter = NULL;
1956+
Py_CLEAR(self->task_fut_waiter);
19471957
self->task_must_cancel = 0;
19481958
self->task_log_destroy_pending = 1;
19491959
Py_INCREF(coro);
1950-
self->task_coro = coro;
1960+
Py_XSETREF(self->task_coro, coro);
19511961

19521962
if (task_call_step_soon(self, NULL)) {
19531963
return -1;

‎Modules/_bz2module.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ _bz2_BZ2Decompressor___init___impl(BZ2Decompressor *self)
644644
self->bzs_avail_in_real = 0;
645645
self->input_buffer = NULL;
646646
self->input_buffer_size = 0;
647-
self->unused_data = PyBytes_FromStringAndSize(NULL, 0);
647+
Py_XSETREF(self->unused_data, PyBytes_FromStringAndSize(NULL, 0));
648648
if (self->unused_data == NULL)
649649
goto error;
650650

‎Modules/_hashopenssl.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,8 +369,8 @@ EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds)
369369
return -1;
370370
}
371371

372-
self->name = name_obj;
373-
Py_INCREF(self->name);
372+
Py_INCREF(name_obj);
373+
Py_XSETREF(self->name, name_obj);
374374

375375
if (data_obj) {
376376
if (view.len >= HASHLIB_GIL_MINSIZE) {

0 commit comments

Comments
 (0)