From 7208bfb64b74f31f9704be3f01f26861c9cf092b Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 21 May 2018 13:09:49 +0300 Subject: [PATCH] [3.6] bpo-33263: Fix FD leak in _SelectorSocketTransport (GH-6450) (#7025) * 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 | 14 +++++++++----- Lib/test/test_asyncio/test_selector_events.py | 15 +++++++++++++++ .../2018-04-11-20-29-19.bpo-33263.B56Hc1.rst | 1 + 3 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-04-11-20-29-19.bpo-33263.B56Hc1.rst diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index aa65702f2d..c7e7909449 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -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 diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index 830b15c089..0794e994c9 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -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 index 0000000000..77994f6a59 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-04-11-20-29-19.bpo-33263.B56Hc1.rst @@ -0,0 +1 @@ +Fix FD leak in `_SelectorSocketTransport` Patch by Vlad Starostin. -- 2.40.0