https://github.com/python/cpython/commit/162bd7ea554c274de94815b75e44e68dbfabcf08
commit: 162bd7ea554c274de94815b75e44e68dbfabcf08
branch: 3.14
author: Timofei Ivankov <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-08-05T20:50:57+05:30
summary:
[3.14] gh-153133: Fix socket leak in error path of asyncio create_connection
(GH-153134) (#154897)
files:
A Misc/NEWS.d/next/Library/2026-07-05-17-39-16.gh-issue-153133.kKSH7g.rst
M Lib/asyncio/base_events.py
M Lib/test/test_asyncio/test_base_events.py
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index d89790e7eeb6e19..a2a840fa081dcb4 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -1214,19 +1214,24 @@ async def _create_connection_transport(
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None):
- sock.setblocking(False)
+ try:
+ sock.setblocking(False)
- protocol = protocol_factory()
- waiter = self.create_future()
- if ssl:
- sslcontext = None if isinstance(ssl, bool) else ssl
- transport = self._make_ssl_transport(
- sock, protocol, sslcontext, waiter,
- server_side=server_side, server_hostname=server_hostname,
- ssl_handshake_timeout=ssl_handshake_timeout,
- ssl_shutdown_timeout=ssl_shutdown_timeout)
- else:
- transport = self._make_socket_transport(sock, protocol, waiter)
+ protocol = protocol_factory()
+ waiter = self.create_future()
+ if ssl:
+ sslcontext = None if isinstance(ssl, bool) else ssl
+ transport = self._make_ssl_transport(
+ sock, protocol, sslcontext, waiter,
+ server_side=server_side, server_hostname=server_hostname,
+ ssl_handshake_timeout=ssl_handshake_timeout,
+ ssl_shutdown_timeout=ssl_shutdown_timeout)
+ else:
+ transport = self._make_socket_transport(sock, protocol, waiter)
+ except:
+ # gh-153133: close the socket if the transport is never created.
+ sock.close()
+ raise
try:
await waiter
diff --git a/Lib/test/test_asyncio/test_base_events.py
b/Lib/test/test_asyncio/test_base_events.py
index 1b727f3b1febefc..a7f89608b5abb7d 100644
--- a/Lib/test/test_asyncio/test_base_events.py
+++ b/Lib/test/test_asyncio/test_base_events.py
@@ -1194,6 +1194,47 @@ def getaddrinfo(*args, **kw):
self.loop.run_until_complete(coro)
self.assertTrue(sock.close.called)
+ def test_create_connection_sock_transport_error_closes_sock(self):
+ # gh-153133: a user-provided socket is closed if the transport is
+ # never created.
+ sock = mock.Mock()
+ sock.type = socket.SOCK_STREAM
+
+ def factory():
+ raise ZeroDivisionError
+
+ coro = self.loop.create_connection(factory, sock=sock)
+ with self.assertRaises(ZeroDivisionError):
+ self.loop.run_until_complete(coro)
+ self.assertTrue(sock.close.called)
+
+ @patch_socket
+ def test_create_connection_transport_error_closes_sock(self, m_socket):
+ # gh-153133: an internally created socket is closed if the transport
+ # is never created.
+ sock = mock.Mock()
+ m_socket.socket.return_value = sock
+
+ def getaddrinfo(*args, **kw):
+ fut = self.loop.create_future()
+ addr = (socket.AF_INET, socket.SOCK_STREAM, 0, '',
+ ('127.0.0.1', 80))
+ fut.set_result([addr])
+ return fut
+ self.loop.getaddrinfo = getaddrinfo
+
+ async def sock_connect(sock, address):
+ return None
+
+ def factory():
+ raise ZeroDivisionError
+
+ with mock.patch.object(self.loop, 'sock_connect', sock_connect):
+ coro = self.loop.create_connection(factory, '127.0.0.1', 80)
+ with self.assertRaises(ZeroDivisionError):
+ self.loop.run_until_complete(coro)
+ self.assertTrue(sock.close.called)
+
@patch_socket
def test_create_connection_happy_eyeballs_empty_exceptions(self, m_socket):
# See gh-135836: Fix IndexError when Happy Eyeballs algorithm
diff --git
a/Misc/NEWS.d/next/Library/2026-07-05-17-39-16.gh-issue-153133.kKSH7g.rst
b/Misc/NEWS.d/next/Library/2026-07-05-17-39-16.gh-issue-153133.kKSH7g.rst
new file mode 100644
index 000000000000000..a7a68158ac895e3
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-05-17-39-16.gh-issue-153133.kKSH7g.rst
@@ -0,0 +1,2 @@
+Fix a socket leak in :meth:`asyncio.loop.create_connection` when the
+transport cannot be created.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]