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

Disable socket inheritance on Windows and macOS #32903

Merged
merged 3 commits into from
Oct 20, 2018
Merged
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class Winsock
{
// Used as last parameter to WSASocket call.
[Flags]
internal enum SocketConstructorFlags
{
WSA_FLAG_OVERLAPPED = 0x01,
WSA_FLAG_MULTIPOINT_C_ROOT = 0x02,
WSA_FLAG_MULTIPOINT_C_LEAF = 0x04,
WSA_FLAG_MULTIPOINT_D_ROOT = 0x08,
WSA_FLAG_MULTIPOINT_D_LEAF = 0x10,
WSA_FLAG_NO_HANDLE_INHERIT = 0x80,
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,6 @@ internal static partial class Interop
{
internal static partial class Winsock
{
// Used as last parameter to WSASocket call.
[Flags]
internal enum SocketConstructorFlags
{
WSA_FLAG_OVERLAPPED = 0x01,
WSA_FLAG_MULTIPOINT_C_ROOT = 0x02,
WSA_FLAG_MULTIPOINT_C_LEAF = 0x04,
WSA_FLAG_MULTIPOINT_D_ROOT = 0x08,
WSA_FLAG_MULTIPOINT_D_LEAF = 0x10,
}

[DllImport(Interop.Libraries.Ws2_32, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern SafeCloseSocket.InnerSafeCloseSocket WSASocketW(
[In] AddressFamily addressFamily,
Expand Down
2 changes: 1 addition & 1 deletion src/Common/src/System/Net/SafeCloseSocket.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ private SocketError InnerReleaseHandle()

internal static InnerSafeCloseSocket CreateWSASocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
{
InnerSafeCloseSocket result = Interop.Winsock.WSASocketW(addressFamily, socketType, protocolType, IntPtr.Zero, 0, Interop.Winsock.SocketConstructorFlags.WSA_FLAG_OVERLAPPED);
InnerSafeCloseSocket result = Interop.Winsock.WSASocketW(addressFamily, socketType, protocolType, IntPtr.Zero, 0, Interop.Winsock.SocketConstructorFlags.WSA_FLAG_OVERLAPPED | Interop.Winsock.SocketConstructorFlags.WSA_FLAG_NO_HANDLE_INHERIT);
if (result.IsInvalid)
{
result.SetHandleAsInvalid();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private static bool IsProtocolSupported(AddressFamily af)

try
{
s = Interop.Winsock.WSASocketW(af, SocketType.Dgram, 0, IntPtr.Zero, 0, 0);
s = Interop.Winsock.WSASocketW(af, SocketType.Dgram, 0, IntPtr.Zero, 0, (int)Interop.Winsock.SocketConstructorFlags.WSA_FLAG_NO_HANDLE_INHERIT);

if (s == IntPtr.Zero)
{
Expand Down
10 changes: 9 additions & 1 deletion src/Native/Unix/System.Native/pal_networking.c
Original file line number Diff line number Diff line change
Expand Up @@ -1978,7 +1978,15 @@ int32_t SystemNative_Socket(int32_t addressFamily, int32_t socketType, int32_t p
platformSocketType |= SOCK_CLOEXEC;
#endif
*createdSocket = socket(platformAddressFamily, platformSocketType, platformProtocolType);
return *createdSocket != -1 ? Error_SUCCESS : SystemNative_ConvertErrorPlatformToPal(errno);
if (*createdSocket == -1)
{
return SystemNative_ConvertErrorPlatformToPal(errno);
}

#ifndef SOCK_CLOEXEC
fcntl(ToFileDescriptor(*createdSocket), F_SETFD, FD_CLOEXEC); // ignore any failures; this is best effort
#endif
return Error_SUCCESS;
}

int32_t SystemNative_GetAtOutOfBandMark(intptr_t socket, int32_t* atMark)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\Interop.WSASocketW.cs">
<Link>Interop\Windows\Winsock\Interop.WSASocketW.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs">
<Link>Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Net\Sockets\ProtocolFamily.cs">
<Link>Common\System\Net\Sockets\ProtocolFamily.cs</Link>
</Compile>
Expand Down Expand Up @@ -198,4 +201,4 @@
<Reference Include="System.Threading.Overlapped" />
<Reference Include="System.Threading.Tasks" />
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\Interop.WSASocketW.cs">
<Link>Interop\Windows\Winsock\Interop.WSASocketW.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs">
<Link>Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Net\Sockets\ProtocolFamily.cs">
<Link>Common\System\Net\Sockets\ProtocolFamily.cs</Link>
</Compile>
Expand Down Expand Up @@ -183,4 +186,4 @@
<Link>Interop\Unix\System.Native\Interop.SocketAddress.cs</Link>
</Compile>
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\Interop.WSASocketW.SafeCloseSocket.cs">
<Link>Interop\Windows\Winsock\Interop.WSASocketW.SafeCloseSocket.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs">
<Link>Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\WSABuffer.cs">
<Link>Interop\Windows\Winsock\WSABuffer.cs</Link>
</Compile>
Expand Down Expand Up @@ -358,4 +361,4 @@
<ItemGroup Condition="'$(TargetsLinux)' == 'true'">
<Reference Include="System.Threading.Timer" />
</ItemGroup>
</Project>
</Project>
5 changes: 4 additions & 1 deletion src/System.Net.Ping/src/System.Net.Ping.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\Interop.WSASocketW.cs">
<Link>Common\Interop\Windows\Winsock\Interop.WSASocketW.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs">
<Link>Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs</Link>
</Compile>
<!-- System.Net.Internals -->
<Compile Include="$(CommonPath)\System\Net\Sockets\SocketType.cs">
<Link>Common\System\Net\Sockets\SocketType.cs</Link>
Expand Down Expand Up @@ -151,4 +154,4 @@
<Reference Include="System.IO.FileSystem" />
<Reference Include="System.Runtime.InteropServices.RuntimeInformation" />
</ItemGroup>
</Project>
</Project>
5 changes: 4 additions & 1 deletion src/System.Net.Sockets/src/System.Net.Sockets.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\Interop.WSASocketW.SafeCloseSocket.cs">
<Link>Interop\Windows\Winsock\Interop.WSASocketW.SafeCloseSocket.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs">
<Link>Interop\Windows\Winsock\Interop.SocketConstructorFlags.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\SafeNativeOverlapped.cs">
<Link>Interop\Windows\Winsock\SafeNativeOverlapped.cs</Link>
</Compile>
Expand Down Expand Up @@ -403,4 +406,4 @@
<ItemGroup Condition="'$(TargetsUnix)' == 'true'">
<Reference Include="System.Threading.ThreadPool" />
</ItemGroup>
</Project>
</Project>
98 changes: 94 additions & 4 deletions src/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Threading.Tasks;
using Xunit;

namespace System.Net.Sockets.Tests
{
public class CreateSocket
public class CreateSocket : RemoteExecutorTestBase
{
public static object[][] DualModeSuccessInputs = {
new object[] { SocketType.Stream, ProtocolType.Tcp },
Expand Down Expand Up @@ -89,9 +93,95 @@ public void Ctor_Failure(AddressFamily addressFamily, SocketType socketType, Pro
[ConditionalTheory(nameof(SupportsRawSockets))]
public void Ctor_Raw_Success(AddressFamily addressFamily, ProtocolType protocolType)
{
using (new Socket(addressFamily, SocketType.Raw, protocolType))
{
}
using (new Socket(addressFamily, SocketType.Raw, protocolType))
{
}
}

[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Sockets are still inheritable on netfx: https://github.com/dotnet/corefx/pull/32903")]
[Theory]
[InlineData(true, 0)] // Accept
[InlineData(false, 0)]
[InlineData(true, 1)] // AcceptAsync
[InlineData(false, 1)]
[InlineData(true, 2)] // Begin/EndAccept
[InlineData(false, 2)]
public void CtorAndAccept_SocketNotKeptAliveViaInheritance(bool validateClientOuter, int acceptApiOuter)
{
// Run the test in another process so as to not have trouble with other tests
// launching child processes that might impact inheritance.
RemoteInvoke((validateClientString, acceptApiString) =>
{
bool validateClient = bool.Parse(validateClientString);
int acceptApi = int.Parse(acceptApiString);

// Create a listening server.
using (var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
listener.Bind(new IPEndPoint(IPAddress.Loopback, 0));
listener.Listen(int.MaxValue);
EndPoint ep = listener.LocalEndPoint;

// Create a client and connect to that listener.
using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
client.Connect(ep);

// Accept the connection using one of multiple accept mechanisms.
Socket server =
acceptApi == 0 ? listener.Accept() :
acceptApi == 1 ? listener.AcceptAsync().GetAwaiter().GetResult() :
acceptApi == 2 ? Task.Factory.FromAsync(listener.BeginAccept, listener.EndAccept, null).GetAwaiter().GetResult() :
throw new Exception($"Unexpected {nameof(acceptApi)}: {acceptApi}");

// Get streams for the client and server, and create a pipe that we'll use
// to communicate with a child process.
using (var serverStream = new NetworkStream(server, ownsSocket: true))
using (var clientStream = new NetworkStream(client, ownsSocket: true))
using (var serverPipe = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable))
{
// Create a child process that blocks waiting to receive a signal on the anonymous pipe.
// The whole purpose of the child is to test whether handles are inherited, so we
// keep the child process alive until we're done validating that handles close as expected.
using (RemoteInvoke(clientPipeHandle =>
{
using (var clientPipe = new AnonymousPipeClientStream(PipeDirection.In, clientPipeHandle))
{
Assert.Equal(42, clientPipe.ReadByte());
}
}, serverPipe.GetClientHandleAsString()))
{
if (validateClient) // Validate that the child isn't keeping alive the "new Socket" for the client
{
// Send data from the server to client, then validate the client gets EOF when the server closes.
serverStream.WriteByte(84);
Assert.Equal(84, clientStream.ReadByte());
serverStream.Close();
Assert.Equal(-1, clientStream.ReadByte());
}
else // Validate that the child isn't keeping alive the "listener.Accept" for the server
{
// Send data from the client to server, then validate the server gets EOF when the client closes.
clientStream.WriteByte(84);
Assert.Equal(84, serverStream.ReadByte());
clientStream.Close();
Assert.Equal(-1, serverStream.ReadByte());
}

// And validate that we after closing the listening socket, we're not able to connect.
listener.Dispose();
using (var tmpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
Assert.ThrowsAny<SocketException>(() => tmpClient.Connect(ep));
}

// Let the child process terminate.
serverPipe.WriteByte(42);
}
}
}
}
}, validateClientOuter.ToString(), acceptApiOuter.ToString()).Dispose();
}
}
}