Windows: Where should `Command::new` look for executables?

On Windows, what strategy should Command::new use to find executables? Possible search locations include:

  • The exe's directory. Traditionally the executable's directory is a kind of application bundle. Binaries are in the root and resources are in subfolders (this also affects default dll loading if not using an rpath equivalent). On the other hand, if that's the wanted behaviour of Command then it's likely that the developer won't want to be running some random other application if, for some reason, it's not where it's supposed to be. And it's easy enough to use std::env::current_exe to get the exe's directory in that case.
  • Path environment variable.
  • "App Paths". Applications can register their presence in the registry instead of (or in addition to) using Path. E.g. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
  • System folders (i.e. C:\Windows\System32 and C:\Windows). These should already be in Path but if they're not then should they still be searched? Do people generally assume (as now) that cmd et al will always be found?

Should Rust try to search all these locations? Some? Or should it just stick to the Path environment variable?

Ideally this would be configurable in some way but even then it's desirable to have sensible defaults. Also assume for the sake of this thread that some implementation details of Command::new can be changed (or else it's replaced with a different method). This follows on from here in which it was pointed out that there can be security implications with the current behaviour of Command::new.

1 Like

Whatever the searched path(s), it should match the lookup behavior of one of

  • typing the exe path/name into the Run dialogue
  • typing the exe path/name into cmd
  • typing the exe path/name into Powershell
  • calling Win32/CreateProcess
  • calling C#/Process.Start

Or some other system that's default on Windows.

It doesn't matter which, really, just that it matches something. IIRC, all of these are subtly different :frowning:

2 Likes

See, I think the fact these all have subtle differences is potentially an argument for not necessarily being too strict about following any one so long as the rules are simple enough to understand. All except CreateProcess and cmd can potentially change in the future (and even those may depend on particular settings or configurations). But any way, to the best of my knowledge this is the search behaviour:

  • CreateProcess:
    • The exe's directory.
    • The current directory*
    • Windows system directories
    • Path environment variable
    • It only runs .exe files
  • ShellExecute:
    • The current directory*
    • Windows system directories
    • Path environment variable
    • App Paths registry
    • It can "run" non-.exe files based on their registered "verbs", if any.
  • Run dialogue:
    • I can't find documentation but it's presumably using ShellExecute. Except I'd assume the current directory isn't relevant.
  • Powershell:
    • Path environment variable
    • It can run non-".exe" files in a similar way to ShellExecute but limited to the extensions listed in the PATHEXT environment variable.
  • cmd:
    • Internal command
    • The current directory*
    • Path environment variable
    • It can run non-".exe" files in a similar way to ShellExecute but limited to the extensions listed in the PATHEXT environment variable.
  • Process.Start:
    • It seems to be a wrapper around CreateProcess and ShellExecute depending on how exactly it's used. I'm not sure if it does anything on top of that.

* NOTE: Using the current directory is what the CVE takes issue with.

I've linked my sources above. Those without links I've provided my best guess based on my current understanding. I would need to investigate further though.

1 Like

The cmd/powershell way would be best IMHO as it most closely matches the behavior on most other OSes where only PATH is considered.

I'm generally nervous of the argument that Windows implementations should match posix/Linux conventions (if only because this can lead to headaches and buggy workarounds for incompatible behaviours).

But in this specific case it would be the easiest to both implement and explain. And it's justifiable based on Powershell.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.