https://github.com/python/cpython/commit/c9e0918aad80ada2b4794bed64f117a1b295b7b7
commit: c9e0918aad80ada2b4794bed64f117a1b295b7b7
branch: 3.15
author: Timofei Ivankov <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-10-19T10:12:09+05:30
summary:
[3.15] gh-159049: Fix reference cycles after a failed TLS handshake in asyncio
(#159682)
gh-159049: Fix reference cycles after a failed TLS handshake in asyncio
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 7b6a67023381fe..59c61f9ba657a1 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -1243,6 +1243,7 @@ async def _create_connection_transport(
await waiter
except:
transport.close()
+ waiter = None
raise
return transport, protocol
@@ -1377,6 +1378,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 326d64025f3685..c335eaee7a7146 100644
--- a/Lib/asyncio/selector_events.py
+++ b/Lib/asyncio/selector_events.py
@@ -1222,6 +1222,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 455397024107e6..851dc6e127651c 100644
--- a/Lib/test/test_asyncio/test_sslproto.py
+++ b/Lib/test/test_asyncio/test_sslproto.py
@@ -824,6 +824,68 @@ async def client(addr):
with self.assertRaises(ssl.SSLCertVerificationError):
self.loop.run_until_complete(client(srv.addr))
+ 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_start_tls_client_corrupted_ssl(self):
self.loop.set_exception_handler(lambda loop, ctx: None)
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 00000000000000..96f1cc3c0520d5
--- /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]