From ddb3e73a902a33427779cc82e18317d49e159c9e Mon Sep 17 00:00:00 2001 From: Tarun Date: Sat, 25 Mar 2023 11:54:19 +0530 Subject: [PATCH 1/8] Initial Commit --- src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs | 2 ++ .../System.IO.Pipes/src/Resources/Strings.resx | 3 +++ .../src/System/IO/Pipes/NamedPipeServerStream.Unix.cs | 5 +++++ .../src/System/IO/Pipes/NamedPipeServerStream.cs | 8 ++++---- .../System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs | 6 +++++- .../tests/NamedPipeTests/NamedPipeTest.CreateServer.cs | 10 +++++++++- 6 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs index a7432dbc5a217..92b38ed45de71 100644 --- a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs +++ b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs @@ -95,6 +95,8 @@ public enum PipeOptions None = 0, CurrentUserOnly = 536870912, Asynchronous = 1073741824, + [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] + FirstPipeInstance = 524288 } public abstract partial class PipeStream : System.IO.Stream { diff --git a/src/libraries/System.IO.Pipes/src/Resources/Strings.resx b/src/libraries/System.IO.Pipes/src/Resources/Strings.resx index ce20624ce4334..470ad29ab0d46 100644 --- a/src/libraries/System.IO.Pipes/src/Resources/Strings.resx +++ b/src/libraries/System.IO.Pipes/src/Resources/Strings.resx @@ -243,6 +243,9 @@ Message transmission mode is not supported on this platform. + + First Pipe Instance option is not supported on this platform. + Access to remote named pipes is not supported on this platform. diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs index 8544186c1c049..075345a80e33f 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs @@ -38,6 +38,11 @@ private void Create(string pipeName, PipeDirection direction, int maxNumberOfSer throw new PlatformNotSupportedException(SR.PlatformNotSupported_MessageTransmissionMode); } + if ((options & PipeOptions.FirstPipeInstance) != 0) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_PipeOptions_FirstPipeInstance); + } + // We don't have a good way to enforce maxNumberOfServerInstances across processes; we only factor it in // for streams created in this process. Between processes, we behave similarly to maxNumberOfServerInstances == 1, // in that the second process to come along and create a stream will find the pipe already in existence and will fail. diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs index 069ff0e0a930d..0e52e86ee05b3 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs @@ -60,10 +60,10 @@ public NamedPipeServerStream(string pipeName, PipeDirection direction, int maxNu /// Win32 note: this gets used for dwPipeMode. CreateNamedPipe allows you to specify PIPE_TYPE_BYTE/MESSAGE /// and PIPE_READMODE_BYTE/MESSAGE independently, but this sets type and readmode to match. /// - /// PipeOption enum: None, Asynchronous, or Write-through + /// PipeOption enum: None, Asynchronous, or Write-through or FirstPipeInstance /// Win32 note: this gets passed in with dwOpenMode to CreateNamedPipe. Asynchronous corresponds to - /// FILE_FLAG_OVERLAPPED option. PipeOptions enum doesn't expose FIRST_PIPE_INSTANCE option because - /// this sets that automatically based on the number of instances specified. + /// FILE_FLAG_OVERLAPPED option. PipeOptions.FIRST_PIPE_INSTANCE + /// is automatically set based on the number of instances specified. /// /// Incoming buffer size, 0 or higher. /// Note: this size is always advisory; OS uses a suggestion. @@ -103,7 +103,7 @@ private void ValidateParameters( { throw new ArgumentOutOfRangeException(nameof(transmissionMode), SR.ArgumentOutOfRange_TransmissionModeByteOrMsg); } - if ((options & ~(PipeOptions.WriteThrough | PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly)) != 0) + if ((options & ~(PipeOptions.WriteThrough | PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly | PipeOptions.FirstPipeInstance)) != 0) { throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_OptionsInvalid); } diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs index 493ee5f675329..fd4547db69709 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs @@ -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.Runtime.Versioning; + namespace System.IO.Pipes { [Flags] @@ -9,6 +11,8 @@ public enum PipeOptions None = 0x0, WriteThrough = unchecked((int)0x80000000), Asynchronous = unchecked((int)0x40000000), // corresponds to FILE_FLAG_OVERLAPPED - CurrentUserOnly = unchecked((int)0x20000000) + CurrentUserOnly = unchecked((int)0x20000000), + [SupportedOSPlatform("windows")] + FirstPipeInstance = unchecked((int)0x00080000) } } diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs index d8afc8094a3de..4e93e227b602f 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs @@ -52,7 +52,8 @@ public static void ReservedPipeName_Throws_ArgumentOutOfRangeException(PipeDirec AssertExtensions.Throws("pipeName", () => new NamedPipeServerStream(reservedName, direction, 1)); AssertExtensions.Throws("pipeName", () => new NamedPipeServerStream(reservedName, direction, 1, PipeTransmissionMode.Byte)); AssertExtensions.Throws("pipeName", () => new NamedPipeServerStream(reservedName, direction, 1, PipeTransmissionMode.Byte, PipeOptions.None)); - AssertExtensions.Throws("pipeName", () => new NamedPipeServerStream(reservedName, direction, 1, PipeTransmissionMode.Byte, PipeOptions.None, 0, 0));} + AssertExtensions.Throws("pipeName", () => new NamedPipeServerStream(reservedName, direction, 1, PipeTransmissionMode.Byte, PipeOptions.None, 0, 0)); + } [Fact] [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] @@ -254,5 +255,12 @@ public static void Windows_ServerCloneWithDifferentDirection_Throws_Unauthorized Assert.Throws(() => new NamedPipeServerStream(uniqueServerName, PipeDirection.Out)); } } + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] + public static void Unix_PipeOptions_FirstPipeInstance_ThrowsPlatformNotSupportedException() + { + Assert.Throws(() => new NamedPipeServerStream(PipeStreamConformanceTests.GetUniquePipeName(), PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.FirstPipeInstance)); + } } } From 361eebe5e4138bceda793729e2b0ef90cbb66d14 Mon Sep 17 00:00:00 2001 From: Tarun Date: Sun, 26 Mar 2023 09:20:41 +0530 Subject: [PATCH 2/8] Update Code to remove SupportedOSPlatform Attribute --- src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs | 1 - src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs index 92b38ed45de71..f7b404b93a502 100644 --- a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs +++ b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs @@ -95,7 +95,6 @@ public enum PipeOptions None = 0, CurrentUserOnly = 536870912, Asynchronous = 1073741824, - [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] FirstPipeInstance = 524288 } public abstract partial class PipeStream : System.IO.Stream diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs index fd4547db69709..28a65b2f9f550 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs @@ -12,7 +12,6 @@ public enum PipeOptions WriteThrough = unchecked((int)0x80000000), Asynchronous = unchecked((int)0x40000000), // corresponds to FILE_FLAG_OVERLAPPED CurrentUserOnly = unchecked((int)0x20000000), - [SupportedOSPlatform("windows")] FirstPipeInstance = unchecked((int)0x00080000) } } From 08837e98ffbe39f9e06429d19d99a00ce87ae741 Mon Sep 17 00:00:00 2001 From: "Gudipati.Tarun" Date: Sun, 9 Apr 2023 20:54:08 +0530 Subject: [PATCH 3/8] Address Review Comments --- .../System.IO.Pipes/ref/System.IO.Pipes.cs | 1 + .../System/IO/Pipes/NamedPipeServerStream.Unix.cs | 2 ++ .../src/System/IO/Pipes/NamedPipeServerStream.cs | 7 ++++--- .../src/System/IO/Pipes/PipeOptions.cs | 1 + .../NamedPipeTests/NamedPipeTest.CreateServer.cs | 13 +++++++++++++ 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs index f7b404b93a502..92b38ed45de71 100644 --- a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs +++ b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs @@ -95,6 +95,7 @@ public enum PipeOptions None = 0, CurrentUserOnly = 536870912, Asynchronous = 1073741824, + [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] FirstPipeInstance = 524288 } public abstract partial class PipeStream : System.IO.Stream diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs index 075345a80e33f..7c34d9940f82c 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs @@ -38,7 +38,9 @@ private void Create(string pipeName, PipeDirection direction, int maxNumberOfSer throw new PlatformNotSupportedException(SR.PlatformNotSupported_MessageTransmissionMode); } +#pragma warning disable CA1416 if ((options & PipeOptions.FirstPipeInstance) != 0) +#pragma warning restore CA1416 { throw new PlatformNotSupportedException(SR.PlatformNotSupported_PipeOptions_FirstPipeInstance); } diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs index 0e52e86ee05b3..8d7ba073c7222 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs @@ -1,7 +1,6 @@ // 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.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; @@ -60,7 +59,7 @@ public NamedPipeServerStream(string pipeName, PipeDirection direction, int maxNu /// Win32 note: this gets used for dwPipeMode. CreateNamedPipe allows you to specify PIPE_TYPE_BYTE/MESSAGE /// and PIPE_READMODE_BYTE/MESSAGE independently, but this sets type and readmode to match. /// - /// PipeOption enum: None, Asynchronous, or Write-through or FirstPipeInstance + /// PipeOption enum: None, Asynchronous, Write-through, or FirstPipeInstance /// Win32 note: this gets passed in with dwOpenMode to CreateNamedPipe. Asynchronous corresponds to /// FILE_FLAG_OVERLAPPED option. PipeOptions.FIRST_PIPE_INSTANCE /// is automatically set based on the number of instances specified. @@ -103,7 +102,9 @@ private void ValidateParameters( { throw new ArgumentOutOfRangeException(nameof(transmissionMode), SR.ArgumentOutOfRange_TransmissionModeByteOrMsg); } +#pragma warning disable CA1416 if ((options & ~(PipeOptions.WriteThrough | PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly | PipeOptions.FirstPipeInstance)) != 0) +#pragma warning restore CA1416 { throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_OptionsInvalid); } @@ -161,7 +162,7 @@ public Task WaitForConnectionAsync() return WaitForConnectionAsync(CancellationToken.None); } - public System.IAsyncResult BeginWaitForConnection(AsyncCallback? callback, object? state) => + public IAsyncResult BeginWaitForConnection(AsyncCallback? callback, object? state) => TaskToAsyncResult.Begin(WaitForConnectionAsync(), callback, state); public void EndWaitForConnection(IAsyncResult asyncResult) => diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs index 28a65b2f9f550..fd4547db69709 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs @@ -12,6 +12,7 @@ public enum PipeOptions WriteThrough = unchecked((int)0x80000000), Asynchronous = unchecked((int)0x40000000), // corresponds to FILE_FLAG_OVERLAPPED CurrentUserOnly = unchecked((int)0x20000000), + [SupportedOSPlatform("windows")] FirstPipeInstance = unchecked((int)0x00080000) } } diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs index 4e93e227b602f..44f6bd5b91333 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Net.NetworkInformation; using Microsoft.Win32.SafeHandles; using Xunit; @@ -262,5 +263,17 @@ public static void Unix_PipeOptions_FirstPipeInstance_ThrowsPlatformNotSupported { Assert.Throws(() => new NamedPipeServerStream(PipeStreamConformanceTests.GetUniquePipeName(), PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.FirstPipeInstance)); } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public static void Windows_PipeOptions_FirstPipeInstanceWithSameNameReuse_Throws_UnauthorizedAccessException() + { + string uniqueServerName = PipeStreamConformanceTests.GetUniquePipeName(); + using (NamedPipeServerStream server = new NamedPipeServerStream(uniqueServerName, PipeDirection.In, 2, PipeTransmissionMode.Byte, PipeOptions.FirstPipeInstance)) + { + Assert.Throws(() => new NamedPipeServerStream(uniqueServerName, PipeDirection.In, 2, PipeTransmissionMode.Byte, PipeOptions.FirstPipeInstance)); + } + } + } } From 951984aea84232e290acaee5d98873880c5872c6 Mon Sep 17 00:00:00 2001 From: "Gudipati.Tarun" Date: Sun, 9 Apr 2023 20:59:12 +0530 Subject: [PATCH 4/8] Remove Unused Imports --- .../tests/NamedPipeTests/NamedPipeTest.CreateServer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs index 44f6bd5b91333..2aee831ee3485 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Net.NetworkInformation; using Microsoft.Win32.SafeHandles; using Xunit; From c335ebcb399806740c0ceeff01432aed3c29211d Mon Sep 17 00:00:00 2001 From: Tarun Date: Sat, 20 May 2023 12:57:05 +0530 Subject: [PATCH 5/8] WIP Commmit --- .../System.IO.Pipes/ref/System.IO.Pipes.cs | 1 - .../src/Resources/Strings.resx | 57 +++++++++---------- .../IO/Pipes/NamedPipeServerStream.Unix.cs | 32 +++++------ .../System/IO/Pipes/NamedPipeServerStream.cs | 2 - .../src/System/IO/Pipes/PipeOptions.cs | 1 - .../NamedPipeTest.CreateServer.cs | 10 +--- 6 files changed, 44 insertions(+), 59 deletions(-) diff --git a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs index 92b38ed45de71..f7b404b93a502 100644 --- a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs +++ b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs @@ -95,7 +95,6 @@ public enum PipeOptions None = 0, CurrentUserOnly = 536870912, Asynchronous = 1073741824, - [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] FirstPipeInstance = 524288 } public abstract partial class PipeStream : System.IO.Stream diff --git a/src/libraries/System.IO.Pipes/src/Resources/Strings.resx b/src/libraries/System.IO.Pipes/src/Resources/Strings.resx index 470ad29ab0d46..8910eb4d7ac45 100644 --- a/src/libraries/System.IO.Pipes/src/Resources/Strings.resx +++ b/src/libraries/System.IO.Pipes/src/Resources/Strings.resx @@ -1,17 +1,17 @@  - @@ -243,9 +243,6 @@ Message transmission mode is not supported on this platform. - - First Pipe Instance option is not supported on this platform. - Access to remote named pipes is not supported on this platform. diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs index 7c34d9940f82c..97c6d3eaeb583 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using System.Net.Sockets; using System.Runtime.InteropServices; -using System.Security; using System.Threading; using System.Threading.Tasks; @@ -38,19 +37,12 @@ private void Create(string pipeName, PipeDirection direction, int maxNumberOfSer throw new PlatformNotSupportedException(SR.PlatformNotSupported_MessageTransmissionMode); } -#pragma warning disable CA1416 - if ((options & PipeOptions.FirstPipeInstance) != 0) -#pragma warning restore CA1416 - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_PipeOptions_FirstPipeInstance); - } - // We don't have a good way to enforce maxNumberOfServerInstances across processes; we only factor it in // for streams created in this process. Between processes, we behave similarly to maxNumberOfServerInstances == 1, // in that the second process to come along and create a stream will find the pipe already in existence and will fail. _instance = SharedServer.Get( GetPipePath(".", pipeName), - (maxNumberOfServerInstances == MaxAllowedServerInstances) ? int.MaxValue : maxNumberOfServerInstances); + (maxNumberOfServerInstances == MaxAllowedServerInstances) ? int.MaxValue : maxNumberOfServerInstances, options); _direction = direction; _options = options; @@ -256,7 +248,7 @@ private sealed class SharedServer /// The concurrent number of concurrent streams using this instance. private int _currentCount; - internal static SharedServer Get(string path, int maxCount) + internal static SharedServer Get(string path, int maxCount, PipeOptions pipeOptions) { Debug.Assert(!string.IsNullOrEmpty(path)); Debug.Assert(maxCount >= 1); @@ -283,7 +275,7 @@ internal static SharedServer Get(string path, int maxCount) else { // No instance exists yet for this path. Create one a new. - server = new SharedServer(path, maxCount); + server = new SharedServer(path, maxCount, pipeOptions); s_servers.Add(path, server); } @@ -318,12 +310,20 @@ internal void Dispose(bool disposing) } } - private SharedServer(string path, int maxCount) + private SharedServer(string path, int maxCount, PipeOptions pipeOptions) { - // Binding to an existing path fails, so we need to remove anything left over at this location. - // There's of course a race condition here, where it could be recreated by someone else between this - // deletion and the bind below, in which case we'll simply let the bind fail and throw. - Interop.Sys.Unlink(path); // ignore any failures + bool firstPipeInstance = (pipeOptions & PipeOptions.FirstPipeInstance) != 0; + if (!firstPipeInstance) + { + // Binding to an existing path fails, so we need to remove anything left over at this location. + // There's of course a race condition here, where it could be recreated by someone else between this + // deletion and the bind below, in which case we'll simply let the bind fail and throw. + Interop.Sys.Unlink(path); // ignore any failures + } + else + { + throw new ArgumentException(); + } // Start listening for connections on the path. var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs index 8d7ba073c7222..1995823a9b858 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs @@ -102,9 +102,7 @@ private void ValidateParameters( { throw new ArgumentOutOfRangeException(nameof(transmissionMode), SR.ArgumentOutOfRange_TransmissionModeByteOrMsg); } -#pragma warning disable CA1416 if ((options & ~(PipeOptions.WriteThrough | PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly | PipeOptions.FirstPipeInstance)) != 0) -#pragma warning restore CA1416 { throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_OptionsInvalid); } diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs index fd4547db69709..28a65b2f9f550 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeOptions.cs @@ -12,7 +12,6 @@ public enum PipeOptions WriteThrough = unchecked((int)0x80000000), Asynchronous = unchecked((int)0x40000000), // corresponds to FILE_FLAG_OVERLAPPED CurrentUserOnly = unchecked((int)0x20000000), - [SupportedOSPlatform("windows")] FirstPipeInstance = unchecked((int)0x00080000) } } diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs index 2aee831ee3485..6d7e83b4f0499 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateServer.cs @@ -257,15 +257,7 @@ public static void Windows_ServerCloneWithDifferentDirection_Throws_Unauthorized } [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] - public static void Unix_PipeOptions_FirstPipeInstance_ThrowsPlatformNotSupportedException() - { - Assert.Throws(() => new NamedPipeServerStream(PipeStreamConformanceTests.GetUniquePipeName(), PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.FirstPipeInstance)); - } - - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public static void Windows_PipeOptions_FirstPipeInstanceWithSameNameReuse_Throws_UnauthorizedAccessException() + public static void PipeOptions_FirstPipeInstanceWithSameNameReuse_Throws_UnauthorizedAccessException() { string uniqueServerName = PipeStreamConformanceTests.GetUniquePipeName(); using (NamedPipeServerStream server = new NamedPipeServerStream(uniqueServerName, PipeDirection.In, 2, PipeTransmissionMode.Byte, PipeOptions.FirstPipeInstance)) From 6f12df42691816bad57386442e405a0628392286 Mon Sep 17 00:00:00 2001 From: Tarun Date: Sat, 20 May 2023 14:48:23 +0530 Subject: [PATCH 6/8] Finalize Implementation on Unix --- .../IO/Pipes/NamedPipeServerStream.Unix.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs index 97c6d3eaeb583..8a2970d8f0bf1 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs @@ -256,6 +256,7 @@ internal static SharedServer Get(string path, int maxCount, PipeOptions pipeOpti lock (s_servers) { SharedServer? server; + bool isFirstPipeInstance = (pipeOptions & PipeOptions.FirstPipeInstance) != 0; if (s_servers.TryGetValue(path, out server)) { // On Windows, if a subsequent server stream is created for the same pipe and with a different @@ -267,7 +268,7 @@ internal static SharedServer Get(string path, int maxCount, PipeOptions pipeOpti { throw new IOException(SR.IO_AllPipeInstancesAreBusy); } - else if (server._currentCount == maxCount) + else if (server._currentCount == maxCount || isFirstPipeInstance) { throw new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, path)); } @@ -275,7 +276,7 @@ internal static SharedServer Get(string path, int maxCount, PipeOptions pipeOpti else { // No instance exists yet for this path. Create one a new. - server = new SharedServer(path, maxCount, pipeOptions); + server = new SharedServer(path, maxCount, isFirstPipeInstance); s_servers.Add(path, server); } @@ -310,20 +311,15 @@ internal void Dispose(bool disposing) } } - private SharedServer(string path, int maxCount, PipeOptions pipeOptions) + private SharedServer(string path, int maxCount, bool isFirstPipeInstance) { - bool firstPipeInstance = (pipeOptions & PipeOptions.FirstPipeInstance) != 0; - if (!firstPipeInstance) + if (!isFirstPipeInstance) { // Binding to an existing path fails, so we need to remove anything left over at this location. // There's of course a race condition here, where it could be recreated by someone else between this // deletion and the bind below, in which case we'll simply let the bind fail and throw. Interop.Sys.Unlink(path); // ignore any failures } - else - { - throw new ArgumentException(); - } // Start listening for connections on the path. var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); @@ -332,6 +328,11 @@ private SharedServer(string path, int maxCount, PipeOptions pipeOptions) socket.Bind(new UnixDomainSocketEndPoint(path)); socket.Listen(int.MaxValue); } + catch (SocketException) when (isFirstPipeInstance) + { + socket.Dispose(); + throw new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, path)); + } catch { socket.Dispose(); From d42615ea4df237b9569ac1d0ddbd3cb6446dc76b Mon Sep 17 00:00:00 2001 From: Tarun Date: Mon, 29 May 2023 19:55:38 +0530 Subject: [PATCH 7/8] Add tests for cross process creation of named pipes --- .../IO/Pipes/NamedPipeServerStream.Unix.cs | 4 +++- .../NamedPipeTests/NamedPipeTest.CrossProcess.cs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs index 8a2970d8f0bf1..2d8e934030ab1 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs @@ -321,14 +321,16 @@ private SharedServer(string path, int maxCount, bool isFirstPipeInstance) Interop.Sys.Unlink(path); // ignore any failures } + bool isSocketBound = false; // Start listening for connections on the path. var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); try { socket.Bind(new UnixDomainSocketEndPoint(path)); + isSocketBound = true; socket.Listen(int.MaxValue); } - catch (SocketException) when (isFirstPipeInstance) + catch (SocketException) when (isFirstPipeInstance && !isSocketBound) { socket.Dispose(); throw new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, path)); diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CrossProcess.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CrossProcess.cs index b94f6771f07d4..e01f42f9d27e5 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CrossProcess.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CrossProcess.cs @@ -103,6 +103,22 @@ public async Task PingPong_Async() } } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] + public void NamedPipeOptionsFirstPipeInstance_Throws_WhenNameIsUsedAcrossProcesses() + { + var uniqueServerName = PipeStreamConformanceTests.GetUniquePipeName(); + using (var firstServer = new NamedPipeServerStream(uniqueServerName, PipeDirection.In, 2, PipeTransmissionMode.Byte, PipeOptions.FirstPipeInstance)) + { + RemoteExecutor.Invoke(new Action(CreateFirstPipeInstance_OtherProcess), uniqueServerName).Dispose(); + } + } + + private static void CreateFirstPipeInstance_OtherProcess(string uniqueServerName) + { + Assert.Throws(() => new NamedPipeServerStream(uniqueServerName, PipeDirection.In, 2, PipeTransmissionMode.Byte, PipeOptions.FirstPipeInstance)); + } + private static void PingPong_OtherProcess(string inName, string outName) { // Create pipes with the supplied names From 719dbcc17e2eb2526b7eed1fb7436e80178a07d2 Mon Sep 17 00:00:00 2001 From: Tarun047 <32017154+Tarun047@users.noreply.github.com> Date: Mon, 29 May 2023 19:56:50 +0530 Subject: [PATCH 8/8] Update src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs Co-authored-by: Adam Sitnik --- .../src/System/IO/Pipes/NamedPipeServerStream.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs index 1995823a9b858..04e61475ecc8e 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs @@ -61,8 +61,7 @@ public NamedPipeServerStream(string pipeName, PipeDirection direction, int maxNu /// /// PipeOption enum: None, Asynchronous, Write-through, or FirstPipeInstance /// Win32 note: this gets passed in with dwOpenMode to CreateNamedPipe. Asynchronous corresponds to - /// FILE_FLAG_OVERLAPPED option. PipeOptions.FIRST_PIPE_INSTANCE - /// is automatically set based on the number of instances specified. + /// FILE_FLAG_OVERLAPPED option. /// /// Incoming buffer size, 0 or higher. /// Note: this size is always advisory; OS uses a suggestion.