Skip to content

Require an execute bit before selecting a Unix apphost - #10641

Draft
Jakub Jareš (nohwnd) wants to merge 1 commit into
microsoft:mainfrom
nohwnd:nohwnd-restore-unix-execute-bit-guard-mtp-appho
Draft

Require an execute bit before selecting a Unix apphost#10641
Jakub Jareš (nohwnd) wants to merge 1 commit into
microsoft:mainfrom
nohwnd:nohwnd-restore-unix-execute-bit-guard-mtp-appho

Conversation

@nohwnd

Copy link
Copy Markdown
Member

When launching a managed .dll, the server-mode client prefers a sibling apphost and decides with File.Exists, which says yes to a file that cannot be started. A test payload built on a Windows agent and executed on a Linux machine arrives with its extensionless apphost stripped of POSIX permission bits, because a zip written on Windows records none. Process.Start then throws Permission denied and aborts the run rather than falling back.

BuildLaunch now gates the choice on IsUsableApphost, which additionally requires an execute bit on Unix, so an unusable candidate falls back to dotnet <dll>. File.GetUnixFileMode is .NET 7+, so it sits behind the package's modern-.NET fence and net462, netstandard2.0 and net5.0–net7.0 consumers keep the existence-only check. That stays correct because the apphost path probe is already OS-aware and never offers a Windows .exe on Unix.

This restores the second half of the fix in microsoft/vstest#16336, which was lost when vstest deleted its own client in favour of this package in microsoft/vstest#16300.

Reproduced end to end before fixing: dotnet publish -r linux-x64 on Windows emits an extensionless apphost beside the .dll; a Windows-written zip stores ExternalAttributes = 0; extracting on Linux yields mode 0644; Process.Start throws Permission denied. The four alternatives were all checked and none holds — the check is absent from main, the layout is reachable, Process.Start does not degrade gracefully, and the .dll branch is live for consumers even though testfx's own callers pass an apphost.

Verified: build.cmd passes; the new tests pass on Windows net8.0 and net462 and on Linux net8.0, and reverting the guard makes the Unix fallback test fail.

Follow-up in microsoft/vstest, once a package with this ships: bump MicrosoftTestingPlatformServerModeClientSourcesVersion in eng/Versions.props and add an acceptance test for the layout.

🤖

🤖

The server-mode client picks a sibling apphost for a managed .dll by asking
File.Exists, which says yes to a file that cannot be launched. A payload built
on a Windows agent and run on a Linux machine arrives with its extensionless
apphost stripped of POSIX permission bits, because a zip written on Windows
records none. Process.Start then throws Permission denied and aborts the run
instead of falling back.

Gate the choice on IsUsableApphost, which additionally requires an execute bit
on Unix. File.GetUnixFileMode is .NET 7+, so net462 and netstandard2.0
consumers keep the existence-only check, which stays correct because the
apphost path probe is already OS-aware.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings August 18, 2026 14:08

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

Restores Unix apphost validation in the source-only server-mode client after vstest adopted it.

Changes:

  • Requires Unix sibling apphosts to have an execute bit.
  • Falls back to dotnet <dll> for unusable apphosts.
  • Adds cross-platform launch-selection tests.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
MtpServerProcess.cs Adds apphost usability validation and fallback logic.
MtpServerProcessTests.cs Covers Windows and Unix apphost selection.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +370 to +374
#if NETCOREAPP
// File.GetUnixFileMode is .NET 7+. Consumers compiling the netstandard2.0 slice (net462, and
// net5.0-net7.0 which select that slice) fall back to the existence check above. That stays correct
// because GetAppHostPath already refuses to consider a Windows ".exe" on Unix, so the only case left
// uncovered there is an extensionless file that lost its execute bit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants