[Python-checkins] bpo-45129 Remove deprecated reuse_address (GH-28207)

ambv webhook-mailer at python.org
Wed Sep 8 12:58:51 EDT 2021


https://github.com/python/cpython/commit/59ea704df7a2fae4559e1e04f7a59d6c40f63657
commit: 59ea704df7a2fae4559e1e04f7a59d6c40f63657
branch: main
author: Hugo van Kemenade <hugovk at users.noreply.github.com>
committer: ambv <lukasz at langa.pl>
date: 2021-09-08T18:58:43+02:00
summary:

bpo-45129 Remove deprecated reuse_address (GH-28207)

Due to significant security concerns, the reuse_address parameter of
asyncio.loop.create_datagram_endpoint, deprecated in Python 3.9, is
now removed. This is because of the behavior of the socket option
SO_REUSEADDR in UDP.

Co-authored-by: Łukasz Langa <lukasz at langa.pl>

files:
A Misc/NEWS.d/next/Library/2021-09-07-14-27-39.bpo-45129.vXH0gw.rst
M Doc/library/asyncio-eventloop.rst
M Doc/whatsnew/3.11.rst
M Lib/asyncio/base_events.py
M Lib/test/test_asyncio/test_base_events.py

diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst
index 147fb2b9bf603d..c4408f122c3789 100644
--- a/Doc/library/asyncio-eventloop.rst
+++ b/Doc/library/asyncio-eventloop.rst
@@ -493,24 +493,9 @@ Opening network connections
 .. coroutinemethod:: loop.create_datagram_endpoint(protocol_factory, \
                         local_addr=None, remote_addr=None, *, \
                         family=0, proto=0, flags=0, \
-                        reuse_address=None, reuse_port=None, \
+                        reuse_port=None, \
                         allow_broadcast=None, sock=None)
 
-   .. note::
-      The parameter *reuse_address* is no longer supported, as using
-      :py:data:`~sockets.SO_REUSEADDR` poses a significant security concern for
-      UDP. Explicitly passing ``reuse_address=True`` will raise an exception.
-
-      When multiple processes with differing UIDs assign sockets to an
-      identical UDP socket address with ``SO_REUSEADDR``, incoming packets can
-      become randomly distributed among the sockets.
-
-      For supported platforms, *reuse_port* can be used as a replacement for
-      similar functionality. With *reuse_port*,
-      :py:data:`~sockets.SO_REUSEPORT` is used instead, which specifically
-      prevents processes with differing UIDs from assigning sockets to the same
-      socket address.
-
    Create a datagram connection.
 
    The socket family can be either :py:data:`~socket.AF_INET`,
@@ -557,16 +542,31 @@ Opening network connections
    :ref:`UDP echo server protocol <asyncio-udp-echo-server-protocol>` examples.
 
    .. versionchanged:: 3.4.4
-      The *family*, *proto*, *flags*, *reuse_address*, *reuse_port,
+      The *family*, *proto*, *flags*, *reuse_address*, *reuse_port*,
       *allow_broadcast*, and *sock* parameters were added.
 
    .. versionchanged:: 3.8.1
-      The *reuse_address* parameter is no longer supported due to security
-      concerns.
+      The *reuse_address* parameter is no longer supported, as using
+      :py:data:`~sockets.SO_REUSEADDR` poses a significant security concern for
+      UDP. Explicitly passing ``reuse_address=True`` will raise an exception.
+
+      When multiple processes with differing UIDs assign sockets to an
+      identical UDP socket address with ``SO_REUSEADDR``, incoming packets can
+      become randomly distributed among the sockets.
+
+      For supported platforms, *reuse_port* can be used as a replacement for
+      similar functionality. With *reuse_port*,
+      :py:data:`~sockets.SO_REUSEPORT` is used instead, which specifically
+      prevents processes with differing UIDs from assigning sockets to the same
+      socket address.
 
    .. versionchanged:: 3.8
       Added support for Windows.
 
+   .. versionchanged:: 3.11
+      The *reuse_address* parameter, disabled since Python 3.9.0, 3.8.1,
+      3.7.6 and 3.6.10, has been entirely removed.
+
 .. coroutinemethod:: loop.create_unix_connection(protocol_factory, \
                         path=None, *, ssl=None, sock=None, \
                         server_hostname=None, ssl_handshake_timeout=None)
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index 55ba8329a1f781..64bdc0f42f9e2e 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -254,6 +254,12 @@ Removed
   Use ``bdist_wheel`` (wheel packages) instead.
   (Contributed by Hugo van Kemenade in :issue:`45124`.)
 
+* Due to significant security concerns, the *reuse_address* parameter of
+  :meth:`asyncio.loop.create_datagram_endpoint`, disabled in Python 3.9, is
+  now entirely removed. This is because of the behavior of the socket option
+  ``SO_REUSEADDR`` in UDP.
+  (Contributed by Hugo van Kemenade in :issue:`45129`.)
+
 * Remove :meth:`__getitem__` methods of
   :class:`xml.dom.pulldom.DOMEventStream`, :class:`wsgiref.util.FileWrapper`
   and :class:`fileinput.FileInput`, deprecated since Python 3.9.
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index 125d32da889935..054d7b45ec2d64 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -66,10 +66,6 @@
 # Maximum timeout passed to select to avoid OS limitations
 MAXIMUM_SELECT_TIMEOUT = 24 * 3600
 
