Fix OS-blind apphost probe in MTP bridge on Unix (regression from #16201) - #16336
Merged
Azat Mukhametshin (azat-msft) merged 1 commit intoAug 6, 2026
Conversation
Copilot started reviewing on behalf of
Azat Mukhametshin (azat-msft)
August 3, 2026 10:24
View session
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a cross-platform regression in the CrossPlatEngine MTP bridge where apphost probing was OS-blind, potentially selecting a Windows *.exe apphost when running on Unix and causing Process.Start failures. The update makes apphost selection OS-aware and adds unit tests around BuildLaunch/apphost usability semantics.
Changes:
- Update
MtpServerConnection.BuildLaunchto probeFoo.exeon Windows and extension-lessFooon Unix, and gate selection viaIsUsableApphost. - Add
IsUsableApphost(Unix execute-bit check under#if NET) to avoid selecting non-executable apphost candidates on Unix when supported. - Add new unit tests validating fallback behavior and Unix execute-bit semantics.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Microsoft.TestPlatform.CrossPlatEngine/Client/MTP/MtpServerConnection.cs | Makes apphost probing OS-aware and introduces IsUsableApphost to avoid launching unusable apphost candidates. |
| test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/MTP/MtpServerConnectionTests.cs | Adds unit tests for BuildLaunch and Unix execute-bit behavior (with OS-conditional coverage). |
…6201) MtpServerConnection.BuildLaunch probed for a sibling apphost by swapping the source extension to .exe unconditionally and gating on File.Exists. On Unix the apphost is extension-less, so the .exe probe is wrong; worse, a payload built on Windows and run on Unix (e.g. aspnetcore tests built on a Windows agent and executed on a Linux Helix machine) drags a Windows PE Foo.exe next to the dll, which File.Exists happily finds and Process.Start then fails to launch with 'Permission denied' (or 'Exec format error' once +x). Make the probe OS-aware (extension-less apphost on Unix) and, on Unix, require an execute bit before treating the candidate as a usable apphost; otherwise fall back to 'dotnet <dll>'. Adds unit tests for BuildLaunch/IsUsableApphost. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azat Mukhametshin (azat-msft)
force-pushed
the
fix/mtp-apphost-os-blind-probe
branch
from
August 6, 2026 10:34
4065733 to
814ba2c
Compare
Copilot started reviewing on behalf of
Azat Mukhametshin (azat-msft)
August 6, 2026 10:35
View session
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/Microsoft.TestPlatform.CrossPlatEngine/Client/MTP/MtpServerConnection.cs:12
using System.Runtime.InteropServices;is only needed for the non-NETFRAMEWORK builds (whereRuntimeInformation/OSPlatformare referenced). In the net462 target the#if NETFRAMEWORKbranch removes all uses, so this using becomes unused and can trigger IDE0005 (this repo commonly wraps such usings with TFM guards; e.g.src/Microsoft.TestPlatform.PlatformAbstractions/net462/System/ProcessHelper.cs). Consider conditionally including the using to avoid build breaks in multi-target builds.
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
Jakub Jareš (nohwnd)
approved these changes
Aug 6, 2026
Azat Mukhametshin (azat-msft)
merged commit Aug 6, 2026
384428e
into
microsoft:main
20 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
MtpServerConnection.BuildLaunch(added in #16201) probes for a test app's native apphost by unconditionally swapping the source extension to.exeand gating onFile.Exists:The apphost file name is OS-specific:
Foo.exeon Windows, but extension-lessFooon Unix. On a normal Linux box the.exedoesn't exist and this harmlessly falls back todotnet <dll>. But when a test payload is built on Windows and run on Unix (e.g. tests built on a Windows agent, zipped, and executed on a Linux Helix machine), the Windows PEFoo.exetravels alongside the dll — and zip carries no Unix permission bits.File.Existsthen finds it andProcess.Startfails:(
chmod +xwould only turn this intoExec format error— it's a Windows binary.)This surfaced in dotnet/aspnetcore's CI (Windows build agents + Linux Helix queues) on an xunit.v3 (MTP-capable) test assembly, but it affects any repo that builds MTP tests (xunit.v3 / MSTest-on-MTP / TUnit) on Windows and runs them on Linux.
Fix
Make the apphost probe OS-aware, and on Unix require the candidate to actually be executable before launching it (otherwise fall back to
dotnet <dll>):Foo.exe; Unix → extension-lessFoo.IsUsableApphost: file must exist, and on Unix must carry an execute bit (File.GetUnixFileMode), so a stray non-native sibling (e.g. a Windows PE copied onto Unix) is never selected.The OS check is
#if NETFRAMEWORK-guarded (net462 has noRuntimeInformationand is Windows-only); the Unix-mode check is#if NET-guarded (File.GetUnixFileModeis .NET 7+). Onnetstandard2.0it degrades to existence-only, which is still correct because the OS-aware path selection already resolves the bug.Tests
Adds
MtpServerConnectionTestscovering:.exesource passes through; dll with a sibling Windows-only.exeon Unix is not selected (falls back todotnet <dll>); dll with no apphost falls back; andIsUsableApphostexecute-bit semantics on Unix.Verification
Reproduced end-to-end with a source-built runner, A/B-swapping only
CrossPlatEngine.dllagainst a real xunit.v3 app plus a dummy sibling.exe, under the same runtime:... start process '.../MtpRepro.exe' ... Permission denied→ Test Run Aborted.Passed! 1— the Windows.exeis ignored. A genuine executable Unix apphost is still selected and launched, and a classic (non-MTP) xUnit v2 project is unaffected.Regression from #16201.