Skip to content

Exception in CommonSocket.Send() #19

@joaofarias

Description

@joaofarias

In the rare occasions that we do enter the first if block, Array.Resize creates a new array internally which 'arr' is then referencing. After the resize call, 'arr' and 'segment.Array' are NOT the same array. The exception is then thrown when creating "a new segment so count is right" because the array being passed in has not been resized, so 'Count + 1' leads to the "Argument_InvalidOffLen" ArgumentException.

Pasting the code for easy reference:

protected EResult Send(HSteamNetConnection steamConnection, ArraySegment<byte> segment, byte channelId)
{
    /* Have to resize array to include channel index
     * if array isn't large enough to fit it. This is because
     * we don't know what channel data comes in on so
     * the channel has to be packed into the data sent.
     * Odds of the array having to resize are extremely low
     * so while this is not ideal, it's still very low risk. */
    if ((segment.Array.Length - 1) <= (segment.Offset + segment.Count))
    {
        byte[] arr = segment.Array;
        Array.Resize(ref arr, arr.Length + 1);
        arr[arr.Length - 1] = channelId;
    }
    //If large enough just increase the segment and set the channel byte.
    else
    {
        segment.Array[segment.Offset + segment.Count] = channelId;
    }
    //Make a new segment so count is right.
    segment = new ArraySegment<byte>(segment.Array, segment.Offset, segment.Count + 1);

    //...
}

We fixed it locally by creating a new segment with the resized array and moving the final segment creation inside the else block since we already set the correct size otherwise:

if ((segment.Array.Length - 1) <= (segment.Offset + segment.Count))
{
    byte[] arr = segment.Array;
    Array.Resize(ref arr, arr.Length + 1);
    arr[arr.Length - 1] = channelId;

    // create a new segment with the resized array
    segment = new ArraySegment<byte>(arr, segment.Offset, segment.Count + 1);
}
//If large enough just increase the segment and set the channel byte.
else
{
    segment.Array[segment.Offset + segment.Count] = channelId;

    //Make a new segment so count is right.
    segment = new ArraySegment<byte>(segment.Array, segment.Offset, segment.Count + 1);
}

It's very easy to reproduce the issue by enabling the latency simulator, as the segments being passed in all have the same length as their array and execution will always go through the if block.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions