[CSHARP-2875] Race condition in TcpStreamFactory could result in CreateStream returning a NetworkStream with a closed Socket Created: 13/Dec/19  Updated: 28/Oct/23  Resolved: 13/Jan/20

Status: Closed
Project: C# Driver
Component/s: Connectivity
Affects Version/s: 2.10.0
Fix Version/s: 2.10.1

Type: Bug Priority: Major - P3
Reporter: Robert Stam Assignee: Robert Stam
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified


 Description   

If a timeout or cancellation request happens AFTER `socket.Connect` returns but BEFORE the `connected` variable is set to true the socket could be Closed before the Connect helper method returns:

https://github.com/mongodb/mongo-csharp-driver/blob/v2.10.0/src/MongoDB.Driver.Core/Core/Connections/TcpStreamFactory.cs#L144

The window for the race condition is VERY small and therefore low probability.



 Comments   
Comment by Githook User [ 13/Jan/20 ]

Author:

{'name': 'rstam', 'email': 'robert@robertstam.org', 'username': 'rstam'}

Message: CSHARP-2875: Fix race condition in TestStreamFactory CreateSttream method.
Branch: v2.10.x
https://github.com/mongodb/mongo-csharp-driver/commit/17adfa158e2a2425d3a13e2aa01b4042f0f2125d

Comment by Githook User [ 13/Jan/20 ]

Author:

{'name': 'rstam', 'email': 'robert@robertstam.org', 'username': 'rstam'}

Message: CSHARP-2875: Fix race condition in TestStreamFactory CreateSttream method.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/7ae4c938518f9fc4009ccfa5a2d7aaf6da154b72

Comment by Robert Stam [ 13/Dec/19 ]

A suggested fix would be:

 

private void Connect(Socket socket, EndPoint endPoint, TimeSpan timeout, CancellationToken cancellationToken)
{
    var state = 1; // 1 == connecting, 2 == connected, 3 == timedout, 4 == cancelled    
 
    void changeState(int to)
    {
        var from = Interlocked.CompareExchange(ref state, to, 1);
        if (from == 1 && to >= 3)
        {
            try { socket.Close(); } catch { } // closing the socket aborts the connection attempt
        }
    }    
 
    using (new Timer(_ => changeState(3), null, timeout, Timeout.InfiniteTimeSpan))
    using (cancellationToken.Register(() => changeState(4)))
    {
        try
        {
            var dnsEndPoint = endPoint as DnsEndPoint;
            if (dnsEndPoint != null)
            {
                // mono doesn't support DnsEndPoint in its BeginConnect method.
                socket.Connect(dnsEndPoint.Host, dnsEndPoint.Port);
            }
            else
            {
                socket.Connect(endPoint);
            }
            changeState(2);
        }
        catch when (state >= 3)
        {
            // a different exception will be thrown instead below
        }        
        if (state == 3) { throw new TimeoutException(); }
        if (state == 4) { throw new OperationCanceledException(); }
    }
}

 

Generated at Wed Feb 07 21:43:45 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.