From 998719b97b1cbba1fc6e511496c529d1917cf556 Mon Sep 17 00:00:00 2001 From: David McFarland Date: Mon, 13 Apr 2026 15:43:10 -0300 Subject: [PATCH] dotnet-sdk_{10,11}: apply muxer path precedence patch Fixes: #464575 --- ...OT-over-directory-traversal-when-fin.patch | 115 ++++++++++++++++++ .../compilers/dotnet/source/vmr.nix | 1 + 2 files changed, 116 insertions(+) create mode 100644 pkgs/development/compilers/dotnet/source/Prefer-DOTNET_ROOT-over-directory-traversal-when-fin.patch diff --git a/pkgs/development/compilers/dotnet/source/Prefer-DOTNET_ROOT-over-directory-traversal-when-fin.patch b/pkgs/development/compilers/dotnet/source/Prefer-DOTNET_ROOT-over-directory-traversal-when-fin.patch new file mode 100644 index 000000000000..b823d4171512 --- /dev/null +++ b/pkgs/development/compilers/dotnet/source/Prefer-DOTNET_ROOT-over-directory-traversal-when-fin.patch @@ -0,0 +1,115 @@ +From 2c240a877888117865e6331b0575e43671ba6c8e Mon Sep 17 00:00:00 2001 +From: Jamie Magee +Date: Wed, 11 Mar 2026 20:26:29 -0700 +Subject: [PATCH] Prefer DOTNET_ROOT over directory traversal when finding + muxer + +The Muxer constructor walks up two directories from +AppContext.BaseDirectory to find the dotnet host. The runtime resolves +symlinks on that path, so on systems that compose multiple SDK versions +into one directory via symlinks (Nix, Guix), it ends up in the wrong +root and can only see one runtime version. + +Move the DOTNET_HOST_PATH and DOTNET_ROOT checks ahead of the directory +traversal so package managers can set the root explicitly. When neither +variable is set, the old heuristic still runs. + +Also stops _muxerPath from being set to a non-muxer process path (e.g. +testhost) when all other lookups fail. + +Ref: NixOS/nixpkgs#464575 +Ref: #51693 +--- + src/Cli/Microsoft.DotNet.Cli.Utils/Muxer.cs | 61 +++++++++++++-------- + 1 file changed, 37 insertions(+), 24 deletions(-) + +diff --git a/src/sdk/src/Cli/Microsoft.DotNet.Cli.Utils/Muxer.cs b/src/sdk/src/Cli/Microsoft.DotNet.Cli.Utils/Muxer.cs +index 960e162a04..5268cd5543 100644 +--- a/src/sdk/src/Cli/Microsoft.DotNet.Cli.Utils/Muxer.cs ++++ b/src/sdk/src/Cli/Microsoft.DotNet.Cli.Utils/Muxer.cs +@@ -36,45 +36,58 @@ public string MuxerPath + + public Muxer() + { +- // Most scenarios are running dotnet.dll as the app +- // Root directory with muxer should be two above app base: /sdk/ +- string? rootPath = Path.GetDirectoryName(Path.GetDirectoryName(AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar))); +- if (rootPath is not null) ++ // Check environment variables first to allow package managers and ++ // other tools to explicitly set the dotnet root. This is needed ++ // when the SDK is installed via symlinks (e.g. Nix, Guix) where ++ // the directory-traversal heuristic below would resolve symlinks ++ // and find the wrong root directory. ++ string? dotnetHostPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH"); ++ if (dotnetHostPath is not null && File.Exists(dotnetHostPath)) + { +- string muxerPathMaybe = Path.Combine(rootPath, $"{MuxerName}{FileNameSuffixes.CurrentPlatform.Exe}"); +- if (File.Exists(muxerPathMaybe)) ++ _muxerPath = dotnetHostPath; ++ } ++ ++ if (_muxerPath is null) ++ { ++ var dotnetRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT"); ++ if (dotnetRoot is not null) + { +- _muxerPath = muxerPathMaybe; ++ string rootMuxer = Path.Combine(dotnetRoot, $"dotnet{Constants.ExeSuffix}"); ++ if (File.Exists(rootMuxer)) ++ { ++ _muxerPath = rootMuxer; ++ } + } + } + + if (_muxerPath is null) + { +- // Best-effort search for muxer. +- // SDK sets DOTNET_HOST_PATH as absolute path to current dotnet executable ++ // Most scenarios are running dotnet.dll as the app ++ // Root directory with muxer should be two above app base: /sdk/ ++ string? rootPath = Path.GetDirectoryName(Path.GetDirectoryName(AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar))); ++ if (rootPath is not null) ++ { ++ string muxerPathMaybe = Path.Combine(rootPath, $"{MuxerName}{FileNameSuffixes.CurrentPlatform.Exe}"); ++ if (File.Exists(muxerPathMaybe)) ++ { ++ _muxerPath = muxerPathMaybe; ++ } ++ } ++ } ++ ++ if (_muxerPath is null) ++ { ++ // Last resort: if the current process is the dotnet muxer itself, use its path. + #if NET6_0_OR_GREATER + string? processPath = Environment.ProcessPath; + #else + string processPath = Process.GetCurrentProcess().MainModule.FileName; + #endif + +- // The current process should be dotnet in most normal scenarios except when dotnet.dll is loaded in a custom host like the testhost +- if (processPath is not null && !Path.GetFileNameWithoutExtension(processPath).Equals("dotnet", StringComparison.OrdinalIgnoreCase)) ++ if (processPath is not null && Path.GetFileNameWithoutExtension(processPath).Equals("dotnet", StringComparison.OrdinalIgnoreCase)) + { +- // SDK sets DOTNET_HOST_PATH as absolute path to current dotnet executable +- processPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH"); +- if (processPath is null) +- { +- // fallback to DOTNET_ROOT which typically holds some dotnet executable +- var root = Environment.GetEnvironmentVariable("DOTNET_ROOT"); +- if (root is not null) +- { +- processPath = Path.Combine(root, $"dotnet{Constants.ExeSuffix}"); +- } +- } ++ _muxerPath = processPath; + } +- +- _muxerPath = processPath; + } + } + +-- +2.53.0 + diff --git a/pkgs/development/compilers/dotnet/source/vmr.nix b/pkgs/development/compilers/dotnet/source/vmr.nix index 33b457d6822f..fed282e5f00d 100644 --- a/pkgs/development/compilers/dotnet/source/vmr.nix +++ b/pkgs/development/compilers/dotnet/source/vmr.nix @@ -149,6 +149,7 @@ stdenv.mkDerivation { ./fix-aspnetcore-portable-build.patch ./vmr-compiler-opt-v8.patch ] + ++ lib.optional (lib.versionAtLeast version "10") ./Prefer-DOTNET_ROOT-over-directory-traversal-when-fin.patch ++ lib.optionals (lib.versionAtLeast version "11") ( [ ./fix-skiperroronprebuilts.patch