Skip to content

[Breaking change]: NamedPipeServerStream with PipeOptions.CurrentUserOnly tightens Unix socket file mode to 0600 #53368

@cincuranet

Description

@cincuranet

Description

On Unix, when you create a NamedPipeServerStream with PipeOptions.CurrentUserOnly, 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 (typically 0644 or 0755), and CurrentUserOnly only restricted who could connect - it did not restrict who could see or open the socket file on disk.

What this means for you:

  • Other local users on the same machine can no longer open or connect() to the socket file at the OS level when the server is created with CurrentUserOnly. This matches the documented intent of the option and aligns Unix behavior with Windows.
  • If your application relied on a non-owner user being able to reach a CurrentUserOnly server's socket file (for example, a helper process running under a different account, or external tooling that probes the socket), those connections will now fail with a permission error.
  • Within a single process, if multiple NamedPipeServerStream instances share the same path, the socket's mode is ratcheted: as soon as any instance for that path is created with CurrentUserOnly, the file is tightened to 0600 and stays 0600 for the rest of that shared server's lifetime, even after the CurrentUserOnly instance is disposed and even if later instances for the same path do not pass CurrentUserOnly. The mode is never loosened back.

See dotnet/runtime#127239.

Version

.NET 11 Preview 4

Previous behavior

On Unix, the socket file backing a NamedPipeServerStream was created with whatever permissions the process umask allowed (commonly 0644 or 0755). PipeOptions.CurrentUserOnly did not change the on-disk file mode:

using var server = new NamedPipeServerStream(
    "/tmp/mypipe", PipeDirection.InOut, 1,
    PipeTransmissionMode.Byte, PipeOptions.CurrentUserOnly);

// Mode reflected umask, e.g. UserRead | UserWrite | GroupRead | OtherRead
UnixFileMode mode = File.GetUnixFileMode("/tmp/mypipe");

Other local users could stat and attempt to connect() to the socket. Cross-user connection attempts were rejected at connect time by CurrentUserOnly peer-credential checks, but the socket file itself was world-visible and connectable from a permissions standpoint.

New behavior

When PipeOptions.CurrentUserOnly is specified, the socket file is chmod'd to 0600 immediately after bind():

using var server = new NamedPipeServerStream(
    "/tmp/mypipe", PipeDirection.InOut, 1,
    PipeTransmissionMode.Byte, PipeOptions.CurrentUserOnly);

// Always UserRead | UserWrite (0600)
UnixFileMode mode = File.GetUnixFileMode("/tmp/mypipe");

Other local users (other than root) can no longer open or connect() to the socket file at the OS level.

For the in-process SharedServer cache (multiple NamedPipeServerStream instances using the same path), the mode change is one-way:

  • If a non-CurrentUserOnly instance is created first and a later instance for the same path requests CurrentUserOnly, the file mode is tightened to 0600 at that point and stays 0600 for the remainder of the path's shared lifetime.
  • A later non-CurrentUserOnly instance does not loosen a mode that a previous CurrentUserOnly instance tightened.

Type of breaking change

  • Binary incompatible: Existing binaries might encounter a breaking change in behavior, such as failure to load or execute, and if so, require recompilation.
  • Source incompatible: When recompiled using the new SDK or component or to target the new runtime, existing source code might require source changes to compile successfully.
  • Behavioral change: Existing binaries might behave differently at run time.

Reason for change

PipeOptions.CurrentUserOnly was documented as restricting access to the current user, but on Unix the underlying socket file was created with permissions derived from the process umask. Enforcement relied solely on peer-credential checks at connect time, which left the socket file discoverable and openable by other local users and made the behavior inconsistent with the option's documented intent and with the Windows implementation. Hardening the file mode aligns the on-disk permissions with the documented guarantee.

Recommended action

Most callers benefit from the tighter permissions and require no action.

If you have code or tooling that depends on the socket file being readable or connectable by other local users (for example, a non-current-user client connecting to the same path), do the following:

  • Stop passing PipeOptions.CurrentUserOnly when the server is intended to be reachable by users other than the owner.

Be aware of the in-process ratcheting behavior: if any NamedPipeServerStream for a given path in the process specifies CurrentUserOnly, all subsequent instances for that path will see 0600 permissions on the socket file until the shared server entry is released.

Feature area

Core .NET libraries

Affected APIs

  • System.IO.Pipes.NamedPipeServerStream constructors that accept a PipeOptions parameter, when PipeOptions.CurrentUserOnly is specified, on Unix platforms.

Associated WorkItem - 572080

Metadata

Metadata

Assignees

Labels

📌 seQUESTeredIdentifies that an issue has been imported into Quest.breaking-changeIndicates a .NET Core breaking change

Type

No type
No fields configured for issues without a type.

Projects

Status

🔖 Ready

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions