Skip to content

Commit 1be335d

Browse files
committed
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.
1 parent bb7e16a commit 1be335d

3 files changed

Lines changed: 34 additions & 0 deletions

File tree

‎Lib/test/test_socket.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3286,6 +3286,26 @@ def testRecvFromNegative(self):
32863286
def _testRecvFromNegative(self):
32873287
self.cli.sendto(MSG, 0, (HOST, self.port))
32883288

3289+
# gh-160622: with MSG_TRUNC, recv() and recvfrom() must not return
3290+
# more bytes than requested, even if the datagram was longer.
3291+
@requireAttrs(socket, "MSG_TRUNC")
3292+
@unittest.skipIf(sys.platform == "win32", "Windows raises WSAEMSGSIZE")
3293+
def testRecvTrunc(self):
3294+
msg = self.serv.recv(2, socket.MSG_TRUNC)
3295+
self.assertEqual(msg, MSG[:2])
3296+
3297+
def _testRecvTrunc(self):
3298+
self.cli.sendto(MSG, 0, (HOST, self.port))
3299+
3300+
@requireAttrs(socket, "MSG_TRUNC")
3301+
@unittest.skipIf(sys.platform == "win32", "Windows raises WSAEMSGSIZE")
3302+
def testRecvFromTrunc(self):
3303+
msg, addr = self.serv.recvfrom(2, socket.MSG_TRUNC)
3304+
self.assertEqual(msg, MSG[:2])
3305+
3306+
def _testRecvFromTrunc(self):
3307+
self.cli.sendto(MSG, 0, (HOST, self.port))
3308+
32893309

32903310
@unittest.skipUnless(HAVE_SOCKET_UDPLITE,
32913311
'UDPLITE sockets required for this test.')
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:meth:`socket.socket.recv` and :meth:`socket.socket.recvfrom` no longer
2+
raise :exc:`ValueError` and lose the datagram when called with
3+
``MSG_TRUNC`` and the datagram is longer than *bufsize*. Only the bytes
4+
actually received are returned now.

‎Modules/socketmodule.c‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4066,6 +4066,12 @@ _socket_socket_recv_impl(PySocketSockObject *s, Py_ssize_t recvlen,
40664066
PyBytesWriter_Discard(writer);
40674067
return NULL;
40684068
}
4069+
/* With MSG_TRUNC, recv() can return the real length of the datagram,
4070+
which may be larger than the buffer. Only recvlen bytes were
4071+
written. */
4072+
if (outlen > recvlen) {
4073+
outlen = recvlen;
4074+
}
40694075

40704076
return PyBytesWriter_FinishWithSize(writer, outlen);
40714077
}
@@ -4242,6 +4248,10 @@ _socket_socket_recvfrom_impl(PySocketSockObject *s, Py_ssize_t recvlen,
42424248
PyBytesWriter_Discard(writer);
42434249
goto error;
42444250
}
4251+
/* See the comment in _socket_socket_recv_impl(). */
4252+
if (outlen > recvlen) {
4253+
outlen = recvlen;
4254+
}
42454255

42464256
PyObject *buf = PyBytesWriter_FinishWithSize(writer, outlen);
42474257
if (buf == NULL) {

0 commit comments

Comments
 (0)