gh-155934: Fix socket leak and silent error for asyncio accepted connections (#155936)

kumaraditya303 <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/1461f0475b55154c0c4e033caae83e29b75f3e4d
commit: 1461f0475b55154c0c4e033caae83e29b75f3e4d
branch: main
author: Seungki Kim <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-08-23T10:10:56Z
summary:

gh-155934: Fix socket leak and silent error for asyncio accepted connections (#155936)

files:
A Misc/NEWS.d/next/Library/2026-08-17-18-00-00.gh-issue-155934.acCept.rst
M Lib/asyncio/selector_events.py
M Lib/test/test_asyncio/test_selector_events.py

diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py
index 83916160b9fbde..16d5c1b6f0a3e1 100644
--- a/Lib/asyncio/selector_events.py
+++ b/Lib/asyncio/selector_events.py
@@ -253,7 +253,9 @@ async def _accept_connection2(
         except (SystemExit, KeyboardInterrupt):
             raise
         except BaseException as exc:
-            if self._debug:
+            if transport is None:
+                conn.close()
+            if transport is None or self._debug:
                 context = {
                     'message':
                         'Error on transport creation for incoming connection',
diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py
index cf46c13fa5e1f3..a323084d262ebf 100644
--- a/Lib/test/test_asyncio/test_selector_events.py
+++ b/Lib/test/test_asyncio/test_selector_events.py
@@ -421,6 +421,60 @@ def test_accept_connection_reschedules_once_on_resource_error(self):
         self.assertEqual(self.loop.call_exception_handler.call_count, 1)
         self.assertEqual(self.loop.call_later.call_count, 1)
 
+    def test_accept_connection2_factory_error_closes_conn(self):
+        # gh-155934: if the transport was never created, the accepted
+        # socket is closed and the error is reported even when debug
+        # mode is disabled.
+        self.loop.set_debug(False)
+        conn = mock.Mock()
+
+        def factory():
+            raise RuntimeError("protocol_factory failed")
+
+        self.loop.call_exception_handler = mock.Mock()
+        self.loop.run_until_complete(
+            self.loop._accept_connection2(factory, conn, {}))
+
+        self.assertTrue(conn.close.called)
+        self.loop.call_exception_handler.assert_called_once()
+
+    def test_accept_connection2_transport_error_closes_conn(self):
+        # gh-155934: same when the transport creation itself fails.
+        self.loop.set_debug(False)
+        conn = mock.Mock()
+        self.loop._make_socket_transport = mock.Mock(
+            side_effect=ZeroDivisionError)
+        self.loop.call_exception_handler = mock.Mock()
+
+        self.loop.run_until_complete(
+            self.loop._accept_connection2(mock.Mock(), conn, {}))
+
+        self.assertTrue(conn.close.called)
+        self.loop.call_exception_handler.assert_called_once()
+
+    def test_accept_connection2_waiter_error_stays_debug_only(self):
+        # Once the transport exists it owns the socket: waiter failures
+        # (e.g. SSL handshake errors) close the transport and stay
+        # debug-only, and the accepted socket is not closed directly.
+        self.loop.set_debug(False)
+        conn = mock.Mock()
+        transport = mock.Mock()
+
+        def make_transport(conn, protocol, waiter=None, **kwargs):
+            waiter.set_exception(OSError("handshake failed"))
+            return transport
+
+        self.loop._make_socket_transport = make_transport
+        self.loop.call_exception_handler = mock.Mock()
+
+        self.loop.run_until_complete(
+            self.loop._accept_connection2(mock.Mock(), conn, {}))
+
+        self.assertTrue(transport.close.called)
+        self.assertFalse(conn.close.called)
+        self.assertFalse(self.loop.call_exception_handler.called)
+
+
 class SelectorTransportTests(test_utils.TestCase):
 
     def setUp(self):
diff --git a/Misc/NEWS.d/next/Library/2026-08-17-18-00-00.gh-issue-155934.acCept.rst b/Misc/NEWS.d/next/Library/2026-08-17-18-00-00.gh-issue-155934.acCept.rst
new file mode 100644
index 00000000000000..cc949bab5cb0bb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-17-18-00-00.gh-issue-155934.acCept.rst
@@ -0,0 +1,3 @@
+Fix a socket leak in :mod:`asyncio` when transport creation fails for a
+connection accepted by a server, and report the error via the loop exception
+handler even when debug mode is disabled.

_______________________________________________
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]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.