summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVlad Starostin <drtyrsa@yandex.ru>2018-05-21 11:13:45 +0300
committerAndrew Svetlov <andrew.svetlov@gmail.com>2018-05-21 11:13:45 +0300
commita84d0b361a26c05c6fadc6640591ec3feee5bfb5 (patch)
treefb5b5ad67a0122a5094af74d0b5ff43a6c768ca9
parent4054b172ab59054715a2aaf4979bd84ac23e3ada (diff)
downloadcpython-git-a84d0b361a26c05c6fadc6640591ec3feee5bfb5.tar.gz
bpo-33263: Fix FD leak in _SelectorSocketTransport (GH-6450)
* 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.
-rw-r--r--Lib/asyncio/selector_events.py12
-rw-r--r--Lib/test/test_asyncio/test_selector_events.py15
-rw-r--r--Misc/NEWS.d/next/Library/2018-04-11-20-29-19.bpo-33263.B56Hc1.rst1
3 files changed, 25 insertions, 3 deletions
diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py
index 354bf9d1c2..f9533a1d77 100644
--- a/Lib/asyncio/selector_events.py
+++ b/Lib/asyncio/selector_events.py
@@ -706,6 +706,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):
@@ -732,7 +738,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
@@ -754,7 +760,7 @@ class _SelectorSocketTransport(_SelectorTransport):
if self._closing or not self._paused:
return
self._paused = False
- 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)
@@ -930,7 +936,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 219ab0eb5b..684c29dec3 100644
--- a/Lib/test/test_asyncio/test_selector_events.py
+++ b/Lib/test/test_asyncio/test_selector_events.py
@@ -871,6 +871,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.