]> granicus.if.org Git - python/commitdiff
[3.6] bpo-33263: Fix FD leak in _SelectorSocketTransport (GH-6450) (#7025)
authorAndrew Svetlov <andrew.svetlov@gmail.com>
Mon, 21 May 2018 10:09:49 +0000 (13:09 +0300)
committerGitHub <noreply@github.com>
Mon, 21 May 2018 10:09:49 +0000 (13:09 +0300)
* bpo-33263 Fix FD leak in _SelectorSocketTransport. (GH-6450)

Under particular circumstances _SelectorSocketTransport can try to add a reader
even the transport is already being closed. This can lead to FD leak and
invalid stated of the following connections. Fixed the SelectorSocketTransport
to add the reader only if the trasport is still active..
(cherry picked from commit a84d0b361a26c05c6fadc6640591ec3feee5bfb5)

Lib/asyncio/selector_events.py
Lib/test/test_asyncio/test_selector_events.py
Misc/NEWS.d/next/Library/2018-04-11-20-29-19.bpo-33263.B56Hc1.rst [new file with mode: 0644]

index aa65702f2dbcbfc641b31c385876e8c234d79d4d..c7e7909449eb4961866a749ecae714c4a2f10be7 100644 (file)
@@ -673,6 +673,12 @@ class _SelectorTransport(transports._FlowControlMixin,
     def get_write_buffer_size(self):
         return len(self._buffer)
 
+    def _add_reader(self, fd, callback, *args):
+        if self._closing:
+            return
+
+        self._loop._add_reader(fd, callback, *args)
+
 
 class _SelectorSocketTransport(_SelectorTransport):
 
@@ -689,7 +695,7 @@ class _SelectorSocketTransport(_SelectorTransport):
 
         self._loop.call_soon(self._protocol.connection_made, self)
         # only start reading when connection_made() has been called
-        self._loop.call_soon(self._loop._add_reader,
+        self._loop.call_soon(self._add_reader,
                              self._sock_fd, self._read_ready)
         if waiter is not None:
             # only wake up the waiter when connection_made() has been called
@@ -710,9 +716,7 @@ class _SelectorSocketTransport(_SelectorTransport):
         if not self._paused:
             raise RuntimeError('Not paused')
         self._paused = False
-        if self._closing:
-            return
-        self._loop._add_reader(self._sock_fd, self._read_ready)
+        self._add_reader(self._sock_fd, self._read_ready)
         if self._loop.get_debug():
             logger.debug("%r resumes reading", self)
 
@@ -1052,7 +1056,7 @@ class _SelectorDatagramTransport(_SelectorTransport):
         self._address = address
         self._loop.call_soon(self._protocol.connection_made, self)
         # only start reading when connection_made() has been called
-        self._loop.call_soon(self._loop._add_reader,
+        self._loop.call_soon(self._add_reader,
                              self._sock_fd, self._read_ready)
         if waiter is not None:
             # only wake up the waiter when connection_made() has been called
index 830b15c0893ee57aff63e4e7e9a53894ae855a5f..0794e994c9d4380bfef0acaa153f9c9e9e92a82f 100644 (file)
@@ -832,6 +832,21 @@ class SelectorTransportTests(test_utils.TestCase):
         self.assertIsNone(tr._protocol)
         self.assertIsNone(tr._loop)
 
+    def test__add_reader(self):
+        tr = self.create_transport()
+        tr._buffer.extend(b'1')
+        tr._add_reader(7, mock.sentinel)
+        self.assertTrue(self.loop.readers)
+
+        tr._force_close(None)
+
+        self.assertTrue(tr.is_closing())
+        self.assertFalse(self.loop.readers)
+
+        # can not add readers after closing
+        tr._add_reader(7, mock.sentinel)
+        self.assertFalse(self.loop.readers)
+
 
 class SelectorSocketTransportTests(test_utils.TestCase):
 
diff --git a/Misc/NEWS.d/next/Library/2018-04-11-20-29-19.bpo-33263.B56Hc1.rst b/Misc/NEWS.d/next/Library/2018-04-11-20-29-19.bpo-33263.B56Hc1.rst
new file mode 100644 (file)
index 0000000..77994f6
--- /dev/null
@@ -0,0 +1 @@
+Fix FD leak in `_SelectorSocketTransport`  Patch by Vlad Starostin.