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) {