]> granicus.if.org Git - python/commitdiff
bpo-31787: Prevent refleaks when calling __init__() more than once (GH-3995)
authorINADA Naoki <methane@users.noreply.github.com>
Tue, 13 Feb 2018 13:15:24 +0000 (22:15 +0900)
committerGitHub <noreply@github.com>
Tue, 13 Feb 2018 13:15:24 +0000 (22:15 +0900)
(cherry picked from commit d019bc8319ea35e93bf4baa38098ff1b57cd3ee5)

13 files changed:
Lib/test/test_asyncio/test_tasks.py
Lib/test/test_bz2.py
Lib/test/test_descr.py
Lib/test/test_hashlib.py
Lib/test/test_lzma.py
Lib/test/test_property.py
Misc/NEWS.d/next/Library/2018-02-09-21-41-56.bpo-31787.owSZ2t.rst [new file with mode: 0644]
Modules/_asynciomodule.c
Modules/_bz2module.c
Modules/_hashopenssl.c
Modules/_lzmamodule.c
Objects/descrobject.c
Objects/funcobject.c

index 42da1fa19548f1c54458dfec824526ebf0b7b7e6..f41160ba3222d309075ecdb92f37b6e46512b798 100644 (file)
@@ -2133,6 +2133,20 @@ class CTask_CFuture_Tests(BaseTaskTests, test_utils.TestCase):
     Task = getattr(tasks, '_CTask', None)
     Future = getattr(futures, '_CFuture', None)
 
+    @support.refcount_test
+    def test_refleaks_in_task___init__(self):
+        gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
+        @asyncio.coroutine
+        def coro():
+            pass
+        task = self.new_task(self.loop, coro())
+        self.loop.run_until_complete(task)
+        refs_before = gettotalrefcount()
+        for i in range(100):
+            task.__init__(coro(), loop=self.loop)
+            self.loop.run_until_complete(task)
+        self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
+
 
 @unittest.skipUnless(hasattr(futures, '_CFuture'),
                      'requires the C _asyncio module')
index eaa472a6ccda03264de500cb4c5e78b2062f3a69..f340f2330c3dab8c3dd02bd5e7a1740b278e54a9 100644 (file)
@@ -13,6 +13,7 @@ import subprocess
 import sys
 from test.support import unlink
 import _compression
+import sys
 
 try:
     import threading
@@ -828,6 +829,16 @@ class BZ2DecompressorTest(BaseTest):
         # Previously, a second call could crash due to internal inconsistency
         self.assertRaises(Exception, bzd.decompress, self.BAD_DATA * 30)
 
+    @support.refcount_test
+    def test_refleaks_in___init__(self):
+        gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
+        bzd = BZ2Decompressor()
+        refs_before = gettotalrefcount()
+        for i in range(100):
+            bzd.__init__()
+        self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
+
+
 class CompressDecompressTest(BaseTest):
     def testCompress(self):
         data = bz2.compress(self.TEXT)
index c5bff7718dc72bff9fcca17cbceb0801be85c37b..0d33e9179308bf752d7b25ec784dd596d0728c79 100644 (file)
@@ -1519,6 +1519,15 @@ order (MRO) for bases """
         del cm.x
         self.assertNotHasAttr(cm, "x")
 
+    @support.refcount_test
+    def test_refleaks_in_classmethod___init__(self):
+        gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
+        cm = classmethod(None)
+        refs_before = gettotalrefcount()
+        for i in range(100):
+            cm.__init__(None)
+        self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
+
     @support.impl_detail("the module 'xxsubtype' is internal")
     def test_classmethods_in_c(self):
         # Testing C-based class methods...
@@ -1574,6 +1583,15 @@ order (MRO) for bases """
         del sm.x
         self.assertNotHasAttr(sm, "x")
 
+    @support.refcount_test
+    def test_refleaks_in_staticmethod___init__(self):
+        gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
+        sm = staticmethod(None)
+        refs_before = gettotalrefcount()
+        for i in range(100):
+            sm.__init__(None)
+        self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
+
     @support.impl_detail("the module 'xxsubtype' is internal")
     def test_staticmethods_in_c(self):
         # Testing C-based static methods...
index 177143e01dc78f84191c11908e7405ba532cdaf9..751a713b3e8b64819653c70ec9187676f1e1a572 100644 (file)
@@ -165,6 +165,15 @@ class HashLibTestCase(unittest.TestCase):
         constructors = self.constructors_to_test.values()
         return itertools.chain.from_iterable(constructors)
 
