Skip to content

Commit cf66f4d

Browse files
committed
environment: check execute permission in TryLocateExecutable
In f1a1ae5 (environment: manually scan $PATH on POSIX systems, 2022-05-31) the `which`-based lookup was replaced with a manual PATH scan that only checks FileExists, without verifying execute permissions. Unlike `which`, this means a non-executable file earlier in PATH can shadow a valid executable, causing process creation to fail when GCM later tries to run the located path. Add an IsExecutable check that verifies at least one execute bit is set on POSIX systems, matching the behaviour of `which`. On Windows, any existing file is considered executable. Guard the POSIX-specific File.GetUnixFileMode call with #if !NETFRAMEWORK for net472 compatibility. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
1 parent 2b8f907 commit cf66f4d

1 file changed

Lines changed: 23 additions & 1 deletion

File tree

src/shared/Core/EnvironmentBase.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ internal virtual bool TryLocateExecutable(string program, ICollection<string> pa
138138
{
139139
string candidatePath = Path.Combine(basePath, program);
140140
if (FileSystem.FileExists(candidatePath) && (pathsToIgnore is null ||
141-
!pathsToIgnore.Contains(candidatePath, StringComparer.OrdinalIgnoreCase)))
141+
!pathsToIgnore.Contains(candidatePath, StringComparer.OrdinalIgnoreCase))
142+
&& IsExecutable(candidatePath))
142143
{
143144
path = candidatePath;
144145
return true;
@@ -172,6 +173,27 @@ public void Refresh()
172173
}
173174

174175
protected abstract IReadOnlyDictionary<string, string> GetCurrentVariables();
176+
177+
private static bool IsExecutable(string path)
178+
{
179+
// On Windows, any file that exists is considered executable.
180+
if (!PlatformUtils.IsPosix())
181+
return true;
182+
183+
#if NETFRAMEWORK
184+
// .NET Framework only targets Windows, so this is unreachable.
185+
return true;
186+
#else
187+
// On POSIX, verify the file has at least one execute bit set,
188+
// matching the behaviour of the `which` utility.
189+
#pragma warning disable CA1416 // Platform guard via PlatformUtils.IsPosix()
190+
var mode = File.GetUnixFileMode(path);
191+
return (mode & (UnixFileMode.UserExecute |
192+
UnixFileMode.GroupExecute |
193+
UnixFileMode.OtherExecute)) != 0;
194+
#pragma warning restore CA1416
195+
#endif
196+
}
175197
}
176198

177199
public static class EnvironmentExtensions

0 commit comments

Comments
 (0)