From bbfdddfc016ecada9e159bed135c04fa4b727b33 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Tue, 5 Sep 2017 18:53:36 +0200 Subject: [PATCH] Truncate sendmsg/recvmsg to IOV_MAX (#23781) * Truncate sendmsg/recvmsg to IOV_MAX * PR feedback * Remove newline --- .../Unix/System.Native/pal_networking.cpp | 8 ++- .../tests/FunctionalTests/SendReceive.cs | 64 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/Native/Unix/System.Native/pal_networking.cpp b/src/Native/Unix/System.Native/pal_networking.cpp index 177232dbf1bf..3f27e04c3811 100644 --- a/src/Native/Unix/System.Native/pal_networking.cpp +++ b/src/Native/Unix/System.Native/pal_networking.cpp @@ -9,6 +9,7 @@ #include "pal_safecrt.h" #include +#include #include #include #include @@ -1145,11 +1146,16 @@ SystemNative_SetIPv6Address(uint8_t* socketAddress, int32_t socketAddressLen, ui static void ConvertMessageHeaderToMsghdr(msghdr* header, const MessageHeader& messageHeader) { + // sendmsg/recvmsg can return EMSGSIZE when msg_iovlen is greather than IOV_MAX. + // We avoid this by truncating msg_iovlen to IOV_MAX, this is ok since sendmsg is + // not required to send all data and recvmsg can be called again to receive more. + auto iovlen = static_castmsg_iovlen)>(messageHeader.IOVectorCount); + iovlen = Min(iovlen, static_cast(IOV_MAX)); *header = { .msg_name = messageHeader.SocketAddress, .msg_namelen = static_cast(messageHeader.SocketAddressLen), .msg_iov = reinterpret_cast(messageHeader.IOVectors), - .msg_iovlen = static_castmsg_iovlen)>(messageHeader.IOVectorCount), + .msg_iovlen = iovlen, .msg_control = messageHeader.ControlBuffer, .msg_controllen = static_castmsg_controllen)>(messageHeader.ControlBufferLen), }; diff --git a/src/System.Net.Sockets/tests/FunctionalTests/SendReceive.cs b/src/System.Net.Sockets/tests/FunctionalTests/SendReceive.cs index 0ca741d08283..5f6b7dc655ab 100644 --- a/src/System.Net.Sockets/tests/FunctionalTests/SendReceive.cs +++ b/src/System.Net.Sockets/tests/FunctionalTests/SendReceive.cs @@ -856,6 +856,70 @@ public void SocketSendReceiveBufferSize_SetZero_ThrowsSocketException() Assert.Equal(e.SocketErrorCode, SocketError.InvalidArgument); } } + + [Fact] + public void SendRecvIovMax_Success() + { + // sending/receiving more than IOV_MAX segments causes EMSGSIZE on some platforms. + // This is handled internally so this error shouldn't surface. + + // Use more than IOV_MAX (1024 on Linux & macOS) segments. + const int segmentCount = 2400; + using (var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + server.BindToAnonymousPort(IPAddress.Loopback); + server.Listen(1); + + var sendBuffer = new byte[segmentCount]; + Task serverProcessingTask = Task.Run(async () => + { + using (Socket acceptSocket = await AcceptAsync(server)) + { + // send data as segmentCount (> IOV_MAX) 1-byte segments. + var sendSegments = new List>(); + for (int i = 0; i < segmentCount; i++) + { + sendBuffer[i] = (byte)i; + sendSegments.Add(new ArraySegment(sendBuffer, i, 1)); + } + SocketError error; + // Send blocks until all segments are sent. + int bytesSent = acceptSocket.Send(sendSegments, SocketFlags.None, out error); + + Assert.Equal(segmentCount, bytesSent); + Assert.Equal(SocketError.Success, error); + } + }); + + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + client.Connect(server.LocalEndPoint); + + // receive data as 1-byte segments. + var receiveBuffer = new byte[segmentCount]; + var receiveSegments = new List>(); + for (int i = 0; i < segmentCount; i++) + { + receiveSegments.Add(new ArraySegment(receiveBuffer, i, 1)); + } + var bytesReceivedTotal = 0; + do + { + SocketError error; + // Receive can return up to IOV_MAX segments. + int bytesReceived = client.Receive(receiveSegments, SocketFlags.None, out error); + bytesReceivedTotal += bytesReceived; + // Offset receiveSegments for next Receive. + receiveSegments.RemoveRange(0, bytesReceived); + + Assert.NotEqual(0, bytesReceived); + Assert.Equal(SocketError.Success, error); + } while (bytesReceivedTotal != segmentCount); + + Assert.Equal(sendBuffer, receiveBuffer); + } + } + } } public sealed class SendReceiveUdpClient : MemberDatas