Skip to content

Fix apphost/bundle creation failure on chmod-hostile filesystems#129342

Open
elinor-fung wants to merge 1 commit into
dotnet:mainfrom
elinor-fung:fix-apphost-chmod-rootless-container
Open

Fix apphost/bundle creation failure on chmod-hostile filesystems#129342
elinor-fung wants to merge 1 commit into
dotnet:mainfrom
elinor-fung:fix-apphost-chmod-rootless-container

Conversation

@elinor-fung

@elinor-fung elinor-fung commented Jun 12, 2026

Copy link
Copy Markdown
Member

Summary

dotnet build and single-file dotnet publish fail inside rootless podman devcontainers because File.SetUnixFileMode (chmod 755) returns EPERM on bind-mounted Windows folders. Under rootless podman's default user-namespace mapping, the bind-mounted files appear root-owned to the non-root container user (e.g. vscode, uid 1000), so a non-owner cannot chmod them — even though they are writable. This surfaces as:

error MSB4018: The "CreateAppHost" task failed unexpectedly.
System.UnauthorizedAccessException: Access to the path '.../obj/Debug/net10.0/apphost' is denied.
 ---> System.IO.IOException: Operation not permitted
   at System.IO.File.SetUnixFileMode(String path, UnixFileMode mode)
   at Microsoft.NET.HostModel.AppHost.HostWriter.CreateAppHost(...)

Fixes #129040.

cc @dotnet/appmodel @AaronRobinsonMSFT

Change

Set the executable permissions when the file is created (via the open syscall, using FileStreamOptions.UnixCreateMode) instead of relying on a separate chmod, which is not permitted on such filesystems. The post-write chmod is now only attempted when the file is actually missing the required permissions, so it is:

  • skipped on chmod-hostile filesystems where the file was already created executable (fixing the bug), and
  • still applied where chmod is supported, enforcing exact 0755 (e.g. under a restrictive umask, where UnixCreateMode alone would be masked).

Both HostWriter (apphost) and Bundler (single-file bundle) are affected by the same bug and now share two helpers in HostModelUtils:

  • CreateFileStreamForHost — creates the output stream, requesting 0755 at creation time on Unix.
  • SetPermissionsForHost — best-effort enforcement that only chmods when needed.

In HostWriter, SetPermissionsForHost runs inside the existing try/catch so that if the result is genuinely non-executable, the broken apphost is deleted and the error is surfaced rather than silently shipped.

The net472-only libc chmod P/Invoke shim (Utils/UnixUtils.cs) is removed — it was the only consumer of File.SetUnixFileMode on net472, and net472 only runs on Windows in the modern SDK, where Unix permissions do not apply.

Testing

  • Added ExecutableImageOverwritesExistingNonExecutableFile (Unix-only) verifying that creating an apphost over a pre-existing non-executable file still yields -rwxr-xr-x.
  • Existing ExecutableImage test continues to cover the executable-permission invariant.
  • Validated end-to-end under real rootless podman (non-root user, root-owned bind mount, .NET 10 SDK): both dotnet build and Bundler.GenerateBundle fail without this change and succeed with it. The conditional-chmod fallback was also confirmed on a normal Linux filesystem.

Note

This pull request was authored with the assistance of GitHub Copilot.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Microsoft.NET.HostModel’s apphost and single-file bundle output writing logic to better handle Unix permission setting on filesystems where a separate chmod step can fail (e.g., EPERM on certain bind mounts). It centralizes “create executable at file creation time” and “enforce permissions when needed” behavior into HostModelUtils, and adds a Unix-only regression test for apphost overwrite behavior.

Changes:

  • Add HostModelUtils.CreateFileStreamForHost (uses FileStreamOptions.UnixCreateMode on Unix) and HostModelUtils.SetPermissionsForHost (conditional permission enforcement).
  • Update HostWriter and Bundler to use the shared helpers instead of unconditional post-write SetUnixFileMode.
  • Add a Unix-only test ensuring apphost overwrites a pre-existing non-executable destination with executable permissions; remove the net472-only Unix chmod shim.
Show a summary per file
File Description
src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost/CreateAppHost.cs Adds Unix-only regression coverage for overwriting an existing non-executable apphost destination.
src/installer/managed/Microsoft.NET.HostModel/Utils/UnixUtils.cs Removes the net472-only Unix chmod shim (no longer needed with #if NET gating).
src/installer/managed/Microsoft.NET.HostModel/HostModelUtils.cs Introduces shared helpers for “create with exec perms” and “conditional chmod enforcement” on Unix.
src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs Switches bundle output creation to the new helpers and centralizes permission enforcement.
src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs Switches apphost destination stream creation to the new helper and uses shared permission enforcement.

Copilot's findings

  • Files reviewed: 5/5 changed files
  • Comments generated: 2

Comment thread src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs Outdated
Comment thread src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs
@elinor-fung elinor-fung force-pushed the fix-apphost-chmod-rootless-container branch from 752cf33 to 5071bc7 Compare June 12, 2026 19:00
CreateAppHost and single-file bundle generation failed inside rootless
podman devcontainers because File.SetUnixFileMode (chmod 755) returns
EPERM on bind-mounted Windows folders that appear root-owned to the
non-root container user, surfacing as:

  System.UnauthorizedAccessException: Access to the path '.../apphost' is denied.
   ---> System.IO.IOException: Operation not permitted
     at System.IO.File.SetUnixFileMode(...)
     at Microsoft.NET.HostModel.AppHost.HostWriter.CreateAppHost(...)

Set the executable permissions when the file is created (via the open
syscall using FileStreamOptions.UnixCreateMode) instead of relying on a
separate chmod, which is not permitted on such filesystems. The
post-write chmod is now only attempted when the file is missing the
required permissions, so it is skipped on filesystems where the file was
already created executable and still enforces exact permissions (e.g.
under a restrictive umask) where chmod is supported.

Both HostWriter (apphost) and Bundler (single-file) now share the
CreateFileStreamForHost and SetPermissionsForHost helpers. The net472
libc chmod shim (UnixUtils.cs) is removed: net472 only runs on Windows in
the modern SDK, where Unix permissions do not apply.

Fixes dotnet#129040

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 12, 2026 22:44
@elinor-fung elinor-fung force-pushed the fix-apphost-chmod-rootless-container branch from 5071bc7 to 4272452 Compare June 12, 2026 22:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 5/5 changed files
  • Comments generated: 2

Comment thread src/installer/managed/Microsoft.NET.HostModel/HostModelUtils.cs
Comment thread src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs
@elinor-fung elinor-fung marked this pull request as ready for review June 12, 2026 22:55
@elinor-fung elinor-fung added this to the 11.0.0 milestone Jun 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Building a C# AppHost fails within a devcontainer, blocking xUnit v3 test projects

2 participants