https://github.com/python/cpython/commit/7dbc7975db8e34db8be8e4a10bd10784ab49b07a
commit: 7dbc7975db8e34db8be8e4a10bd10784ab49b07a
branch: main
author: Timofei Ivankov <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-10-18T21:55:24+05:30
summary:
gh-159049: Fix reference cycles after a failed TLS handshake in asyncio
(#159140)
files:
A Misc/NEWS.d/next/Library/2026-10-10-22-29-13.gh-issue-159049.eJUiKL.rst
M Lib/asyncio/base_events.py
M Lib/asyncio/selector_events.py
M Lib/test/test_asyncio/test_sslproto.py
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index 90269c936555cb5..9eb34c5f29fb551 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -1244,6 +1244,7 @@ async def _create_connection_transport(
await waiter
except:
transport.close()
+ waiter = None
raise
return transport, protocol
@@ -1381,6 +1382,7 @@ async def start_tls(self, transport, protocol,
sslcontext, *,
transport.close()
conmade_cb.cancel()
resume_cb.cancel()
+ waiter = None
raise
return ssl_protocol._app_transport
diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py
index bf20f053ded4353..f41fa47b891d4b1 100644
--- a/Lib/asyncio/selector_events.py
+++ b/Lib/asyncio/selector_events.py
@@ -1225,6 +1225,7 @@ def _call_connection_lost(self, exc):
super()._call_connection_lost(exc)
finally:
self._write_ready = None
+ self._read_ready_cb = None
if self._empty_waiter is not None:
self._empty_waiter.set_exception(
ConnectionError("Connection is closed by peer"))
diff --git a/Lib/test/test_asyncio/test_sslproto.py
b/Lib/test/test_asyncio/test_sslproto.py
index 253a470680ff052..acb80c19c0bdd0a 100644
--- a/Lib/test/test_asyncio/test_sslproto.py
+++ b/Lib/test/test_asyncio/test_sslproto.py
@@ -832,6 +832,68 @@ async def client(addr):
self.assertIsInstance(server_err, ssl.SSLError)
self.assertIn('ALERT_UNKNOWN_CA', server_err.reason or '')
+ def test_create_connection_ssl_failed_certificate_no_cycles(self):
+ # gh-159049
+ sslctx = test_utils.simple_server_sslcontext()
+ client_sslctx = test_utils.simple_client_sslcontext(
+ disable_verify=False)
+ transports = []
+
+ def server(sock):
+ try:
+ sock.start_tls(sslctx, server_side=True)
+ except OSError:
+ pass
+
+ connection_made = sslproto.SSLProtocol.connection_made
+
+ def remember_transport(proto, tr):
+ transports.append(weakref.ref(tr))
+ connection_made(proto, tr)
+
+ async def conn(addr):
+ try:
+ await self.loop.create_connection(
+ asyncio.Protocol, *addr,
+ ssl=client_sslctx, server_hostname='')
+ except ssl.SSLCertVerificationError:
+ pass
+
+ support.gc_collect()
+ with (self.tcp_server(server) as srv, support.disable_gc(),
+ mock.patch.object(sslproto.SSLProtocol, 'connection_made',
+ remember_transport)):
+ self.loop.run_until_complete(conn(srv.addr))
+ self.assertEqual([tr() for tr in transports], [None])
+
+ def test_start_tls_failed_certificate_no_cycles(self):
+ # gh-159049
+ sslctx = test_utils.simple_server_sslcontext()
+ client_sslctx = test_utils.simple_client_sslcontext(
+ disable_verify=False)
+ transports = []
+
+ def server(sock):
+ try:
+ sock.start_tls(sslctx, server_side=True)
+ except OSError:
+ pass
+
+ async def client(addr):
+ tr, proto = await self.loop.create_connection(
+ asyncio.Protocol, *addr)
+ transports.append(weakref.ref(tr))
+ try:
+ await self.loop.start_tls(
+ tr, proto, client_sslctx, server_hostname='')
+ except ssl.SSLCertVerificationError:
+ pass
+
+ support.gc_collect()
+ with self.tcp_server(server) as srv, support.disable_gc():
+ self.loop.run_until_complete(client(srv.addr))
+ self.assertEqual([tr() for tr in transports], [None])
+
def test_create_server_ssl_failed_handshake_sends_alert(self):
# gh-98078: when the handshake fails, the server must send the
# fatal TLS alert generated by OpenSSL to the client before
diff --git
a/Misc/NEWS.d/next/Library/2026-10-10-22-29-13.gh-issue-159049.eJUiKL.rst
b/Misc/NEWS.d/next/Library/2026-10-10-22-29-13.gh-issue-159049.eJUiKL.rst
new file mode 100644
index 000000000000000..96f1cc3c0520d57
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-10-22-29-13.gh-issue-159049.eJUiKL.rst
@@ -0,0 +1,3 @@
+Fix reference cycles in :meth:`asyncio.loop.create_connection` and
+:meth:`asyncio.loop.start_tls` after a failed TLS handshake.
+Patch by Timofei Ivankov.
_______________________________________________
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]