From b9ae8e279038c65e4a81867e518bcb35719f30b6 Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Wed, 15 Jan 2025 22:32:22 +0800 Subject: [PATCH 01/19] Fix address and flags for send_fds/recv_fds --- Lib/socket.py | 5 ++- Lib/test/test_socket.py | 84 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/Lib/socket.py b/Lib/socket.py index be37c24d6174a2..4b8aa575b74594 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -563,7 +563,8 @@ def send_fds(sock, buffers, fds, flags=0, address=None): import array return sock.sendmsg(buffers, [(_socket.SOL_SOCKET, - _socket.SCM_RIGHTS, array.array("i", fds))]) + _socket.SCM_RIGHTS, array.array("i", fds))], + flags, address) __all__.append("send_fds") if hasattr(_socket.socket, "recvmsg"): @@ -579,7 +580,7 @@ def recv_fds(sock, bufsize, maxfds, flags=0): # Array of ints fds = array.array("i") msg, ancdata, flags, addr = sock.recvmsg(bufsize, - _socket.CMSG_LEN(maxfds * fds.itemsize)) + _socket.CMSG_LEN(maxfds * fds.itemsize), flags) for cmsg_level, cmsg_type, cmsg_data in ancdata: if (cmsg_level == _socket.SOL_SOCKET and cmsg_type == _socket.SCM_RIGHTS): fds.frombytes(cmsg_data[: diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index faf326d9164e1b..ce65f90c5f4bb1 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7037,6 +7037,12 @@ def test_dual_stack_client_v6(self): @requireAttrs(socket, "recv_fds") @requireAttrs(socket, "AF_UNIX") class SendRecvFdsTests(unittest.TestCase): + def _test_pipe(self, rfd, wfd, msg): + assert len(msg) < 512 + os.write(wfd, msg) + data = os.read(rfd, 512) + self.assertEqual(data, msg) + def testSendAndRecvFds(self): def close_pipes(pipes): for fd1, fd2 in pipes: @@ -7066,13 +7072,79 @@ def close_fds(fds): # don't test addr # test that file descriptors are connected - for index, fds in enumerate(pipes): - rfd, wfd = fds - os.write(wfd, str(index).encode()) + for index, ((_, wfd), rfd) in enumerate(zip(pipes, fds2)): + self._test_pipe(rfd, wfd, str(index).encode()) + + def test_send_recv_fds_with_addrs(self): + sock1 = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + sock2 = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + rfd, wfd = os.pipe() + self.addCleanup(os.close, rfd) + self.addCleanup(os.close, wfd) + + with tempfile.TemporaryDirectory() as tmpdir, sock1, sock2: + sock1_addr = os.path.join(tmpdir, "sock1") + sock2_addr = os.path.join(tmpdir, "sock2") + sock1.bind(sock1_addr) + sock2.bind(sock2_addr) + sock2.setblocking(False) + + socket.send_fds(sock1, [MSG], [rfd], address=sock2_addr) + msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 1) + new_rfd = fds[0] + self.addCleanup(os.close, new_rfd) + + self.assertEqual(msg, MSG) + self.assertEqual(len(fds), 1) + self.assertEqual(addr, sock1_addr) + + self._test_pipe(new_rfd, wfd, MSG) + + @requireAttrs(socket, "MSG_PEEK") + def test_recv_fds_peek(self): + rfd, wfd = os.pipe() + self.addCleanup(os.close, rfd) + self.addCleanup(os.close, wfd) - for index, rfd in enumerate(fds2): - data = os.read(rfd, 100) - self.assertEqual(data, str(index).encode()) + sock1, sock2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM) + with sock1, sock2: + socket.send_fds(sock1, [MSG], [rfd]) + sock2.setblocking(False) + + # peek message on sock2 + peek_len = len(MSG) // 2 + msg, fds, flags, addr = socket.recv_fds(sock2, peek_len, 1, + socket.MSG_PEEK) + self.addCleanup(os.close, fds[0]) + self.assertEqual(len(msg), peek_len) + self.assertEqual(msg, MSG[:peek_len]) + self.assertEqual(flags & socket.MSG_TRUNC, socket.MSG_TRUNC) + self._test_pipe(fds[0], wfd, MSG) + + # will raise BlockingIOError if MSG_PEEK didn't work + msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 1) + self.addCleanup(os.close, fds[0]) + self.assertEqual(msg, MSG) + self._test_pipe(fds[0], wfd, MSG) + + @requireAttrs(socket, "MSG_DONTWAIT") + def test_send_fds_dontwait(self): + rfd, wfd = os.pipe() + self.addCleanup(os.close, rfd) + self.addCleanup(os.close, wfd) + + sock1, sock2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM) + with sock1, sock2: + sock1.setblocking(True) + with self.assertRaises(BlockingIOError): + for _ in range(64 * 1024): + socket.send_fds(sock1, [MSG], [rfd], socket.MSG_DONTWAIT) + + msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 1) + self.addCleanup(os.close, fds[0]) + + self.assertEqual(msg, MSG) + self._test_pipe(fds[0], wfd, MSG) class FreeThreadingTests(unittest.TestCase): From 45b28f67b5fb40ce905974716ab21340a6bcfd5d Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Wed, 15 Jan 2025 22:51:04 +0800 Subject: [PATCH 02/19] Add NEWS --- .../next/Library/2025-01-15-22-50-41.gh-issue-128881.JBL_9E.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-01-15-22-50-41.gh-issue-128881.JBL_9E.rst diff --git a/Misc/NEWS.d/next/Library/2025-01-15-22-50-41.gh-issue-128881.JBL_9E.rst b/Misc/NEWS.d/next/Library/2025-01-15-22-50-41.gh-issue-128881.JBL_9E.rst new file mode 100644 index 00000000000000..4c259b8a26f03b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-15-22-50-41.gh-issue-128881.JBL_9E.rst @@ -0,0 +1,2 @@ +Fix ``flags`` and ``address`` parameters which were ignored in +``socket.send_fds`` and ``socket.recv_fds``. From fbb26680d698fb897384caf89281b0f7572070ce Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Wed, 15 Jan 2025 23:01:33 +0800 Subject: [PATCH 03/19] Only test MSG_DONTWAIT on Linux --- Lib/test/test_socket.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index ce65f90c5f4bb1..2d73b1d0cfd3e6 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7128,6 +7128,7 @@ def test_recv_fds_peek(self): self._test_pipe(fds[0], wfd, MSG) @requireAttrs(socket, "MSG_DONTWAIT") + @unittest.skipUnless(sys.platform in ('linux', 'android'), 'Linux specific test') def test_send_fds_dontwait(self): rfd, wfd = os.pipe() self.addCleanup(os.close, rfd) From c58d2de6d27ae5bb1a0ed405aa10776de8d40cca Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Fri, 17 Jan 2025 00:30:53 +0800 Subject: [PATCH 04/19] Fix cleanup fds --- Lib/test/test_socket.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 2d73b1d0cfd3e6..b33252689ea848 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7037,6 +7037,12 @@ def test_dual_stack_client_v6(self): @requireAttrs(socket, "recv_fds") @requireAttrs(socket, "AF_UNIX") class SendRecvFdsTests(unittest.TestCase): + def _cleanup_fds(self, fds): + def close_fds(fds): + for fd in fds: + os.close(fd) + self.addCleanup(close_fds, fds) + def _test_pipe(self, rfd, wfd, msg): assert len(msg) < 512 os.write(wfd, msg) @@ -7044,18 +7050,9 @@ def _test_pipe(self, rfd, wfd, msg): self.assertEqual(data, msg) def testSendAndRecvFds(self): - def close_pipes(pipes): - for fd1, fd2 in pipes: - os.close(fd1) - os.close(fd2) - - def close_fds(fds): - for fd in fds: - os.close(fd) - # send 10 file descriptors pipes = [os.pipe() for _ in range(10)] - self.addCleanup(close_pipes, pipes) + self._cleanup_fds(fd for pair in pipes for fd in pair) fds = [rfd for rfd, wfd in pipes] # use a UNIX socket pair to exchange file descriptors locally @@ -7064,7 +7061,7 @@ def close_fds(fds): socket.send_fds(sock1, [MSG], fds) # request more data and file descriptors than expected msg, fds2, flags, addr = socket.recv_fds(sock2, len(MSG) * 2, len(fds) * 2) - self.addCleanup(close_fds, fds2) + self._cleanup_fds(fds2) self.assertEqual(msg, MSG) self.assertEqual(len(fds2), len(fds)) @@ -7091,14 +7088,13 @@ def test_send_recv_fds_with_addrs(self): socket.send_fds(sock1, [MSG], [rfd], address=sock2_addr) msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 1) - new_rfd = fds[0] - self.addCleanup(os.close, new_rfd) + self._cleanup_fds(fds) self.assertEqual(msg, MSG) self.assertEqual(len(fds), 1) self.assertEqual(addr, sock1_addr) - self._test_pipe(new_rfd, wfd, MSG) + self._test_pipe(fds[0], wfd, MSG) @requireAttrs(socket, "MSG_PEEK") def test_recv_fds_peek(self): @@ -7115,16 +7111,20 @@ def test_recv_fds_peek(self): peek_len = len(MSG) // 2 msg, fds, flags, addr = socket.recv_fds(sock2, peek_len, 1, socket.MSG_PEEK) - self.addCleanup(os.close, fds[0]) + self._cleanup_fds(fds) + self.assertEqual(len(msg), peek_len) self.assertEqual(msg, MSG[:peek_len]) self.assertEqual(flags & socket.MSG_TRUNC, socket.MSG_TRUNC) + self.assertEqual(len(fds), 1) self._test_pipe(fds[0], wfd, MSG) # will raise BlockingIOError if MSG_PEEK didn't work msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 1) - self.addCleanup(os.close, fds[0]) + self._cleanup_fds(fds) + self.assertEqual(msg, MSG) + self.assertEqual(len(fds), 1) self._test_pipe(fds[0], wfd, MSG) @requireAttrs(socket, "MSG_DONTWAIT") @@ -7142,9 +7142,10 @@ def test_send_fds_dontwait(self): socket.send_fds(sock1, [MSG], [rfd], socket.MSG_DONTWAIT) msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 1) - self.addCleanup(os.close, fds[0]) + self._cleanup_fds(fds) self.assertEqual(msg, MSG) + self.assertEqual(len(fds), 1) self._test_pipe(fds[0], wfd, MSG) From aefccca1d15cfeb235d9dc791efd1d97f85e0c6a Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Fri, 17 Jan 2025 00:39:00 +0800 Subject: [PATCH 05/19] Skip platform-dependent tests --- Lib/test/test_socket.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index b33252689ea848..d80e45a75e6aa9 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7072,6 +7072,8 @@ def testSendAndRecvFds(self): for index, ((_, wfd), rfd) in enumerate(zip(pipes, fds2)): self._test_pipe(rfd, wfd, str(index).encode()) + @unittest.skipUnless(sys.platform in ("linux", "android", "darwin"), + "works on Linux and macOS") def test_send_recv_fds_with_addrs(self): sock1 = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) sock2 = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) @@ -7097,6 +7099,7 @@ def test_send_recv_fds_with_addrs(self): self._test_pipe(fds[0], wfd, MSG) @requireAttrs(socket, "MSG_PEEK") + @unittest.skipUnless(sys.platform in ("linux", "android"), "works on Linux") def test_recv_fds_peek(self): rfd, wfd = os.pipe() self.addCleanup(os.close, rfd) @@ -7128,7 +7131,7 @@ def test_recv_fds_peek(self): self._test_pipe(fds[0], wfd, MSG) @requireAttrs(socket, "MSG_DONTWAIT") - @unittest.skipUnless(sys.platform in ('linux', 'android'), 'Linux specific test') + @unittest.skipUnless(sys.platform in ("linux", "android"), "Linux specific test") def test_send_fds_dontwait(self): rfd, wfd = os.pipe() self.addCleanup(os.close, rfd) From 6b58f4b6f74341f6eae18ee21edb9842a5c623a6 Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Sat, 18 Jan 2025 17:31:38 +0800 Subject: [PATCH 06/19] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_socket.py | 2 +- .../next/Library/2025-01-15-22-50-41.gh-issue-128881.JBL_9E.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index d80e45a75e6aa9..6d3864f0d5791f 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7113,7 +7113,7 @@ def test_recv_fds_peek(self): # peek message on sock2 peek_len = len(MSG) // 2 msg, fds, flags, addr = socket.recv_fds(sock2, peek_len, 1, - socket.MSG_PEEK) + flags=socket.MSG_PEEK) self._cleanup_fds(fds) self.assertEqual(len(msg), peek_len) diff --git a/Misc/NEWS.d/next/Library/2025-01-15-22-50-41.gh-issue-128881.JBL_9E.rst b/Misc/NEWS.d/next/Library/2025-01-15-22-50-41.gh-issue-128881.JBL_9E.rst index 4c259b8a26f03b..7c2a874e048d59 100644 --- a/Misc/NEWS.d/next/Library/2025-01-15-22-50-41.gh-issue-128881.JBL_9E.rst +++ b/Misc/NEWS.d/next/Library/2025-01-15-22-50-41.gh-issue-128881.JBL_9E.rst @@ -1,2 +1,2 @@ Fix ``flags`` and ``address`` parameters which were ignored in -``socket.send_fds`` and ``socket.recv_fds``. +:func:`socket.send_fds` and :func:`socket.recv_fds`. From 0134f01794b3e613d56a8c43a2413b145407fd53 Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Mon, 2 Jun 2025 11:21:58 +0800 Subject: [PATCH 07/19] fix test on FreeBSD --- Lib/test/test_socket.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index d6acc504827241..1baf70aa9c25ba 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7362,8 +7362,6 @@ def testSendAndRecvFds(self): for index, ((_, wfd), rfd) in enumerate(zip(pipes, fds2)): self._test_pipe(rfd, wfd, str(index).encode()) - @unittest.skipUnless(sys.platform in ("linux", "android", "darwin"), - "works on Linux and macOS") def test_send_recv_fds_with_addrs(self): sock1 = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) sock2 = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) @@ -7379,10 +7377,18 @@ def test_send_recv_fds_with_addrs(self): sock2.setblocking(False) socket.send_fds(sock1, [MSG], [rfd], address=sock2_addr) - msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 1) + if sys.platform.startswith("freebsd"): + # FreeBSD requires at least CMSG_LEN(2 * sizeof(int)), otherwise + # the cmsg will be truncated + recv_fds_len = 2 + else: + recv_fds_len = 1 + msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), recv_fds_len) self._cleanup_fds(fds) self.assertEqual(msg, MSG) + if hasattr(socket, "MSG_CTRUNC"): + self.assertEqual(flags & socket.MSG_CTRUNC, 0) self.assertEqual(len(fds), 1) self.assertEqual(addr, sock1_addr) @@ -7409,6 +7415,8 @@ def test_recv_fds_peek(self): self.assertEqual(len(msg), peek_len) self.assertEqual(msg, MSG[:peek_len]) self.assertEqual(flags & socket.MSG_TRUNC, socket.MSG_TRUNC) + if hasattr(socket, "MSG_CTRUNC"): + self.assertEqual(flags & socket.MSG_CTRUNC, 0) self.assertEqual(len(fds), 1) self._test_pipe(fds[0], wfd, MSG) @@ -7417,6 +7425,8 @@ def test_recv_fds_peek(self): self._cleanup_fds(fds) self.assertEqual(msg, MSG) + if hasattr(socket, "MSG_CTRUNC"): + self.assertEqual(flags & socket.MSG_CTRUNC, 0) self.assertEqual(len(fds), 1) self._test_pipe(fds[0], wfd, MSG) From 5cca1b41aa1459a77f96e00a1d0ea50af2981dcb Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Mon, 2 Jun 2025 12:05:16 +0800 Subject: [PATCH 08/19] fix test_send_fds_dontwait on *BSD --- Lib/test/test_socket.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 1baf70aa9c25ba..8679d8eadb4f46 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7431,20 +7431,27 @@ def test_recv_fds_peek(self): self._test_pipe(fds[0], wfd, MSG) @requireAttrs(socket, "MSG_DONTWAIT") - @unittest.skipUnless(sys.platform in ("linux", "android"), "Linux specific test") def test_send_fds_dontwait(self): rfd, wfd = os.pipe() self.addCleanup(os.close, rfd) self.addCleanup(os.close, wfd) - sock1, sock2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM) + # use SOCK_STREAM instead of SOCK_DGRAM to support *BSD platforms + # ref: https://docs.python.org/3/library/asyncio-protocol.html#datagram-protocols + sock1, sock2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM) with sock1, sock2: sock1.setblocking(True) with self.assertRaises(BlockingIOError): for _ in range(64 * 1024): socket.send_fds(sock1, [MSG], [rfd], socket.MSG_DONTWAIT) - msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 1) + if sys.platform.startswith("freebsd"): + # FreeBSD requires at least CMSG_LEN(2 * sizeof(int)), otherwise + # the cmsg will be truncated + recv_fds_len = 2 + else: + recv_fds_len = 1 + msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), recv_fds_len) self._cleanup_fds(fds) self.assertEqual(msg, MSG) From 6c226c47759d6739c4a79c6efe64e640a291fbc4 Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Mon, 2 Jun 2025 15:30:35 +0800 Subject: [PATCH 09/19] skip sendmsg with MSG_DONTWAIT test on macOS --- Lib/test/test_socket.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 8679d8eadb4f46..efab99512e0742 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -3516,7 +3516,7 @@ def _testSendmsgTimeout(self): # Linux supports MSG_DONTWAIT when sending, but in general, it # only works when receiving. Could add other platforms if they # support it too. - @skipWithClientIf(sys.platform not in {"linux", "android"}, + @skipWithClientIf(sys.platform not in {"linux", "android", "freebsd"}, "MSG_DONTWAIT not known to work on this platform when " "sending") def testSendmsgDontWait(self): @@ -7431,6 +7431,9 @@ def test_recv_fds_peek(self): self._test_pipe(fds[0], wfd, MSG) @requireAttrs(socket, "MSG_DONTWAIT") + @unittest.skipIf(sys.platform in ("darwin",), + "MSG_DONTWAIT not known to work on this platform when " + "sending") def test_send_fds_dontwait(self): rfd, wfd = os.pipe() self.addCleanup(os.close, rfd) From b13010897643b3949810a036cb4ad0edbd9e5c50 Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Mon, 2 Jun 2025 16:04:24 +0800 Subject: [PATCH 10/19] try enabling test_recv_fds_peek on macOS --- Lib/test/test_socket.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index efab99512e0742..0ac31e0eb8f6e6 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7395,7 +7395,9 @@ def test_send_recv_fds_with_addrs(self): self._test_pipe(fds[0], wfd, MSG) @requireAttrs(socket, "MSG_PEEK") - @unittest.skipUnless(sys.platform in ("linux", "android"), "works on Linux") + @unittest.skipUnless(sys.platform in ("linux", "android", "darwin"), + "MSG_PEEK with SCM_RIGHTS not known to work on this " + "platform") def test_recv_fds_peek(self): rfd, wfd = os.pipe() self.addCleanup(os.close, rfd) @@ -7408,7 +7410,7 @@ def test_recv_fds_peek(self): # peek message on sock2 peek_len = len(MSG) // 2 - msg, fds, flags, addr = socket.recv_fds(sock2, peek_len, 1, + msg, fds, flags, addr = socket.recv_fds(sock2, peek_len, 2, flags=socket.MSG_PEEK) self._cleanup_fds(fds) @@ -7421,7 +7423,7 @@ def test_recv_fds_peek(self): self._test_pipe(fds[0], wfd, MSG) # will raise BlockingIOError if MSG_PEEK didn't work - msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 1) + msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 2) self._cleanup_fds(fds) self.assertEqual(msg, MSG) From bb1c5adf94c2462705dfb8f8c14380c0d19b1f84 Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Mon, 2 Jun 2025 16:10:23 +0800 Subject: [PATCH 11/19] Revert "try enabling test_recv_fds_peek on macOS" This reverts commit b13010897643b3949810a036cb4ad0edbd9e5c50. --- Lib/test/test_socket.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 0ac31e0eb8f6e6..efab99512e0742 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7395,9 +7395,7 @@ def test_send_recv_fds_with_addrs(self): self._test_pipe(fds[0], wfd, MSG) @requireAttrs(socket, "MSG_PEEK") - @unittest.skipUnless(sys.platform in ("linux", "android", "darwin"), - "MSG_PEEK with SCM_RIGHTS not known to work on this " - "platform") + @unittest.skipUnless(sys.platform in ("linux", "android"), "works on Linux") def test_recv_fds_peek(self): rfd, wfd = os.pipe() self.addCleanup(os.close, rfd) @@ -7410,7 +7408,7 @@ def test_recv_fds_peek(self): # peek message on sock2 peek_len = len(MSG) // 2 - msg, fds, flags, addr = socket.recv_fds(sock2, peek_len, 2, + msg, fds, flags, addr = socket.recv_fds(sock2, peek_len, 1, flags=socket.MSG_PEEK) self._cleanup_fds(fds) @@ -7423,7 +7421,7 @@ def test_recv_fds_peek(self): self._test_pipe(fds[0], wfd, MSG) # will raise BlockingIOError if MSG_PEEK didn't work - msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 2) + msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 1) self._cleanup_fds(fds) self.assertEqual(msg, MSG) From 02b0ddd5501769c35269a3e02faa5b7a3b981a05 Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Mon, 2 Jun 2025 18:46:41 +0800 Subject: [PATCH 12/19] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit update comments Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_socket.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index efab99512e0742..adf114cc7eb6fa 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7378,8 +7378,8 @@ def test_send_recv_fds_with_addrs(self): socket.send_fds(sock1, [MSG], [rfd], address=sock2_addr) if sys.platform.startswith("freebsd"): - # FreeBSD requires at least CMSG_LEN(2 * sizeof(int)), otherwise - # the cmsg will be truncated + # FreeBSD requires at least CMSG_LEN(2*sizeof(int)), + # otherwise the access control message is truncated. recv_fds_len = 2 else: recv_fds_len = 1 @@ -7449,8 +7449,8 @@ def test_send_fds_dontwait(self): socket.send_fds(sock1, [MSG], [rfd], socket.MSG_DONTWAIT) if sys.platform.startswith("freebsd"): - # FreeBSD requires at least CMSG_LEN(2 * sizeof(int)), otherwise - # the cmsg will be truncated + # FreeBSD requires at least CMSG_LEN(2*sizeof(int)), + # otherwise the access control message is truncated. recv_fds_len = 2 else: recv_fds_len = 1 From 7368656402ecdc4c2d700b01fd764b89d63d5339 Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Mon, 2 Jun 2025 18:53:43 +0800 Subject: [PATCH 13/19] extract the logic into _recv_one_fd method --- Lib/test/test_socket.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index adf114cc7eb6fa..ee004ac539c9d0 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7339,6 +7339,16 @@ def _test_pipe(self, rfd, wfd, msg): data = os.read(rfd, 512) self.assertEqual(data, msg) + @staticmethod + def _recv_one_fd(sock, bufsize, flags=0): + if sys.platform.startswith("freebsd"): + # FreeBSD requires at least CMSG_LEN(2*sizeof(int)), + # otherwise the access control message is truncated. + max_fds = 2 + else: + max_fds = 1 + return socket.recv_fds(sock, bufsize, max_fds, flags) + def testSendAndRecvFds(self): # send 10 file descriptors pipes = [os.pipe() for _ in range(10)] @@ -7377,13 +7387,7 @@ def test_send_recv_fds_with_addrs(self): sock2.setblocking(False) socket.send_fds(sock1, [MSG], [rfd], address=sock2_addr) - if sys.platform.startswith("freebsd"): - # FreeBSD requires at least CMSG_LEN(2*sizeof(int)), - # otherwise the access control message is truncated. - recv_fds_len = 2 - else: - recv_fds_len = 1 - msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), recv_fds_len) + msg, fds, flags, addr = self._recv_one_fd(sock2, len(MSG)) self._cleanup_fds(fds) self.assertEqual(msg, MSG) @@ -7448,13 +7452,7 @@ def test_send_fds_dontwait(self): for _ in range(64 * 1024): socket.send_fds(sock1, [MSG], [rfd], socket.MSG_DONTWAIT) - if sys.platform.startswith("freebsd"): - # FreeBSD requires at least CMSG_LEN(2*sizeof(int)), - # otherwise the access control message is truncated. - recv_fds_len = 2 - else: - recv_fds_len = 1 - msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), recv_fds_len) + msg, fds, flags, addr = self._recv_one_fd(sock2, len(MSG)) self._cleanup_fds(fds) self.assertEqual(msg, MSG) From 4f8d1ca57c2da27dd70088702c0598d198ae019f Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Mon, 2 Jun 2025 18:57:52 +0800 Subject: [PATCH 14/19] make sockets live only as little as possible --- Lib/test/test_socket.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index ee004ac539c9d0..e9f42726f985dc 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7373,13 +7373,13 @@ def testSendAndRecvFds(self): self._test_pipe(rfd, wfd, str(index).encode()) def test_send_recv_fds_with_addrs(self): - sock1 = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) - sock2 = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) rfd, wfd = os.pipe() self.addCleanup(os.close, rfd) self.addCleanup(os.close, wfd) - with tempfile.TemporaryDirectory() as tmpdir, sock1, sock2: + with tempfile.TemporaryDirectory() as tmpdir, \ + socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) as sock1, \ + socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) as sock2: sock1_addr = os.path.join(tmpdir, "sock1") sock2_addr = os.path.join(tmpdir, "sock2") sock1.bind(sock1_addr) From c42372306d4cd72aeff182419fe874c41353eed2 Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Sun, 22 Jun 2025 17:56:45 +0800 Subject: [PATCH 15/19] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_socket.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index e9f42726f985dc..8436523ecbe2fd 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7349,7 +7349,7 @@ def _recv_one_fd(sock, bufsize, flags=0): max_fds = 1 return socket.recv_fds(sock, bufsize, max_fds, flags) - def testSendAndRecvFds(self): + def test_send_and_recv_fds(self): # send 10 file descriptors pipes = [os.pipe() for _ in range(10)] self._cleanup_fds(fd for pair in pipes for fd in pair) @@ -7369,7 +7369,7 @@ def testSendAndRecvFds(self): # don't test addr # test that file descriptors are connected - for index, ((_, wfd), rfd) in enumerate(zip(pipes, fds2)): + for index, ((_, wfd), rfd) in enumerate(zip(pipes, fds2, strict=True)): self._test_pipe(rfd, wfd, str(index).encode()) def test_send_recv_fds_with_addrs(self): @@ -7412,6 +7412,7 @@ def test_recv_fds_peek(self): # peek message on sock2 peek_len = len(MSG) // 2 + self.assertGreater(peek_len, 0) msg, fds, flags, addr = socket.recv_fds(sock2, peek_len, 1, flags=socket.MSG_PEEK) self._cleanup_fds(fds) From 1dcd26f7acdbd7eb11107651331cbc640f1e185f Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Sun, 22 Jun 2025 18:13:01 +0800 Subject: [PATCH 16/19] rename flags to msg_flags --- Lib/socket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/socket.py b/Lib/socket.py index 963e8292e01e76..856105a4fc54f4 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -579,14 +579,14 @@ def recv_fds(sock, bufsize, maxfds, flags=0): # Array of ints fds = array.array("i") - msg, ancdata, flags, addr = sock.recvmsg(bufsize, + msg, ancdata, msg_flags, addr = sock.recvmsg(bufsize, _socket.CMSG_LEN(maxfds * fds.itemsize), flags) for cmsg_level, cmsg_type, cmsg_data in ancdata: if (cmsg_level == _socket.SOL_SOCKET and cmsg_type == _socket.SCM_RIGHTS): fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) - return msg, list(fds), flags, addr + return msg, list(fds), msg_flags, addr __all__.append("recv_fds") if hasattr(_socket.socket, "share"): From 5d3e8969f5d9899fc8cf9b3a26a6e78c2846a516 Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Sun, 22 Jun 2025 18:15:19 +0800 Subject: [PATCH 17/19] also check flags in test_send_fds_dontwait --- Lib/test/test_socket.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 8436523ecbe2fd..33a936a22c2f3f 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7457,6 +7457,8 @@ def test_send_fds_dontwait(self): self._cleanup_fds(fds) self.assertEqual(msg, MSG) + if hasattr(socket, "MSG_CTRUNC"): + self.assertEqual(flags & socket.MSG_CTRUNC, 0) self.assertEqual(len(fds), 1) self._test_pipe(fds[0], wfd, MSG) From d37ba0717b384fe7792b7ad3008c9e312c0d7d1d Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Sun, 22 Jun 2025 18:27:55 +0800 Subject: [PATCH 18/19] make 2 tests of MSG_DONTWAIT to be consistent --- Lib/test/test_socket.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 33a936a22c2f3f..062014ad556538 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -3513,10 +3513,11 @@ def _testSendmsgTimeout(self): # XXX: would be nice to have more tests for sendmsg flags argument. - # Linux supports MSG_DONTWAIT when sending, but in general, it - # only works when receiving. Could add other platforms if they + # Linux and FreeBSD support MSG_DONTWAIT when sending, but in general, + # it only works when receiving. Could add other platforms if they # support it too. - @skipWithClientIf(sys.platform not in {"linux", "android", "freebsd"}, + @requireAttrs(socket, "MSG_DONTWAIT") + @skipWithClientIf(sys.platform in ("darwin",), "MSG_DONTWAIT not known to work on this platform when " "sending") def testSendmsgDontWait(self): From f4c9d6773518e5edaa42be782a89d94e5064d1f5 Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Sun, 22 Jun 2025 18:54:31 +0800 Subject: [PATCH 19/19] clarify PIPE_BUF in _test_pipe --- Lib/test/test_socket.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 062014ad556538..0ff91d3b6db07b 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7335,9 +7335,11 @@ def close_fds(fds): self.addCleanup(close_fds, fds) def _test_pipe(self, rfd, wfd, msg): - assert len(msg) < 512 + # POSIX requires PIPE_BUF to be at least 512 bytes. + PIPE_BUF = 512 + assert len(msg) < PIPE_BUF os.write(wfd, msg) - data = os.read(rfd, 512) + data = os.read(rfd, PIPE_BUF) self.assertEqual(data, msg) @staticmethod