Skip to content

just prioritizes System32 over $Path; is impossible to put bash on $Path if you have WSL2 installed #2947

Description

@cspotcode

This is a doozy, but I've done my best to explain it concisely.

Bug

just on Windows, by virtue of std::process::Command, does not respect $Path they way we want when resolving shell or interpreter executables. This means, when you try to use bash and you have a bash.exe on your $Path (perhaps it's Git Bash), and you coincidentally have WSL2 installed, just ignores your preferred bash.exe and instead calls C:\Windows\System32\bash.exe, which launches a shell inside the WSL2 guest virtual machine, not a bash shell on your host.

This breaks integration tests on Windows, because test.rs sets --shell bash.

It's also a bug for anyone who wants to use Git Bash or any other bash.exe as their just shell.

Fix

My proposed fix is for just to resolve executables to absolute paths using "which" or "pathsearch" crates. It can use this implementation consistently across:

  • which() function
  • set shell
  • [script()] / set script-interpreter

We already want which() functionality, so using it to resolve shells and interpreters to absolute paths, fixing a bug in the process, and fixing the integration tests on Windows: seems like a win all around.

Reproduction

You can reproduce it quickly if you have WSL2 installed:

# Put C:\Program Files\git\bin at the top of $Path
# Open any shell to confirm this works:
$ bash -c 'if wslinfo --version >/dev/null 2>&1; then echo BAD we are in WSL2 ; else echo GOOD, we are in Git Bash! ; fi'
GOOD, we are in Git Bash!

Now create this justfile:

set shell := ["bash", "-c"]
@default:
    if wslinfo --version >/dev/null 2>&1; then echo BAD we are in WSL2 ; else echo GOOD, we are in Git Bash! ; fi

...and run it in the shell:

$ just
BAD we are in WSL2

I've created a more exhaustive reproduction here: https://github.com/cspotcode/repros2/tree/just-path-behavior-windows

Q&A

Why in the world does std::process::Command prioritize System32?

EDIT previous link was outdated

These rust release notes explain it clearly.

It checks in this order: child's $PATH if it's been explicitly set, then System32, then parent's $PATH
We want it to check $PATH before System32. (note that $PATH always contains a System32 entry, too)

It makes some (buggy, undocumented) attempts to resolve the executable only when a PATH env var has been explicitly set. In just's case, it doesn't resolve the executable, leaving it to CreateProcess which checks a bunch of places before $Path: the local directory, System32/, etc.

https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa

But isn't default resolution the correct (albeit strange) behavior, since it's the rust standard library?

I argue: no, it's not what we want. The proof is that other shells intentionally don't behave this way! They resolve the target executable according to $Path before the System32 fallback, which matches user expectations. They do not give C:\Windows\System32 special treatment.

What's the problem with using WSL2 bash.exe as your shell?

It might work sometimes, break in others. For example, the C: drive is often mounted at a different location in WSL. Ultimately, just should respect $Path the same way that other shells do both on Windows and Linux.

Related tickets

#2926
#2826
rust-lang/rust#15149
rust-lang/rust#37519

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions