Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TEST ONLY] Try to repro a Linux socket kernel bug on CI #42741

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Reflection.Metadata.Ecma335;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
Expand Down Expand Up @@ -183,6 +185,123 @@ await RetryHelper.ExecuteAsync(async () =>
public sealed class ConnectSync : Connect<SocketHelperArraySync>
{
public ConnectSync(ITestOutputHelper output) : base(output) {}

[Fact]
[PlatformSpecific(TestPlatforms.Linux)]
public unsafe void DisconnectKernelBugRepro()
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.Connect("www.microsoft.com", 443);

// Connect to AF_UNSPEC
const int AddressLength = 16;
byte* address = stackalloc byte[AddressLength]; // note: AF_UNSPEC is zero.
int rv = connect((int)s.Handle, address, AddressLength);
if (rv == -1)
{
int errno = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
throw new Exception($"fail, errno is {errno}");
}
else
{
// Success
}
}

[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
private static unsafe extern int connect(int socket, byte* address, uint address_len);

[Fact]
[PlatformSpecific(TestPlatforms.Linux)]
public async Task DisposeShouldAbortSyncConnectOnLinux()
{
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("1.1.1.1"), 23);

var client = new Socket(SocketType.Stream, ProtocolType.Tcp);

Task connectTask = ConnectAsync(client, endPoint);

// Make 100% sure the connect operation starts
await Task.Delay(200);

Task timeoutTask = Task.Delay(2000);
Task disposeTask = Task.Run(() => client.Dispose());

Task finishedFirst = null;
try
{
finishedFirst = await Task.WhenAny(connectTask, timeoutTask, disposeTask);
}
catch (SocketException ex)
{
Assert.True(finishedFirst == connectTask, $"Got {ex.SocketErrorCode} during Dispose: {ex.Message}");
}

if (finishedFirst == timeoutTask)
{
throw new TimeoutException();
}
else if (finishedFirst == connectTask)
{
await disposeTask;
}
else
{
await Assert.ThrowsAsync<SocketException>(() => connectTask);
}
}

//[Fact]
//[PlatformSpecific(TestPlatforms.Linux)]
//public async Task ConnectCancelByDispose()
//{
// var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

// Task connectTask = ConnectAsync(client, new IPEndPoint(IPAddress.Parse("1.1.1.1"), 23));

// // Wait a little so the operation is started.
// await Task.Delay(100);
// Task disposeTask = Task.Run(() => client.Dispose());

// var cts = new CancellationTokenSource();
// Task timeoutTask = Task.Delay(30000, cts.Token);
// Assert.NotSame(timeoutTask, await Task.WhenAny(disposeTask, connectTask, timeoutTask));
// cts.Cancel();

// await disposeTask;

// SocketError? localSocketError = null;
// bool disposedException = false;
// try
// {
// await connectTask;
// }
// catch (SocketException se)
// {
// // On connection timeout, retry.
// Assert.NotEqual(SocketError.TimedOut, se.SocketErrorCode);

// localSocketError = se.SocketErrorCode;
// }
// catch (ObjectDisposedException)
// {
// disposedException = true;
// }

// if (UsesApm)
// {
// Assert.Null(localSocketError);
// Assert.True(disposedException);
// }
// else if (UsesSync)
// {
// Assert.Equal(SocketError.NotSocket, localSocketError);
// }
// else
// {
// Assert.Equal(SocketError.OperationAborted, localSocketError);
// }
//}
}

public sealed class ConnectSyncForceNonBlocking : Connect<SocketHelperSyncForceNonBlocking>
Expand All @@ -198,6 +317,46 @@ public ConnectApm(ITestOutputHelper output) : base(output) {}
public sealed class ConnectTask : Connect<SocketHelperTask>
{
public ConnectTask(ITestOutputHelper output) : base(output) {}

[Fact]
[PlatformSpecific(TestPlatforms.Linux)]
public async Task DisposeShouldAbortSyncConnectOnLinux()
{
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("1.1.1.1"), 23);

var client = new Socket(SocketType.Stream, ProtocolType.Tcp);

Task connectTask = ConnectAsync(client, endPoint);

// Make 100% sure the connect operation starts
await Task.Delay(200);

Task timeoutTask = Task.Delay(2000);
Task disposeTask = Task.Run(() => client.Dispose());

Task finishedFirst = null;
try
{
finishedFirst = await Task.WhenAny(connectTask, timeoutTask, disposeTask);
}
catch (SocketException ex)
{
Assert.True(finishedFirst == connectTask, $"Got {ex.SocketErrorCode} during Dispose: {ex.Message}");
}

if (finishedFirst == timeoutTask)
{
throw new TimeoutException();
}
else if (finishedFirst == connectTask)
{
await disposeTask;
}
else
{
await Assert.ThrowsAsync<SocketException>(() => connectTask);
}
}
}

public sealed class ConnectEap : Connect<SocketHelperEap>
Expand Down