From 1be335d888c113b49cd6aee21bd0e14a8e528658 Mon Sep 17 00:00:00 2001 From: Artsem Lebiadzevich Date: Sun, 25 Oct 2026 22:05:44 +0300 Subject: [PATCH] gh-160622: Fix socket recv() and recvfrom() with MSG_TRUNC With MSG_TRUNC, recv() and recvfrom() can return the real length of the datagram, which may be larger than the requested size. Since PyBytesWriter_FinishWithSize() now rejects a size larger than the allocated one, this raised ValueError after the datagram had already been consumed. Return only the bytes actually received instead. --- Lib/test/test_socket.py | 20 +++++++++++++++++++ ...-10-25-19-04-57.gh-issue-160622.7n56jv.rst | 4 ++++ Modules/socketmodule.c | 10 ++++++++++ 3 files changed, 34 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-10-25-19-04-57.gh-issue-160622.7n56jv.rst diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index e4b3d848923f8c9..69b30eb100a89b4 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -3286,6 +3286,26 @@ def testRecvFromNegative(self): def _testRecvFromNegative(self): self.cli.sendto(MSG, 0, (HOST, self.port)) + # gh-160622: with MSG_TRUNC, recv() and recvfrom() must not return + # more bytes than requested, even if the datagram was longer. + @requireAttrs(socket, "MSG_TRUNC") + @unittest.skipIf(sys.platform == "win32", "Windows raises WSAEMSGSIZE") + def testRecvTrunc(self): + msg = self.serv.recv(2, socket.MSG_TRUNC) + self.assertEqual(msg, MSG[:2]) + + def _testRecvTrunc(self): + self.cli.sendto(MSG, 0, (HOST, self.port)) + + @requireAttrs(socket, "MSG_TRUNC") + @unittest.skipIf(sys.platform == "win32", "Windows raises WSAEMSGSIZE") + def testRecvFromTrunc(self): + msg, addr = self.serv.recvfrom(2, socket.MSG_TRUNC) + self.assertEqual(msg, MSG[:2]) + + def _testRecvFromTrunc(self): + self.cli.sendto(MSG, 0, (HOST, self.port)) + @unittest.skipUnless(HAVE_SOCKET_UDPLITE, 'UDPLITE sockets required for this test.') diff --git a/Misc/NEWS.d/next/Library/2026-10-25-19-04-57.gh-issue-160622.7n56jv.rst b/Misc/NEWS.d/next/Library/2026-10-25-19-04-57.gh-issue-160622.7n56jv.rst new file mode 100644 index 000000000000000..e1880ba65b2e687 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-25-19-04-57.gh-issue-160622.7n56jv.rst @@ -0,0 +1,4 @@ +:meth:`socket.socket.recv` and :meth:`socket.socket.recvfrom` no longer +raise :exc:`ValueError` and lose the datagram when called with +``MSG_TRUNC`` and the datagram is longer than *bufsize*. Only the bytes +actually received are returned now. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 97146b1d752c3d8..832736db5fc3cd8 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -4066,6 +4066,12 @@ _socket_socket_recv_impl(PySocketSockObject *s, Py_ssize_t recvlen, PyBytesWriter_Discard(writer); return NULL; } + /* With MSG_TRUNC, recv() can return the real length of the datagram, + which may be larger than the buffer. Only recvlen bytes were + written. */ + if (outlen > recvlen) { + outlen = recvlen; + } return PyBytesWriter_FinishWithSize(writer, outlen); } @@ -4242,6 +4248,10 @@ _socket_socket_recvfrom_impl(PySocketSockObject *s, Py_ssize_t recvlen, PyBytesWriter_Discard(writer); goto error; } + /* See the comment in _socket_socket_recv_impl(). */ + if (outlen > recvlen) { + outlen = recvlen; + } PyObject *buf = PyBytesWriter_FinishWithSize(writer, outlen); if (buf == NULL) {