Skip to content

Commit c9e0918

Browse files
authored
[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
1 parent 7872a4e commit c9e0918

4 files changed

Lines changed: 68 additions & 0 deletions

File tree

‎Lib/asyncio/base_events.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,7 @@ async def _create_connection_transport(
12431243
await waiter
12441244
except:
12451245
transport.close()
1246+
waiter = None
12461247
raise
12471248

12481249
return transport, protocol
@@ -1377,6 +1378,7 @@ async def start_tls(self, transport, protocol, sslcontext, *,
13771378
transport.close()
13781379
conmade_cb.cancel()
13791380
resume_cb.cancel()
1381+
waiter = None
13801382
raise
13811383

13821384
return ssl_protocol._app_transport

‎Lib/asyncio/selector_events.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,7 @@ def _call_connection_lost(self, exc):
12221222
super()._call_connection_lost(exc)
12231223
finally:
12241224
self._write_ready = None
1225+
self._read_ready_cb = None
12251226
if self._empty_waiter is not None:
12261227
self._empty_waiter.set_exception(
12271228
ConnectionError("Connection is closed by peer"))

‎Lib/test/test_asyncio/test_sslproto.py‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,68 @@ async def client(addr):
824824
with self.assertRaises(ssl.SSLCertVerificationError):
825825
self.loop.run_until_complete(client(srv.addr))
826826

827+
def test_create_connection_ssl_failed_certificate_no_cycles(self):
828+
# gh-159049
829+
sslctx = test_utils.simple_server_sslcontext()
830+
client_sslctx = test_utils.simple_client_sslcontext(
831+
disable_verify=False)
832+
transports = []
833+
834+
def server(sock):
835+
try:
836+
sock.start_tls(sslctx, server_side=True)
837+
except OSError:
838+
pass
839+
840+
connection_made = sslproto.SSLProtocol.connection_made
841+
842+
def remember_transport(proto, tr):
843+
transports.append(weakref.ref(tr))
844+
connection_made(proto, tr)
845+
846+
async def conn(addr):
847+
try:
848+
await self.loop.create_connection(
849+
asyncio.Protocol, *addr,
850+
ssl=client_sslctx, server_hostname='')
851+
except ssl.SSLCertVerificationError:
852+
pass
853+
854+
support.gc_collect()
855+
with (self.tcp_server(server) as srv, support.disable_gc(),
856+
mock.patch.object(sslproto.SSLProtocol, 'connection_made',
857+
remember_transport)):
858+
self.loop.run_until_complete(conn(srv.addr))
859+
self.assertEqual([tr() for tr in transports], [None])
860+
861+
def test_start_tls_failed_certificate_no_cycles(self):
862+
# gh-159049
863+
sslctx = test_utils.simple_server_sslcontext()
864+
client_sslctx = test_utils.simple_client_sslcontext(
865+
disable_verify=False)
866+
transports = []
867+
868+
def server(sock):
869+
try:
870+
sock.start_tls(sslctx, server_side=True)
871+
except OSError:
872+
pass
873+
874+
async def client(addr):
875+
tr, proto = await self.loop.create_connection(
876+
asyncio.Protocol, *addr)
877+
transports.append(weakref.ref(tr))
878+
try:
879+
await self.loop.start_tls(
880+
tr, proto, client_sslctx, server_hostname='')
881+
except ssl.SSLCertVerificationError:
882+
pass
883+
884+
support.gc_collect()
885+
with self.tcp_server(server) as srv, support.disable_gc():
886+
self.loop.run_until_complete(client(srv.addr))
887+
self.assertEqual([tr() for tr in transports], [None])
888+
827889
def test_start_tls_client_corrupted_ssl(self):
828890
self.loop.set_exception_handler(lambda loop, ctx: None)
829891

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix reference cycles in :meth:`asyncio.loop.create_connection` and
2+
:meth:`asyncio.loop.start_tls` after a failed TLS handshake.
3+
Patch by Timofei Ivankov.

0 commit comments

Comments
 (0)