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.
This commit is contained in:
Vlad Starostin 2018-05-21 11:13:45 +03:00 committed by Andrew Svetlov
parent 4054b172ab
commit a84d0b361a
3 changed files with 25 additions and 3 deletions

View file

@ -706,6 +706,12 @@ class _SelectorTransport(transports._FlowControlMixin,
def get_write_buffer_size(self): def get_write_buffer_size(self):
return len(self._buffer) 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): class _SelectorSocketTransport(_SelectorTransport):
@ -732,7 +738,7 @@ class _SelectorSocketTransport(_SelectorTransport):
self._loop.call_soon(self._protocol.connection_made, self) self._loop.call_soon(self._protocol.connection_made, self)
# only start reading when connection_made() has been called # 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) self._sock_fd, self._read_ready)
if waiter is not None: if waiter is not None:
# only wake up the waiter when connection_made() has been called # 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: if self._closing or not self._paused:
return return
self._paused = False 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(): if self._loop.get_debug():
logger.debug("%r resumes reading", self) logger.debug("%r resumes reading", self)
@ -930,7 +936,7 @@ class _SelectorDatagramTransport(_SelectorTransport):
self._address = address self._address = address
self._loop.call_soon(self._protocol.connection_made, self) self._loop.call_soon(self._protocol.connection_made, self)
# only start reading when connection_made() has been called # 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) self._sock_fd, self._read_ready)
if waiter is not None: if waiter is not None:
# only wake up the waiter when connection_made() has been called # only wake up the waiter when connection_made() has been called

View file

@ -871,6 +871,21 @@ class SelectorTransportTests(test_utils.TestCase):
self.assertIsNone(tr._protocol) self.assertIsNone(tr._protocol)
self.assertIsNone(tr._loop) 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): class SelectorSocketTransportTests(test_utils.TestCase):

View file

@ -0,0 +1 @@
Fix FD leak in `_SelectorSocketTransport` Patch by Vlad Starostin.