+    @support.refcount_test
+    def test_refleaks_in_hash___init__(self):
+        gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
+        sha1_hash = c_hashlib.new('sha1')
+        refs_before = gettotalrefcount()
+        for i in range(100):
+            sha1_hash.__init__('sha1')
+        self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
+
     def test_hash_array(self):
         a = array.array("b", range(10))
         for cons in self.hash_constructors:
index d7a8576d512340dccc1589bde6ec58ec0dffd6fb..3dc2c1e7e3b779e3e54ad598aba15d2a41444dcf 100644 (file)
@@ -4,6 +4,8 @@ import os
 import pathlib
 import pickle
 import random
+import sys
+from test import support
 import unittest
 
 from test.support import (
@@ -364,6 +366,15 @@ class CompressorDecompressorTestCase(unittest.TestCase):
             with self.assertRaises(TypeError):
                 pickle.dumps(LZMADecompressor(), proto)
 
+    @support.refcount_test
+    def test_refleaks_in_decompressor___init__(self):
+        gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
+        lzd = LZMADecompressor()
+        refs_before = gettotalrefcount()
+        for i in range(100):
+            lzd.__init__()
+        self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
+
 
 class CompressDecompressFunctionTestCase(unittest.TestCase):
 
index 26b7d5283a2220b729b91cd237ec4a9c9d260c4f..f6f8f5ed0e45ee604ec517dc211e5cc4172f6cc2 100644 (file)
@@ -3,6 +3,7 @@
 
 import sys
 import unittest
+from test import support
 
 class PropertyBase(Exception):
     pass
@@ -173,6 +174,16 @@ class PropertyTests(unittest.TestCase):
         sub.__class__.spam.__doc__ = 'Spam'
         self.assertEqual(sub.__class__.spam.__doc__, 'Spam')
 
+    @support.refcount_test
+    def test_refleaks_in___init__(self):
+        gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
+        fake_prop = property('fget', 'fset', 'fdel', 'doc')
+        refs_before = gettotalrefcount()
+        for i in range(100):
+            fake_prop.__init__('fget', 'fset', 'fdel', 'doc')
+        self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
+
+
 # Issue 5890: subclasses of property do not preserve method __doc__ strings
 class PropertySub(property):
     """This is a subclass of property"""
diff --git a/Misc/NEWS.d/next/Library/2018-02-09-21-41-56.bpo-31787.owSZ2t.rst b/Misc/NEWS.d/next/Library/2018-02-09-21-41-56.bpo-31787.owSZ2t.rst
new file mode 100644 (file)
index 0000000..f0cde59
--- /dev/null
@@ -0,0 +1,2 @@
+Fixed refleaks of ``__init__()`` methods in various modules.
+(Contributed by Oren Milman)
index 35632a6e6faacb27a86c923493571352642a4772..f53387115e73aa0d375a1b9854d23816f0bb2e45 100644 (file)
@@ -132,6 +132,7 @@ future_schedule_callbacks(FutureObj *fut)
     return 0;
 }
 
