Skip to content

Commit d748ca1

Browse files
committed
bpo-37228: Fix loop.create_datagram_endpoint()'s usage of SO_REUSEADDR
1 parent d672791 commit d748ca1

File tree

3 files changed

+30
-23
lines changed

3 files changed

+30
-23
lines changed

Doc/library/asyncio-eventloop.rst

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,13 @@ Opening network connections
473473
reuse_address=None, reuse_port=None, \
474474
allow_broadcast=None, sock=None)
475475

476+
.. note::
477+
The parameter *reuse_address* is no longer supported, as using
478+
:py:data:`~sockets.SO_REUSEADDR` poses a significant security concern for
479+
UDP. Explicitly passing `reuse_address=True` will raise an exception.
480+
For supported platforms, *reuse_port* can be used instead for similar
481+
functionality.
482+
476483
Create a datagram connection.
477484

478485
The socket family can be either :py:data:`~socket.AF_INET`,
@@ -501,11 +508,6 @@ Opening network connections
501508
resolution. If given, these should all be integers from the
502509
corresponding :mod:`socket` module constants.
503510

504-
* *reuse_address* tells the kernel to reuse a local socket in
505-
``TIME_WAIT`` state, without waiting for its natural timeout to
506-
expire. If not specified will automatically be set to ``True`` on
507-
Unix.
508-
509511
* *reuse_port* tells the kernel to allow this endpoint to be bound to the
510512
same port as other existing endpoints are bound to, so long as they all
511513
set this flag when being created. This option is not supported on Windows
@@ -527,6 +529,10 @@ Opening network connections
527529
The *family*, *proto*, *flags*, *reuse_address*, *reuse_port,
528530
*allow_broadcast*, and *sock* parameters were added.
529531

532+
.. versionchanged:: 3.5.10
533+
The *reuse_address* parameter is no longer supported due to security
534+
concerns.
535+
530536
.. versionchanged:: 3.8
531537
Added support for Windows.
532538

Lib/asyncio/base_events.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,8 +1306,12 @@ async def create_datagram_endpoint(self, protocol_factory,
13061306

13071307
exceptions = []
13081308

1309-
if reuse_address is None:
1310-
reuse_address = os.name == 'posix' and sys.platform != 'cygwin'
1309+
if reuse_address:
1310+
# bpo-37228
1311+
raise RuntimeError("Passing `reuse_address=True` is no "
1312+
"longer supported, as the usage of "
1313+
"SO_REUSEPORT in UDP poses a significant "
1314+
"security concern.")
13111315

13121316
for ((family, proto),
13131317
(local_address, remote_address)) in addr_pairs_info:
@@ -1316,9 +1320,6 @@ async def create_datagram_endpoint(self, protocol_factory,
13161320
try:
13171321
sock = socket.socket(
13181322
family=family, type=socket.SOCK_DGRAM, proto=proto)
1319-
if reuse_address:
1320-
sock.setsockopt(
1321-
socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
13221323
if reuse_port:
13231324
_set_reuseport(sock)
13241325
if allow_broadcast:

Lib/test/test_asyncio/test_base_events.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,7 +1752,6 @@ class FakeSock:
17521752

17531753
def test_create_datagram_endpoint_sockopts(self):
17541754
# Socket options should not be applied unless asked for.
1755-
# SO_REUSEADDR defaults to on for UNIX.
17561755
# SO_REUSEPORT is not available on all platforms.
17571756

17581757
coro = self.loop.create_datagram_endpoint(
@@ -1761,18 +1760,8 @@ def test_create_datagram_endpoint_sockopts(self):
17611760
transport, protocol = self.loop.run_until_complete(coro)
17621761
sock = transport.get_extra_info('socket')
17631762

1764-
reuse_address_default_on = (
1765-
os.name == 'posix' and sys.platform != 'cygwin')
17661763
reuseport_supported = hasattr(socket, 'SO_REUSEPORT')
17671764

1768-
if reuse_address_default_on:
1769-
self.assertTrue(
1770-
sock.getsockopt(
1771-
socket.SOL_SOCKET, socket.SO_REUSEADDR))
1772-
else:
1773-
self.assertFalse(
1774-
sock.getsockopt(
1775-
socket.SOL_SOCKET, socket.SO_REUSEADDR))
17761765
if reuseport_supported:
17771766
self.assertFalse(
17781767
sock.getsockopt(
@@ -1788,13 +1777,12 @@ def test_create_datagram_endpoint_sockopts(self):
17881777
coro = self.loop.create_datagram_endpoint(
17891778
lambda: MyDatagramProto(create_future=True, loop=self.loop),
17901779
local_addr=('127.0.0.1', 0),
1791-
reuse_address=True,
17921780
reuse_port=reuseport_supported,
17931781
allow_broadcast=True)
17941782
transport, protocol = self.loop.run_until_complete(coro)
17951783
sock = transport.get_extra_info('socket')
17961784

1797-
self.assertTrue(
1785+
self.assertFalse(
17981786
sock.getsockopt(
17991787
socket.SOL_SOCKET, socket.SO_REUSEADDR))
18001788
if reuseport_supported:
@@ -1809,6 +1797,18 @@ def test_create_datagram_endpoint_sockopts(self):
18091797
self.loop.run_until_complete(protocol.done)
18101798
self.assertEqual('CLOSED', protocol.state)
18111799

1800+
def test_create_datagram_endpoint_reuse_address_error(self):
1801+
# bpo-37228: Ensure that explicit passing of `reuse_address=True`
1802+
# raises an error, as it is not safe to use SO_REUSEADDR when using UDP
1803+
1804+
coro = self.loop.create_datagram_endpoint(
1805+
lambda: MyDatagramProto(create_future=True, loop=self.loop),
1806+
local_addr=('127.0.0.1', 0),
1807+
reuse_address=True)
1808+
1809+
with self.assertRaises(RuntimeError):
1810+
self.loop.run_until_complete(coro)
1811+
18121812
@patch_socket
18131813
def test_create_datagram_endpoint_nosoreuseport(self, m_socket):
18141814
del m_socket.SO_REUSEPORT

0 commit comments

Comments
 (0)