Skip to content

Make Runtime=NET task host handshake architecture-agnostic#13890

Open
ViktorHofer wants to merge 3 commits into
dotnet:mainfrom
ViktorHofer:vihofer/fix-arm64-net-task-host-handshake
Open

Make Runtime=NET task host handshake architecture-agnostic#13890
ViktorHofer wants to merge 3 commits into
dotnet:mainfrom
ViktorHofer:vihofer/fix-arm64-net-task-host-handshake

Conversation

@ViktorHofer
Copy link
Copy Markdown
Member

@ViktorHofer ViktorHofer commented May 28, 2026

Summary

Runtime="NET" task host launches fail with MSB4216 whenever the parent and child architectures don't line up the way the original implementation expected. Tracked as #13879 (Bug A).

Two coordinated handshake changes — one on each side — close every reasonable parent/child arch combination:

1. Parent side — CommunicationsUtilities.GetHandshakeOptions

For NET task host the launched child process's architecture is whatever the .NET SDK shipped (x64 today, arm64 in the future), not the parent's. Emitting the parent's arch bit produces a wire-level mismatch with already-shipped SDK children whose arch differs.

Suppress the X64/Arm64 bit when invoked by the parent for a NET task host (detected by Runtime="net" in the explicit TaskHostParameters). The child path (TaskHostParameters.Empty) is unaffected, so already-deployed parents that still emit an arch bit continue to match against new children.

2. Child side — NodeEndpointOutOfProcBase.IsAllowedBitnessMismatch

The .NET task host child-side relaxation that lets a parent without an arch bit on the wire connect to an SDK child. Previously only tolerated expected = X64, so any arm64 SDK child rejected the connection. Now also tolerates expected = Arm64. True cross-arch mismatches (parent X64 ↔ child Arm64) remain rejected.

Method is promoted to internal static so the test project can exercise the tolerance matrix directly. It was a stateless pure predicate already.

Effect across parent / SDK combinations

current = behavior with this PR applied (parent change in VS MSBuild, child change in a future SDK). For each combo, "current behavior" notes whether the SDK side needs to update.

Parent Child (SDK MSBuild.dll) Wire arch bit Old behavior Current behavior
x86 .NET Fx VS x64 SDK none → x64 ✅ (existing expectedIsX64 tolerance)
x86 .NET Fx VS arm64 SDK none → arm64 ❌ MSB4216 (Bug A) — the user's binlog ✅ once SDK ships with child-side expectedIsArm64 tolerance
amd64 .NET Fx VS x64 SDK x64 → x64 ✅ (matches exactly) ✅ (after parent fix: no bit on wire; already-shipped SDK tolerates none → x64)
amd64 .NET Fx VS arm64 SDK x64 → arm64 ❌ MSB4216 ✅ once VS picks up parent-side suppression; SDK must also have child-side Arm64 tolerance
arm64 .NET Fx VS x64 SDK arm64 → x64 ❌ MSB4216 ✅ once VS picks up parent-side suppression (works against already-shipped SDKs)
arm64 .NET Fx VS arm64 SDK arm64 → arm64 ✅ (matches exactly)
old parent (pre-this-PR) new SDK child x64/arm64 → matching unchanged unchanged — still works if arches match, still hits Bug A if they don't (parent has no fix)

Where each fix takes effect

Failing combo Fixed by Ships in
x86 .NET Fx VS → win-arm64 SDK child-side Arm64 tolerance a new .NET SDK build that picks up this PR
amd64 .NET Fx VS → win-arm64 SDK parent-side arch suppression + child-side Arm64 tolerance next VS insertion of MSBuild (parent side) plus a new SDK build (child side)
arm64 .NET Fx VS → win-x64 SDK parent-side arch suppression (existing expectedIsX64 tolerance already covers the child) next VS insertion of MSBuild only — no SDK update needed

The reported binlog (Roslyn build on Windows-ARM, SDK 10.0.108, x86 VS 18 MSBuild parent) is the first row — needs the SDK side to pick this up.

Tests

src/Build.UnitTests/BackEnd/NodeEndpointOutOfProcBase_Tests.cs (6 cases):

  • NoArchBitParent_X64Child_IsTolerated
  • NoArchBitParent_Arm64Child_IsTolerated
  • X64Parent_X64Child_NotConsideredMismatch
  • X64Parent_Arm64Child_NotTolerated
  • Arm64Parent_X64Child_NotTolerated
  • NoArchBitParent_NoArchBitChild_NotTolerated (guards against a future simplification to return receivedIsX86;)

src/Build.UnitTests/BackEnd/CommunicationsUtilities_Tests.cs (5 cases):

  • GetHandshakeOptions_NetTaskHostParent_SuppressesArchBit × { x64, arm64, x86 }
  • GetHandshakeOptions_NonNetTaskHostParent_KeepsX64ArchBit
  • GetHandshakeOptions_NonNetTaskHostParent_KeepsArm64ArchBit
  • GetHandshakeOptions_NetTaskHostChild_KeepsArchBit

All 11 pass on net10.0.

References

IsAllowedBitnessMismatch is the .NET task host child-side relaxation that
lets a parent without an architecture bit on the wire (typically a .NET
Framework MSBuild) connect to an SDK child that runs on x64. On
Windows-ARM the SDK child runs as arm64 instead, but the function only
considered expectedIsX64, so any arm64 child rejected the connection ->
the launch times out and Runtime="NET" tasks fail with MSB4216.

Also tolerate expectedIsArm64. True cross-arch mismatches (parent X64 vs
child Arm64, or vice versa) remain rejected.

