Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Truncate sendmsg/recvmsg to IOV_MAX (#23781)
Browse files Browse the repository at this point in the history
* Truncate sendmsg/recvmsg to IOV_MAX

* PR feedback

* Remove newline
  • Loading branch information
tmds authored and stephentoub committed Sep 5, 2017
1 parent fe0e443 commit bbfdddf
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 1 deletion.
8 changes: 7 additions & 1 deletion src/Native/Unix/System.Native/pal_networking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "pal_safecrt.h"

#include <stdlib.h>
#include <limits.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <assert.h>
Expand Down Expand Up @@ -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_cast<decltype(header->msg_iovlen)>(messageHeader.IOVectorCount);
iovlen = Min(iovlen, static_cast<decltype(iovlen)>(IOV_MAX));
*header = {
.msg_name = messageHeader.SocketAddress,
.msg_namelen = static_cast<unsigned int>(messageHeader.SocketAddressLen),
.msg_iov = reinterpret_cast<iovec*>(messageHeader.IOVectors),
.msg_iovlen = static_cast<decltype(header->msg_iovlen)>(messageHeader.IOVectorCount),
.msg_iovlen = iovlen,
.msg_control = messageHeader.ControlBuffer,
.msg_controllen = static_cast<decltype(header->msg_controllen)>(messageHeader.ControlBufferLen),
};
Expand Down
64 changes: 64 additions & 0 deletions src/System.Net.Sockets/tests/FunctionalTests/SendReceive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ArraySegment<byte>>();
for (int i = 0; i < segmentCount; i++)
{
sendBuffer[i] = (byte)i;
sendSegments.Add(new ArraySegment<byte>(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<ArraySegment<byte>>();
for (int i = 0; i < segmentCount; i++)
{
receiveSegments.Add(new ArraySegment<byte>(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
Expand Down

0 comments on commit bbfdddf

Please sign in to comment.