Skip to content

Commit

Permalink
bpo-33263: Fix FD leak in _SelectorSocketTransport (GH-6450) (#7022)
Browse files Browse the repository at this point in the history
* 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)

Co-authored-by: Vlad Starostin <drtyrsa@yandex.ru>
  • Loading branch information
2 people authored and asvetlov committed May 21, 2018
1 parent 4ecdc11 commit b8b8000
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 3 deletions.
12 changes: 9 additions & 3 deletions Lib/asyncio/selector_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,12 @@ def _call_connection_lost(self, exc):
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):

Expand All @@ -732,7 +738,7 @@ def __init__(self, loop, sock, protocol, waiter=None,

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
Expand All @@ -754,7 +760,7 @@ def resume_reading(self):
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)

Expand Down Expand Up @@ -930,7 +936,7 @@ def __init__(self, loop, sock, protocol, address=None,
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
Expand Down
15 changes: 15 additions & 0 deletions Lib/test/test_asyncio/test_selector_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,21 @@ def test_connection_lost(self):
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):

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix FD leak in `_SelectorSocketTransport` Patch by Vlad Starostin.

0 comments on commit b8b8000

Please sign in to comment.