Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions Lib/asyncio/base_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -1497,10 +1497,15 @@ async def create_datagram_endpoint(self, protocol_factory,
else:
raise exceptions[0]

protocol = protocol_factory()
waiter = self.create_future()
transport = self._make_datagram_transport(
sock, protocol, r_addr, waiter)
try:
protocol = protocol_factory()
waiter = self.create_future()
transport = self._make_datagram_transport(
sock, protocol, r_addr, waiter)
except:
# gh-156400: close the socket if the transport is never created.
sock.close()
raise
if self._debug:
if local_addr:
logger.info("Datagram endpoint local_addr=%r remote_addr=%r "
Expand Down Expand Up @@ -1714,9 +1719,14 @@ async def connect_accepted_socket(
return transport, protocol

async def connect_read_pipe(self, protocol_factory, pipe):
protocol = protocol_factory()
waiter = self.create_future()
transport = self._make_read_pipe_transport(pipe, protocol, waiter)
try:
protocol = protocol_factory()
waiter = self.create_future()
transport = self._make_read_pipe_transport(pipe, protocol, waiter)
except:
# gh-156400: close the pipe if the transport is never created.
pipe.close()
raise

try:
await waiter
Expand All @@ -1730,9 +1740,14 @@ async def connect_read_pipe(self, protocol_factory, pipe):
return transport, protocol

async def connect_write_pipe(self, protocol_factory, pipe):
protocol = protocol_factory()
waiter = self.create_future()
transport = self._make_write_pipe_transport(pipe, protocol, waiter)
try:
protocol = protocol_factory()
waiter = self.create_future()
transport = self._make_write_pipe_transport(pipe, protocol, waiter)
except:
# gh-156400: close the pipe if the transport is never created.
pipe.close()
raise

try:
await waiter
Expand Down
37 changes: 37 additions & 0 deletions Lib/test/test_asyncio/test_base_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -2041,6 +2041,43 @@ def test_create_datagram_endpoint_sock(self):
self.loop.run_until_complete(protocol.done)
self.assertEqual('CLOSED', protocol.state)

def test_create_datagram_endpoint_transport_error_closes_sock(self):
# gh-156400: the socket is closed if the transport is never created.
sock = mock.Mock()
sock.type = socket.SOCK_DGRAM

def factory():
raise ZeroDivisionError

coro = self.loop.create_datagram_endpoint(factory, sock=sock)
with self.assertRaises(ZeroDivisionError):
self.loop.run_until_complete(coro)
self.assertTrue(sock.close.called)

def test_connect_read_pipe_transport_error_closes_pipe(self):
# gh-156400: the pipe is closed if the transport is never created.
pipe = mock.Mock()

def factory():
raise ZeroDivisionError

coro = self.loop.connect_read_pipe(factory, pipe)
with self.assertRaises(ZeroDivisionError):
self.loop.run_until_complete(coro)
self.assertTrue(pipe.close.called)

def test_connect_write_pipe_transport_error_closes_pipe(self):
# gh-156400: the pipe is closed if the transport is never created.
pipe = mock.Mock()

def factory():
raise ZeroDivisionError

coro = self.loop.connect_write_pipe(factory, pipe)
with self.assertRaises(ZeroDivisionError):
self.loop.run_until_complete(coro)
self.assertTrue(pipe.close.called)

@unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets')
def test_create_datagram_endpoint_sock_unix(self):
fut = self.loop.create_datagram_endpoint(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Fix socket and pipe leaks in :mod:`asyncio` when ``protocol_factory()`` or
transport creation fails in :meth:`loop.create_datagram_endpoint
<asyncio.loop.create_datagram_endpoint>`, :meth:`loop.connect_read_pipe
<asyncio.loop.connect_read_pipe>`, and :meth:`loop.connect_write_pipe
<asyncio.loop.connect_write_pipe>`. The socket or pipe is now closed instead
of leaking until garbage collection.
Loading