Make the method internal static so the test project can exercise the
tolerance matrix directly. The previous instance method did not touch any
instance state.

Partial fix for dotnet#13879 (Bug A).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 28, 2026 09:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes a child-side handshake check that prevented .NET Framework MSBuild parents from launching a Runtime="NET" task host on Windows-ARM. IsAllowedBitnessMismatch previously only tolerated an x64-expecting child when the parent sent no architecture bit; it now tolerates arm64-expecting children as well, while still rejecting true cross-arch mismatches (X64↔Arm64). The method is also promoted to internal static to make it directly unit-testable.

Changes:

  • Extend IsAllowedBitnessMismatch to accept expectedIsArm64 in addition to expectedIsX64.
  • Convert the method to internal static and refresh its doc comment.
  • Add unit tests covering the no-arch-bit-parent tolerance matrix (x64/arm64 child) and rejection of true cross-arch mismatches.
Show a summary per file
File Description
src/Shared/NodeEndpointOutOfProcBase.cs Tolerate arm64-expecting child in bitness mismatch check; expose as internal static; update XML doc.
src/Build.UnitTests/BackEnd/NodeEndpointOutOfProcBase_Tests.cs New #if NET tests for the tolerance matrix and cross-arch rejection.

Copilot's findings

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

@ViktorHofer ViktorHofer marked this pull request as draft May 28, 2026 10:04
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Review Summary — PR #13890

Well-scoped bug fix extending the .NET task host handshake tolerance to arm64 children. The logic change is correct and the approach is sound.

Findings

# Dimension Severity Summary
1 Test Coverage & Completeness MODERATE Missing test for "no-arch parent → no-arch child" (the path where receivedIsX86==true but method returns false)
2 Documentation Accuracy NIT Inherited mask comment has version/flags description inverted

Clean Dimensions (22/24)

Backwards Compatibility ✓ · ChangeWave ✓ · Performance ✓ · Error Messages ✓ · Logging ✓ · String Comparison ✓ · API Surface ✓ · Target Authoring ✓ · Design ✓ · Cross-Platform ✓ · Code Simplification ✓ · Concurrency ✓ · Naming ✓ · SDK Integration ✓ · Idiomatic C# ✓ · File I/O ✓ · Build Infrastructure ✓ · Scope & PR Discipline ✓ · Evaluation Model ✓ · Correctness ✓ · Dependency Mgmt ✓ · Security ✓

Notes

  • The private → internal static visibility change is appropriate for testability; the class is already internal and the method is a pure stateless predicate.
  • The behavioral change is strictly a relaxation (accepts arm64 where previously only x64 was accepted), so no ChangeWave is needed.
  • The fix correctly rejects true cross-arch mismatches (e.g., parent declares X64 but child expects Arm64).

Note

🔒 Integrity filter blocked 2 items

The following items were blocked because they don't meet the GitHub integrity level.

  • #13890 pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
  • #13890 pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

Generated by Expert Code Review (on open) for issue #13890 · ● 5.5M

Comment thread src/Build.UnitTests/BackEnd/NodeEndpointOutOfProcBase_Tests.cs
Comment thread src/Shared/NodeEndpointOutOfProcBase.cs Outdated
For NET task host launches the child process's architecture is determined
by what the .NET SDK shipped (x64 today, arm64 in the future), not by the
parent's process architecture. Emitting the parent's arch bit creates a
wire-level mismatch with already-shipped SDK children whose arch differs
(e.g. arm64 VS launching an x64 SDK MSBuild.dll, or amd64 VS launching an
arm64 SDK MSBuild.dll). The existing child-side IsAllowedBitnessMismatch
tolerance only accepts "parent sent no arch bit", so this currently fails
with MSB4216.

In CommunicationsUtilities.GetHandshakeOptions, suppress the X64/Arm64 bit
when invoked by the parent for a NET task host (detected by Runtime="net"
in the explicit TaskHostParameters). The child path (TaskHostParameters
.Empty) is unaffected so already-deployed parents that still emit an arch
bit continue to match.

Combined with the child-side Arm64 tolerance in this PR, every parent/
child architecture combination now connects against every already-shipped
SDK that has the original (x64-only) tolerance.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ViktorHofer ViktorHofer changed the title Tolerate Arm64 child in NET task host handshake bitness check Make Runtime=NET task host handshake architecture-agnostic May 28, 2026
@ViktorHofer ViktorHofer requested a review from Copilot May 28, 2026 11:45
- Add NoArchBitParent_NoArchBitChild_NotTolerated test (catches a regression that would simplify the return to just receivedIsX86).
- Fix inverted comment on the 0x00FFFFFF mask (lower 24 bits are flags, upper byte is version).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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: 4/4 changed files
  • Comments generated: 0 new

@ViktorHofer ViktorHofer marked this pull request as ready for review May 28, 2026 13:13

if (!string.IsNullOrEmpty(architectureFlagToSet))
// For the NET task host, the launched child process's architecture is determined by
// what the .NET SDK shipped (x64 today, arm64 in the future), not by the parent's
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't get this comment, what future?

// (which calls GetHandshakeOptions with TaskHostParameters.Empty) keeps its own arch
// bit so that already-deployed parents that still emit an arch bit continue to match
// (or fall through to IsAllowedBitnessMismatch's tolerance).
bool isNetTaskHostParent =
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this whole connection decision tree would greatly benefit from a flowchart/visualizations in docs

@ViktorHofer
Copy link
Copy Markdown
Member Author

@rainersigwald it would be great if you could also take a look :) Just want to be careful.

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.

MSB4216 on Windows ARM: NET task host handshake never tolerates expected=Arm64; #13741 implementation diverges from description

3 participants