Skip to content

Commit 7208bfb

Browse files
authored
[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 a84d0b3)
1 parent 983e965 commit 7208bfb

File tree

3 files changed

+25
-5
lines changed

3 files changed

+25
-5
lines changed

‎Lib/asyncio/selector_events.py‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,12 @@ def _call_connection_lost(self, exc):
673673
def get_write_buffer_size(self):
674674
return len(self._buffer)
675675

676+
def _add_reader(self, fd, callback, *args):
677+
if self._closing:
678+
return
679+
680+
self._loop._add_reader(fd, callback, *args)
681+
676682

677683
class _SelectorSocketTransport(_SelectorTransport):
678684

@@ -689,7 +695,7 @@ def __init__(self, loop, sock, protocol, waiter=None,
689695

690696
self._loop.call_soon(self._protocol.connection_made, self)
691697
# only start reading when connection_made() has been called
692-
self._loop.call_soon(self._loop._add_reader,
698+
self._loop.call_soon(self._add_reader,
693699
self._sock_fd, self._read_ready)
694700
if waiter is not None:
695701
# only wake up the waiter when connection_made() has been called
@@ -710,9 +716,7 @@ def resume_reading(self):
710716
if not self._paused:
711717
raise RuntimeError('Not paused')
712718
self._paused = False
713-
if self._closing:
714-
return
715-
self._loop._add_reader(self._sock_fd, self._read_ready)
719+
self._add_reader(self._sock_fd, self._read_ready)
716720
if self._loop.get_debug():
717721
logger.debug("%r resumes reading", self)
718722

@@ -1052,7 +1056,7 @@ def __init__(self, loop, sock, protocol, address=None,
10521056
self._address = address
10531057
self._loop.call_soon(self._protocol.connection_made, self)
10541058
# only start reading when connection_made() has been called
1055-
self._loop.call_soon(self._loop._add_reader,
1059+
self._loop.call_soon(self._add_reader,
10561060
self._sock_fd, self._read_ready)
10571061
if waiter is not None:
10581062
# only wake up the waiter when connection_made() has been called

‎Lib/test/test_asyncio/test_selector_events.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,21 @@ def test_connection_lost(self):
832832
self.assertIsNone(tr._protocol)
833833
self.assertIsNone(tr._loop)
834834

835+
def test__add_reader(self):
836+
tr = self.create_transport()
837+
tr._buffer.extend(b'1')
838+
tr._add_reader(7, mock.sentinel)
839+
self.assertTrue(self.loop.readers)
840+
841+
tr._force_close(None)
842+
843+
self.assertTrue(tr.is_closing())
844+
self.assertFalse(self.loop.readers)
845+
846+
# can not add readers after closing
847+
tr._add_reader(7, mock.sentinel)
848+
self.assertFalse(self.loop.readers)
849+
835850

836851
class SelectorSocketTransportTests(test_utils.TestCase):
837852

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix FD leak in `_SelectorSocketTransport` Patch by Vlad Starostin.

0 commit comments

Comments
 (0)