+
 static int
 future_init(FutureObj *fut, PyObject *loop)
 {
@@ -139,6 +140,17 @@ future_init(FutureObj *fut, PyObject *loop)
     int is_true;
     _Py_IDENTIFIER(get_debug);
 
+    // Same to FutureObj_clear() but not clearing fut->dict
+    Py_CLEAR(fut->fut_loop);
+    Py_CLEAR(fut->fut_callbacks);
+    Py_CLEAR(fut->fut_result);
+    Py_CLEAR(fut->fut_exception);
+    Py_CLEAR(fut->fut_source_tb);
+
+    fut->fut_state = STATE_PENDING;
+    fut->fut_log_tb = 0;
+    fut->fut_blocking = 0;
+
     if (loop == Py_None) {
         loop = _PyObject_CallNoArg(asyncio_get_event_loop);
         if (loop == NULL) {
@@ -148,7 +160,7 @@ future_init(FutureObj *fut, PyObject *loop)
     else {
         Py_INCREF(loop);
     }
-    Py_XSETREF(fut->fut_loop, loop);
+    fut->fut_loop = loop;
 
     res = _PyObject_CallMethodId(fut->fut_loop, &PyId_get_debug, NULL);
     if (res == NULL) {
@@ -160,13 +172,13 @@ future_init(FutureObj *fut, PyObject *loop)
         return -1;
     }
     if (is_true) {
-        Py_XSETREF(fut->fut_source_tb, _PyObject_CallNoArg(traceback_extract_stack));
+        fut->fut_source_tb = _PyObject_CallNoArg(traceback_extract_stack);
         if (fut->fut_source_tb == NULL) {
             return -1;
         }
     }
 
-    Py_XSETREF(fut->fut_callbacks, PyList_New(0));
+    fut->fut_callbacks = PyList_New(0);
     if (fut->fut_callbacks == NULL) {
         return -1;
     }
@@ -1336,12 +1348,12 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop)
         return -1;
     }
 
-    self->task_fut_waiter = NULL;
+    Py_CLEAR(self->task_fut_waiter);
     self->task_must_cancel = 0;
     self->task_log_destroy_pending = 1;
 
     Py_INCREF(coro);
-    self->task_coro = coro;
+    Py_XSETREF(self->task_coro, coro);
 
     if (task_call_step_soon(self, NULL)) {
         return -1;
index 5cea42cc6b2f8151b834df31d2280af110a78e2c..9c5a631eff45f347e2f4c3d8bcb6cff5e908af9d 100644 (file)
@@ -663,7 +663,7 @@ _bz2_BZ2Decompressor___init___impl(BZ2Decompressor *self)
     self->bzs_avail_in_real = 0;
     self->input_buffer = NULL;
     self->input_buffer_size = 0;
-    self->unused_data = PyBytes_FromStringAndSize(NULL, 0);
+    Py_XSETREF(self->unused_data, PyBytes_FromStringAndSize(NULL, 0));
     if (self->unused_data == NULL)
         goto error;
 
index 8670fbd4572d02b4f56dbf654d31a0b52700e996..8f6185ccb73be48209047d9baf01b4408d561a54 100644 (file)
@@ -378,8 +378,8 @@ EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds)
         return -1;
     }
 
-    self->name = name_obj;
-    Py_INCREF(self->name);
+    Py_INCREF(name_obj);
+    Py_XSETREF(self->name, name_obj);
 
     if (data_obj) {
         if (view.len >= HASHLIB_GIL_MINSIZE) {
index bb77552b67b15ddc20ad9b019ed47ed74407869a..b25faff5ac4924a377f6832aa7e4701c1db88e12 100644 (file)
@@ -1192,7 +1192,7 @@ _lzma_LZMADecompressor___init___impl(Decompressor *self, int format,
     self->needs_input = 1;
     self->input_buffer = NULL;
     self->input_buffer_size = 0;
-    self->unused_data = PyBytes_FromStringAndSize(NULL, 0);
+    Py_XSETREF(self->unused_data, PyBytes_FromStringAndSize(NULL, 0));
     if (self->unused_data == NULL)
         goto error;
 
index 076e74148148254333f91eadb78417e296a3d682..9020ccd325479226c242b8dd068fc985d1c5b79e 100644 (file)
@@ -1483,10 +1483,10 @@ property_init(PyObject *self, PyObject *args, PyObject *kwds)
     Py_XINCREF(del);
     Py_XINCREF(doc);
 
-    prop->prop_get = get;
-    prop->prop_set = set;
-    prop->prop_del = del;
-    prop->prop_doc = doc;
+    Py_XSETREF(prop->prop_get, get);
+    Py_XSETREF(prop->prop_set, set);
+    Py_XSETREF(prop->prop_del, del);
+    Py_XSETREF(prop->prop_doc, doc);
     prop->getter_doc = 0;
 
     /* if no docstring given and the getter has one, use that one */
index 69cd973384b2047ead07983d48ee2f233a4dd7e4..6ce0cb43e5e929a2693443b020e26def0f459d71 100644 (file)
@@ -745,7 +745,7 @@ cm_init(PyObject *self, PyObject *args, PyObject *kwds)
     if (!_PyArg_NoKeywords("classmethod", kwds))
         return -1;
     Py_INCREF(callable);
-    cm->cm_callable = callable;
+    Py_XSETREF(cm->cm_callable, callable);
     return 0;
 }
 
@@ -926,7 +926,7 @@ sm_init(PyObject *self, PyObject *args, PyObject *kwds)
     if (!_PyArg_NoKeywords("staticmethod", kwds))
         return -1;
     Py_INCREF(callable);
-    sm->sm_callable = callable;
+    Py_XSETREF(sm->sm_callable, callable);
     return 0;
 }