-# Used for deprecation and removal of `loop.create_datagram_endpoint()`'s
-# *reuse_address* parameter
-_unset = object()
-
 
 def _format_handle(handle):
     cb = handle._callback
@@ -1235,7 +1231,7 @@ async def start_tls(self, transport, protocol, sslcontext, *,
     async def create_datagram_endpoint(self, protocol_factory,
                                        local_addr=None, remote_addr=None, *,
                                        family=0, proto=0, flags=0,
-                                       reuse_address=_unset, reuse_port=None,
+                                       reuse_port=None,
                                        allow_broadcast=None, sock=None):
         """Create datagram connection."""
         if sock is not None:
@@ -1248,7 +1244,7 @@ async def create_datagram_endpoint(self, protocol_factory,
                 # show the problematic kwargs in exception msg
                 opts = dict(local_addr=local_addr, remote_addr=remote_addr,
                             family=family, proto=proto, flags=flags,
-                            reuse_address=reuse_address, reuse_port=reuse_port,
+                            reuse_port=reuse_port,
                             allow_broadcast=allow_broadcast)
                 problems = ', '.join(f'{k}={v}' for k, v in opts.items() if v)
                 raise ValueError(
@@ -1311,19 +1307,6 @@ async def create_datagram_endpoint(self, protocol_factory,
 
             exceptions = []
 
-            # bpo-37228
-            if reuse_address is not _unset:
-                if reuse_address:
-                    raise ValueError("Passing `reuse_address=True` is no "
-                                     "longer supported, as the usage of "
-                                     "SO_REUSEPORT in UDP poses a significant "
-                                     "security concern.")
-                else:
-                    warnings.warn("The *reuse_address* parameter has been "
-                                  "deprecated as of 3.5.10 and is scheduled "
-                                  "for removal in 3.11.", DeprecationWarning,
-                                  stacklevel=2)
-
             for ((family, proto),
                  (local_address, remote_address)) in addr_pairs_info:
                 sock = None
@@ -1407,7 +1390,6 @@ async def create_server(
             sock=None,
             backlog=100,
             ssl=None,
-            reuse_address=None,
             reuse_port=None,
             ssl_handshake_timeout=None,
             start_serving=True):
@@ -1438,8 +1420,6 @@ async def create_server(
                 raise ValueError(
                     'host/port and sock can not be specified at the same time')
 
-            if reuse_address is None:
-                reuse_address = os.name == 'posix' and sys.platform != 'cygwin'
             sockets = []
             if host == '':
                 hosts = [None]
@@ -1469,9 +1449,6 @@ async def create_server(
                                            af, socktype, proto, exc_info=True)
                         continue
                     sockets.append(sock)
-                    if reuse_address:
-                        sock.setsockopt(
-                            socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
                     if reuse_port:
                         _set_reuseport(sock)
                     # Disable IPv4/IPv6 dual stack support (enabled by
diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py
index adc7bd42c1508d..b522fac23a23bd 100644
--- a/Lib/test/test_asyncio/test_base_events.py
+++ b/Lib/test/test_asyncio/test_base_events.py
@@ -1794,32 +1794,6 @@ def test_create_datagram_endpoint_sockopts(self):
         self.loop.run_until_complete(protocol.done)
         self.assertEqual('CLOSED', protocol.state)
 
-    def test_create_datagram_endpoint_reuse_address_error(self):
-        # bpo-37228: Ensure that explicit passing of `reuse_address=True`
-        # raises an error, as it is not safe to use SO_REUSEADDR when using UDP
-
-        coro = self.loop.create_datagram_endpoint(
-            lambda: MyDatagramProto(create_future=True, loop=self.loop),
-            local_addr=('127.0.0.1', 0),
-            reuse_address=True)
-
-        with self.assertRaises(ValueError):
-            self.loop.run_until_complete(coro)
-
-    def test_create_datagram_endpoint_reuse_address_warning(self):
-        # bpo-37228: Deprecate *reuse_address* parameter
-
-        coro = self.loop.create_datagram_endpoint(
-            lambda: MyDatagramProto(create_future=True, loop=self.loop),
-            local_addr=('127.0.0.1', 0),
-            reuse_address=False)
-
-        with self.assertWarns(DeprecationWarning):
-            transport, protocol = self.loop.run_until_complete(coro)
-            transport.close()
-            self.loop.run_until_complete(protocol.done)
-            self.assertEqual('CLOSED', protocol.state)
-
     @patch_socket
     def test_create_datagram_endpoint_nosoreuseport(self, m_socket):
         del m_socket.SO_REUSEPORT
diff --git a/Misc/NEWS.d/next/Library/2021-09-07-14-27-39.bpo-45129.vXH0gw.rst b/Misc/NEWS.d/next/Library/2021-09-07-14-27-39.bpo-45129.vXH0gw.rst
new file mode 100644
index 00000000000000..5ba6721923dbdb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-09-07-14-27-39.bpo-45129.vXH0gw.rst
@@ -0,0 +1,6 @@
+Due to significant security concerns, the *reuse_address* parameter of
+:meth:`asyncio.loop.create_datagram_endpoint`, disabled in Python 3.9, is
+now entirely removed. This is because of the behavior of the socket option
+``SO_REUSEADDR`` in UDP.
+
+Patch by Hugo van Kemenade.



More information about the Python-checkins mailing list