From dea253a25f61b498a941622aca31ac6428aa34e8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 19:33:56 +0000 Subject: [PATCH 1/6] Initial plan From ecc2af6339aa95160b2c9bcd765b3308133656ec Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 19:36:56 +0000 Subject: [PATCH 2/6] docs: add breaking change article for NamedPipeServerStream Unix socket permissions in .NET 11 Agent-Logs-Url: https://github.com/dotnet/docs/sessions/ec7bd413-d926-4f72-959f-6376e84957ae Co-authored-by: gewarren <24882762+gewarren@users.noreply.github.com> --- docs/core/compatibility/11.md | 1 + .../namedpipeserverstream-unix-permissions.md | 67 +++++++++++++++++++ docs/core/compatibility/toc.yml | 2 + 3 files changed, 70 insertions(+) create mode 100644 docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md diff --git a/docs/core/compatibility/11.md b/docs/core/compatibility/11.md index 089c8e7daf3f4..183a2671b10ac 100644 --- a/docs/core/compatibility/11.md +++ b/docs/core/compatibility/11.md @@ -29,6 +29,7 @@ See [Breaking changes in ASP.NET Core 10](/aspnet/core/breaking-changes/10/overv | [DeflateStream and GZipStream write headers and footers for empty payload](core-libraries/11/deflatestream-gzipstream-empty-payload.md) | Behavioral change | | [Environment.TickCount made consistent with Windows timeout behavior](core-libraries/11/environment-tickcount-windows-behavior.md) | Behavioral change | | [MemoryStream maximum capacity updated and exception behavior changed](core-libraries/11/memorystream-max-capacity.md) | Behavioral change | +| [NamedPipeServerStream with PipeOptions.CurrentUserOnly tightens Unix socket file permissions](core-libraries/11/namedpipeserverstream-unix-permissions.md) | Behavioral change | | [Nullable.GetUnderlyingType throws for custom Type subclasses](core-libraries/11/nullable-getunderlyingtype-throws.md) | Behavioral change | | [API obsoletions with non-default diagnostic IDs (.NET 11)](core-libraries/11/obsolete-apis.md) | Source incompatible | | [TAR-reading APIs verify header checksums when reading](core-libraries/11/tar-checksum-validation.md) | Behavioral change | diff --git a/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md b/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md new file mode 100644 index 0000000000000..6e721feb46f26 --- /dev/null +++ b/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md @@ -0,0 +1,67 @@ +--- +title: "Breaking change: NamedPipeServerStream with PipeOptions.CurrentUserOnly tightens Unix socket file permissions" +description: "Learn about the breaking change in .NET 11 where NamedPipeServerStream with PipeOptions.CurrentUserOnly sets the Unix socket file mode to 0600." +ms.date: 05/04/2026 +ai-usage: ai-assisted +--- + +# NamedPipeServerStream with PipeOptions.CurrentUserOnly tightens Unix socket file permissions + +To better align on-disk permissions with the documented intent of and with the Windows implementation, the underlying Unix domain socket file is now created with file permissions `0600` (read/write for the owning user only). Previously, the socket file inherited permissions from the process umask, and `CurrentUserOnly` only rejected cross-user connections at connect time without restricting who could open the socket file itself. + +## Version introduced + +.NET 11 Preview 4 + +## Previous behavior + +Previously, the socket file backing a was created with whatever permissions the process umask allowed (commonly `0644` or `0755`). Specifying didn't change the on-disk file mode. Other local users could `stat` and attempt to connect to the socket file. Cross-user connection attempts were rejected at connect time by peer-credential checks, but the socket file itself was world-visible and connectable at the operating system level. + +```csharp +using var server = new NamedPipeServerStream( + "mypipe", PipeDirection.InOut, 1, + PipeTransmissionMode.Byte, PipeOptions.CurrentUserOnly); + +// Mode reflected the process umask, for example UserRead | UserWrite | GroupRead | OtherRead +UnixFileMode mode = File.GetUnixFileMode("/tmp/CoreFxPipe_mypipe"); +``` + +## New behavior + +Starting in .NET 11, when you specify , the socket file is `chmod`'d to `0600` immediately after `bind()`. Other local users (other than root) can no longer open or connect to the socket file at the operating system level. + +```csharp +using var server = new NamedPipeServerStream( + "mypipe", PipeDirection.InOut, 1, + PipeTransmissionMode.Byte, PipeOptions.CurrentUserOnly); + +// Always UserRead | UserWrite (0600) +UnixFileMode mode = File.GetUnixFileMode("/tmp/CoreFxPipe_mypipe"); +``` + +For the in-process shared server cache—where multiple `NamedPipeServerStream` instances use the same pipe name—the permission change is one-way (ratcheted): + +- If a `CurrentUserOnly` instance is created for a given pipe name, the socket file is tightened to `0600` at that point and stays `0600` for the remainder of that path's shared lifetime. +- A later instance for the same pipe name that doesn't specify `CurrentUserOnly` doesn't loosen the mode back. + +## Type of breaking change + +This change is a [behavioral change](../../categories.md#behavioral-change). + +## Reason for change + + is documented as restricting access to the current user. On Unix, however, the socket file was created with permissions derived from the process umask, so enforcement relied solely on peer-credential checks at connect time. This left the socket file discoverable and connectable by other local users, and made Unix behavior inconsistent with the option's documented intent and with the Windows implementation. For more information, see [dotnet/runtime#127239](https://github.com/dotnet/runtime/pull/127239). + +## Recommended action + +Most callers benefit from the tighter permissions and require no action. + +To allow the server to be reachable by local users other than the owner—for example, a helper process that runs under a different account or external tooling that probes the socket—stop passing when other-user access is required. + +Also be aware of the in-process ratcheting behavior: if any `NamedPipeServerStream` for a given pipe name in the process specifies `CurrentUserOnly`, all subsequent instances for that pipe name will see `0600` permissions on the socket file until the shared server entry is released. + +## Affected APIs + +- +- +- diff --git a/docs/core/compatibility/toc.yml b/docs/core/compatibility/toc.yml index ed732eb84f966..2c1a6553d5fc8 100644 --- a/docs/core/compatibility/toc.yml +++ b/docs/core/compatibility/toc.yml @@ -20,6 +20,8 @@ items: href: core-libraries/11/environment-tickcount-windows-behavior.md - name: MemoryStream maximum capacity updated and exception behavior changed href: core-libraries/11/memorystream-max-capacity.md + - name: NamedPipeServerStream with PipeOptions.CurrentUserOnly tightens Unix socket file permissions + href: core-libraries/11/namedpipeserverstream-unix-permissions.md - name: Nullable.GetUnderlyingType throws for custom Type subclasses href: core-libraries/11/nullable-getunderlyingtype-throws.md - name: API obsoletions with non-default diagnostic IDs From 748768d597100bc93dc07867dd67c4c3b9eb0f47 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Mon, 4 May 2026 13:06:11 -0700 Subject: [PATCH 3/6] Apply suggestions from code review --- .../11/namedpipeserverstream-unix-permissions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md b/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md index 6e721feb46f26..a393780c4c957 100644 --- a/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md +++ b/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md @@ -22,7 +22,7 @@ using var server = new NamedPipeServerStream( "mypipe", PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.CurrentUserOnly); -// Mode reflected the process umask, for example UserRead | UserWrite | GroupRead | OtherRead +// Mode reflected the process umask, for example UserRead | UserWrite | GroupRead | OtherRead. UnixFileMode mode = File.GetUnixFileMode("/tmp/CoreFxPipe_mypipe"); ``` @@ -35,11 +35,11 @@ using var server = new NamedPipeServerStream( "mypipe", PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.CurrentUserOnly); -// Always UserRead | UserWrite (0600) +// Always UserRead | UserWrite (0600). UnixFileMode mode = File.GetUnixFileMode("/tmp/CoreFxPipe_mypipe"); ``` -For the in-process shared server cache—where multiple `NamedPipeServerStream` instances use the same pipe name—the permission change is one-way (ratcheted): +For the in-process shared server cache, where multiple `NamedPipeServerStream` instances use the same pipe name, the permission change is one-way (ratcheted): - If a `CurrentUserOnly` instance is created for a given pipe name, the socket file is tightened to `0600` at that point and stays `0600` for the remainder of that path's shared lifetime. - A later instance for the same pipe name that doesn't specify `CurrentUserOnly` doesn't loosen the mode back. From 7486e502ac38eb6b407178248bb50023f3313b96 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Mon, 4 May 2026 13:07:22 -0700 Subject: [PATCH 4/6] Update docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md --- .../11/namedpipeserverstream-unix-permissions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md b/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md index a393780c4c957..ab6f2310392b7 100644 --- a/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md +++ b/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md @@ -62,6 +62,6 @@ Also be aware of the in-process ratcheting behavior: if any `NamedPipeServerStre ## Affected APIs -- -- -- +- +- +- From 0eb844c596e4007ea73dae48d34e02fcb3027e0c Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Mon, 4 May 2026 13:29:01 -0700 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../11/namedpipeserverstream-unix-permissions.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md b/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md index ab6f2310392b7..9e8e9b1819350 100644 --- a/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md +++ b/docs/core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md @@ -15,7 +15,7 @@ To better align on-disk permissions with the documented intent of was created with whatever permissions the process umask allowed (commonly `0644` or `0755`). Specifying didn't change the on-disk file mode. Other local users could `stat` and attempt to connect to the socket file. Cross-user connection attempts were rejected at connect time by peer-credential checks, but the socket file itself was world-visible and connectable at the operating system level. +Previously, the socket file backing a was created with whatever permissions the process umask allowed (commonly `0644` or `0755`). Specifying didn't change the on-disk file mode. Other local users could `stat` and might be able to attempt a connection to the socket file, depending on the platform and effective permissions. Cross-user connection attempts were rejected at connect time by peer-credential checks, but the socket file itself could still be visible to other users and, on some Unix systems, might also be connectable at the operating system level. ```csharp using var server = new NamedPipeServerStream( @@ -50,15 +50,19 @@ This change is a [behavioral change](../../categories.md#behavioral-change). ## Reason for change - is documented as restricting access to the current user. On Unix, however, the socket file was created with permissions derived from the process umask, so enforcement relied solely on peer-credential checks at connect time. This left the socket file discoverable and connectable by other local users, and made Unix behavior inconsistent with the option's documented intent and with the Windows implementation. For more information, see [dotnet/runtime#127239](https://github.com/dotnet/runtime/pull/127239). + is documented as restricting access to the current user. On Unix, however, the socket file was created with permissions derived from the process umask, so enforcement relied on peer-credential checks at connect time. This left the socket file discoverable by other local users, and on platforms that honor socket-node permission bits for `connect()`, it might also allow other users to attempt or make a connection before peer-credential checks rejected cross-user access. That behavior made Unix behavior inconsistent with the option's documented intent and with the Windows implementation. For more information, see [dotnet/runtime#127239](https://github.com/dotnet/runtime/pull/127239). ## Recommended action Most callers benefit from the tighter permissions and require no action. -To allow the server to be reachable by local users other than the owner—for example, a helper process that runs under a different account or external tooling that probes the socket—stop passing when other-user access is required. +On Linux and macOS, uses Unix domain sockets. If you rely on that socket file being visible or connectable to local users other than the owner, update your guidance and your app assumptions to reflect that now sets the socket file mode to `0600` at bind time. -Also be aware of the in-process ratcheting behavior: if any `NamedPipeServerStream` for a given pipe name in the process specifies `CurrentUserOnly`, all subsequent instances for that pipe name will see `0600` permissions on the socket file until the shared server entry is released. +Removing doesn't guarantee cross-user access by itself. When you omit that option, the socket file still inherits permissions from the process umask, and other users also need enough access to the directory that contains the socket path. If you intend to allow cross-user access, verify the effective socket file mode and the directory permissions on the target system. + +To let the server accept connections from other local users—for example, from a helper process that runs under a different account, or from external tooling that probes the socket—stop passing . + +Also account for the in-process ratcheting behavior. If any `NamedPipeServerStream` instance for a given pipe name in the process specifies `CurrentUserOnly`, all later instances for that pipe name keep `0600` permissions on the socket file until the shared server entry is released. ## Affected APIs From 755388651e2488e4f0a62ef3d2e5eed50011a577 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 20:31:13 +0000 Subject: [PATCH 6/6] docs: update named pipes conceptual doc with .NET 11 CurrentUserOnly Unix permission change note Agent-Logs-Url: https://github.com/dotnet/docs/sessions/fd1dd68f-55c1-48e9-b156-338a3d6ca39e Co-authored-by: gewarren <24882762+gewarren@users.noreply.github.com> --- ...o-use-named-pipes-for-network-interprocess-communication.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/standard/io/how-to-use-named-pipes-for-network-interprocess-communication.md b/docs/standard/io/how-to-use-named-pipes-for-network-interprocess-communication.md index f360288c50e75..f1e3d33afff30 100644 --- a/docs/standard/io/how-to-use-named-pipes-for-network-interprocess-communication.md +++ b/docs/standard/io/how-to-use-named-pipes-for-network-interprocess-communication.md @@ -21,6 +21,9 @@ Named pipes provide interprocess communication between a pipe server and one or > [!IMPORTANT] > .NET on Linux uses Unix Domain Sockets (UDS) for the implementation of these APIs. +> [!NOTE] +> Starting in .NET 11, when you create a with on Unix, the underlying socket file is set to mode `0600` (owner read/write only) at bind time. Previously, the socket file inherited permissions from the process umask. For more information, see [NamedPipeServerStream with PipeOptions.CurrentUserOnly tightens Unix socket file permissions](../../core/compatibility/core-libraries/11/namedpipeserverstream-unix-permissions.md). + To implement name pipes, use the and classes. ## Example 1