runtime icon indicating copy to clipboard operation
runtime copied to clipboard

Socket ConnectAsync returns successfully with a non-connected socket if canceled

Open bentoi opened this issue 3 years ago • 2 comments

Description

Socket.ConnetAsync sporadically returns successfully but the socket is not connected (Socket.Connected == false). This occurs when canceling ConnectAsync while it's in progress after the server side accepts the socket.

Reproduction Steps

using System.Net;
using System.Net.Sockets;

while (true)
{
    using var serverSocket =  new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    serverSocket.Bind(new IPEndPoint(IPAddress.Loopback, 0));
    serverSocket.Listen();

    using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    using var cts = new CancellationTokenSource();

    ValueTask connectTask = socket.ConnectAsync(serverSocket.LocalEndPoint!, cts.Token);
    using var _ = await serverSocket.AcceptAsync();
    cts.Cancel();

    try
    {
        await connectTask;
        if (!socket.Connected)
        {
            Console.Error.WriteLine("unexpected non-connected socket after successfull ConnectAsync");
            return 1;
        }
    }
    catch (OperationCanceledException)
    {
        // Expected if canceled before connect.
    }
}

Expected behavior

The ConnectAsync call should either throw OperationCanceledException or return a connected socket.

Actual behavior

ConnectAsync returns but the socket is not connected

Regression?

No response

Known Workarounds

No response

Configuration

.NET Core 6 Linux Debian Buster and macOS

Other information

No response

bentoi avatar Sep 20 '22 12:09 bentoi

Tagging subscribers to this area: @dotnet/ncl See info in area-owners.md if you want to be subscribed.

Issue Details

Description

Socket.ConnetAsync sporadically returns successfully but the socket is not connected (Socket.Connected == false). This occurs when canceling ConnectAsync while it's in progress after the server side accepts the socket.

Reproduction Steps

using System.Net;
using System.Net.Sockets;

while (true)
{
    using var serverSocket =  new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    serverSocket.Bind(new IPEndPoint(IPAddress.Loopback, 0));
    serverSocket.Listen();

    using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    using var cts = new CancellationTokenSource();

    ValueTask connectTask = socket.ConnectAsync(serverSocket.LocalEndPoint!, cts.Token);
    using var _ = await serverSocket.AcceptAsync();
    cts.Cancel();

    try
    {
        await connectTask;
        if (!socket.Connected)
        {
            Console.Error.WriteLine("unexpected non-connected socket after successfull ConnectAsync");
            return 1;
        }
    }
    catch (OperationCanceledException)
    {
        // Expected if canceled before connect.
    }
}

Expected behavior

The ConnectAsync call should either throw OperationCanceledException or return a connected socket.

Actual behavior

ConnectAsync returns but the socket is not connected

Regression?

No response

Known Workarounds

No response

Configuration

.NET Core 6 Linux Debian Buster and macOS

Other information

No response

Author: bentoi
Assignees: -
Labels:

area-System.Net.Sockets

Milestone: -

ghost avatar Sep 20 '22 12:09 ghost

I think on macOS this can happen when peer closes the connection. Can you perhaps post complete repro @bentoi that has both sides?

wfurt avatar Sep 20 '22 16:09 wfurt

It has both sides already. The test case creates a server socket to accept connections over the loopback interface. The accepted server socket is not disposed while the client socket connection establishment is in progress or canceled. Not that this occurs on both Linux and macOS. I didn't get a chance to check Windows.

bentoi avatar Sep 21 '22 07:09 bentoi

I was able to reproduce the issue in main, .NET 7.0, 6.0 and 5.0. I think it should be easy to fix. Not critical. We can investigate it more deeply.

liveans avatar Sep 27 '22 17:09 liveans

Triage: It is unpleasant behavior, however, it is the only report in last few years (it is an edge case scenario).

karelz avatar Sep 29 '22 16:09 karelz

Hi. With a slightly different repro, I have been able to get disposed Sockets in Connected=true state. Could this be a potential socket leaking case?

using System.Net;
using System.Net.Sockets;
using System.Reflection;

var disposedProperty = typeof(Socket).GetProperty("Disposed", BindingFlags.NonPublic | BindingFlags.Instance);

while (true)
{
    using var serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    serverSocket.Bind(new IPEndPoint(IPAddress.Loopback, 0));
    serverSocket.Listen();

    using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    using var cts = new CancellationTokenSource();

    ValueTask connectTask = socket.ConnectAsync(serverSocket.LocalEndPoint!, cts.Token);
    using var _ = await serverSocket.AcceptAsync();
    cts.Cancel();
    
    try
    {
        await connectTask;
        var disposed = (bool)disposedProperty.GetValue(socket);
        if (disposed && socket.Connected)
        {
            Console.WriteLine($"Disposed and Connected");
        }
    }
    catch (Exception e)
    {
     
    }
}

kaiohenrique avatar Jul 11 '24 13:07 kaiohenrique