Skip to content

Conversation

Byron
Copy link
Member

@Byron Byron commented Jun 24, 2024

Try harder to find the git executable on Windows, including known locations.
Also try really hard not to do any unnecessary IO work.

Tasks

  • utilities to find Git executable
  • use these within Gitoxide instead of relying on PATH exclusively.

@Byron Byron changed the title try harder on Windows find git.exe harder on Windows Jun 24, 2024
@Byron Byron force-pushed the git-executable branch 2 times, most recently from 364b148 to 1866a52 Compare June 24, 2024 13:27
Byron added 2 commits June 24, 2024 15:29
That way we can make it easier to rely on Git even if finding it is a bit
more involved.
That way we are more likely to find Git even on platforms that
don't have a reliable PATH.
@Byron Byron merged commit f0a4431 into main Jun 24, 2024
@Byron Byron deleted the git-executable branch June 24, 2024 14:04
EliahKagan added a commit to EliahKagan/gitoxide that referenced this pull request Aug 18, 2025
`gix-path` looks for and runs an installed `git` executable, when
present, to discover information about how Git is set up. This
is used in several functions in `gix_path::env()`, especially on
Windows, where if `git` is not found in a `PATH` search, then
common installation locations for Git for Windows are checked.

These locations on Windows are resolved based on information from
the current environment, since different systems have different
program files directories. This was implemented in GitoxideLabs#1456 (which
built on GitoxideLabs#1419). Although this was sufficient to find the most
common Git for Windows installation locations, it did not find
ARM64 builds of Git for Windows. Such builds place the non-shim
`git.exe` program in `(git root)/clangarm64/bin`, rather than in
`(git root)/mingw64/bin` or `(git root)/mingw32/bin`. At the time
of GitoxideLabs#1416 and GitoxideLabs#1419, no stable ARM64 builds of Git for Windows were
available. Since then, Git for Windows began releasing such builds.

This modifies the alternative locations examined if `git.exe` is
not found in a `PATH` search on Windows so that, where `(git root)`
is in a 64-bit program files directory, we check for a
`(git root)/clangarm64/bin` directory, in addition to checking for
a `(git root)/mingw64/bin` directory as was already done.

(Although 64-bit and 32-bit program files directories are separate,
on ARM64 systems the 64-bit program files directory is used for
both ARM64 programs that the system can run directly and x86_64
programs that the system must run through emulation.)

This checks both `clangarm64` and `mingw64`, where `mingw64` was
checked before. It does so in that order, because if both were
available, then we are probably on an ARM64 system, and the ARM64
build of Git for Windows should be preferred, both because it will
tend to perform better and because the user is likely to expect
that to be used (an x86_64 build, especially if present directly
alongside an ARM64 build, may be left over from a previous version
of Git for Windows that didn't have ARM64 builds and that was only
incompletely installed).

It does so on all systems where we had chcked `mingw64` before,
even on x86_64 systems. This is because, to limit dependencies and
code complexity, we have been examining only environment variables
to ascertain which program files directories exist and whether they
are 64-bit or 32-bit program files directories; and at least for
now, this preserves that general approach. But determining whether
the system is ARM64 or x86_64 is less straightforward (than program
files directory locations) to ascertain from environment variables,
if we are to accommodate all reasonable edge cases.

This is because, if our parent process (or other ancestor) passes
down a sanitized environment, while still attempting to allow the
program files directories to be found, then:

- That process should make available all of the `ProgramFiles`,
  `ProgramW6432`, and `ProgramFiles(x86)` variables that exist in
  its own environment.

- Even if not designed with the WoW64 rules in mind, it will likely
  pass down at least `ProgramFiles`.

- In contrast, it may omit the variables `PROCESSOR_ARCHITECTURE`
  and `PROCESSOR_ARCHITEW6432`, which are not obviously needed for
  locating programs.

- Even if `PROCESSOR_ARCHITE*` variables are preserved, there are
  common cases where they are not sufficient, such as when we are
  an x86_64 build, but the system is ARM64 and Git for Windows is
  ARM64.

- Although the `PROCESSOR_IDENTIFIER` variable is more reliable if
  present (see actions/partner-runner-images#117 for an example of
  where this is more informative than `PROCESSOR_ARCHITECTURE`), it
  is more complex to parse, and more importantly it may be omitted
  even if a parent/ancestor lets `PROCESSOR_ARCHITE*` through.
EliahKagan added a commit to EliahKagan/gitoxide that referenced this pull request Aug 18, 2025
`gix-path` looks for and runs an installed `git` executable, when
present, to discover information about how Git is set up. This
is used in several functions in `gix_path::env()`, especially on
Windows, where if `git` is not found in a `PATH` search, then
common installation locations for Git for Windows are checked.

These locations on Windows are resolved based on information from
the current environment, since different systems have different
program files directories. This was implemented in GitoxideLabs#1456 (which
built on GitoxideLabs#1419). Although this was sufficient to find the most
common Git for Windows installation locations, it did not find
ARM64 builds of Git for Windows. Such builds place the non-shim
`git.exe` program in `(git root)\clangarm64\bin`, rather than in
`(git root)\mingw64\bin` or `(git root)\mingw32\bin`. At the time
of GitoxideLabs#1416 and GitoxideLabs#1419, no stable ARM64 builds of Git for Windows were
available. Since then, Git for Windows began releasing such builds.

This modifies the alternative locations examined if `git.exe` is
not found in a `PATH` search on Windows so that, where `(git root)`
is in a 64-bit program files directory, we check for a
`(git root)\clangarm64\bin` directory, in addition to checking for
a `(git root)\mingw64\bin` directory as was already done.

Although 64-bit and 32-bit program files directories are separate,
on ARM64 systems the 64-bit program files directory is used for
both ARM64 programs that the system can run directly and x86_64
programs that the system must run through emulation.

This checks both `clangarm64` and `mingw64`, where `mingw64` was
checked before. It does so in that order, because if both are
available, then we are probably on an ARM64 system, and the ARM64
build of Git for Windows should be preferred, both because it will
tend to perform better and because the user is likely to expect
that to be used. (An x86_64 build, especially if present directly
alongside an ARM64 build, may be left over from a previous version
of Git for Windows that didn't have ARM64 builds and that was only
incompletely uninstalled.)

This checks both, in that order, on all systems where we had
checked `mingw64` before, even on x86_64 systems. This is because,
to limit production dependencies and code complexity, we have been
examining only environment variables to ascertain which program
files directories exist and whether they are 64-bit or 32-bit
program files directories; and at least for now, this preserves
that general approach. But determining whether the system is ARM64
or x86_64 is less straightforward (than program files directory
locations) to ascertain from environment variables, if we are to
accommodate all reasonable edge cases.

This is because, if our parent process (or other ancestor) passes
down a sanitized environment while still attempting to allow the
program files directories to be found, then:

- That process should make available all of the `ProgramFiles`,
  `ProgramW6432`, and `ProgramFiles(x86)` variables that exist in
  its own environment.

  This is becauses, on 64-bit Windows, the child `ProgramFiles` is
  populated from the parent `ProgramW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)`, depending on the architectures of the parent
  and child, if the parent passes down the relevant variable.

- Even if the parent/ancestor is not designed with the WoW64 rules
  in mind, it will likely pass down at least `ProgramFiles`.

  This will then be used as the child `ProgramFiles`, if whichever
  of `ProgramFilesW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)` is relevant is not passed down by the parent.

- In contrast, the parent/ancestor may omit the variables
  `PROCESSOR_ARCHITECTURE` and `PROCESSOR_ARCHITEW6432`, which are
  not obviously needed for locating programs.

- Even if `PROCESSOR_ARCHITE*` variables are preserved, there are
  common cases where they are not sufficient, such as when we are
  an x86_64 build, but the system is ARM64 and Git for Windows is
  ARM64. `gix-path` is a library crate, and it is highly plausible
  that it may be used in an x86_64 program on such a system.

  Also, if it is used in an Arm64EC program, then even though its
  code may be ARM64, the program would still be treated as an
  x86_64 program in its interactions with the system and other
  programs, including in how its environment variables' values are
  populated and how their values are adjusted due to WoW64.

- Although the `PROCESSOR_IDENTIFIER` variable is more reliable if
  present (see actions/partner-runner-images#117 for an example of
  where this is more informative than `PROCESSOR_ARCHITECTURE`), it
  is more complex to parse. More importantly it may be omitted even
  if a parent/ancestor lets `PROCESSOR_ARCHITE*` through.

- It would sometimes work to look for `ProgramFiles(ARM)`; if set,
  then the Windows system could be treated as ARM64. However, this
  may be omitted even if other program files related environment
  variables including `ProgramFiles(x86)` are passed down. This
  could happen because the `ProgramFiles(ARM)` environment variable
  is less well known. It could also happen if the parent process is
  only passing down variables needed to find Git for Windows, for
  which one should not need to know the 32-bit ARM program files
  directory location, since Git for Windows has never had 32-bit
  ARM builds.

  Augmenting the search by checking the filesystem for a
  `Program Files (ARM)` sibling directory should not be done,
  because this directory has no special meaning outside of ARM64
  Windows. It may therefore be left over from a previous
  installation or migration, or even created by a local user as in
  the CVE-2024-40644 scenario patched in GitoxideLabs#1456.

(Complexities related to deciding whether and in what order to
search for `bin` subdirectories called `clangarm64`, `mingw64`, and
`mingw32` would go away if we looked for the shim rather than the
non-shim executable. This is because the path from `(git root)` to
the shim does not contain a directory component named after a build
target. That simplification would carry its own tradeoffs, and it
is unclear if it ought to be done; it is not done here.)
EliahKagan added a commit to EliahKagan/gitoxide that referenced this pull request Aug 18, 2025
`gix-path` looks for and runs an installed `git` executable, when
present, to discover information about how Git is set up. This
is used in several functions in `gix_path::env()`, especially on
Windows, where if `git` is not found in a `PATH` search, then
common installation locations for Git for Windows are checked.

These locations on Windows are resolved based on information from
the current environment, since different systems have different
program files directories. This was implemented in GitoxideLabs#1456 (which
built on GitoxideLabs#1419). Although this was sufficient to find the most
common Git for Windows installation locations, it did not find
ARM64 builds of Git for Windows. Such builds place the non-shim
`git.exe` program in `(git root)\clangarm64\bin`, rather than in
`(git root)\mingw64\bin` or `(git root)\mingw32\bin`. At the time
of GitoxideLabs#1416 and GitoxideLabs#1419, no stable ARM64 builds of Git for Windows were
available. Since then, Git for Windows began releasing such builds.

This modifies the alternative locations examined if `git.exe` is
not found in a `PATH` search on Windows so that, where `(git root)`
is in a 64-bit program files directory, we check for a
`(git root)\clangarm64\bin` directory, in addition to checking for
a `(git root)\mingw64\bin` directory as was already done.

Although 64-bit and 32-bit program files directories are separate,
on ARM64 systems the 64-bit program files directory is used for
both ARM64 programs that the system can run directly and x86_64
programs that the system must run through emulation.

This checks both `clangarm64` and `mingw64`, where `mingw64` was
checked before. It does so in that order, because if both are
available, then we are probably on an ARM64 system, and the ARM64
build of Git for Windows should be preferred, both because it will
tend to perform better and because the user is likely to expect
that to be used. (An x86_64 build, especially if present directly
alongside an ARM64 build, may be left over from a previous version
of Git for Windows that didn't have ARM64 builds and that was only
incompletely uninstalled.)

This checks both, in that order, on all systems where we had
checked `mingw64` before, even on x86_64 systems. This is because,
to limit production dependencies and code complexity, we have been
examining only environment variables to ascertain which program
files directories exist and whether they are 64-bit or 32-bit
program files directories; and at least for now, this preserves
that general approach. But determining whether the system is ARM64
or x86_64 is less straightforward (than program files directory
locations) to ascertain from environment variables, if we are to
accommodate all reasonable edge cases.

The reason is that, if our parent process (or other ancestor)
passes down a sanitized environment while still attempting to allow
the program files directories to be found, then:

- That process should make available all of the `ProgramFiles`,
  `ProgramW6432`, and `ProgramFiles(x86)` variables that exist in
  its own environment.

  This is becauses, on 64-bit Windows, the child `ProgramFiles` is
  populated from the parent `ProgramW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)`, depending on the architectures of the parent
  and child, if the parent passes down the relevant variable.

- Even if the parent/ancestor is not designed with the WoW64 rules
  in mind, it will likely pass down at least `ProgramFiles`.

  This will then be used as the child `ProgramFiles`, if whichever
  of `ProgramFilesW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)` is relevant is not passed down by the parent.

- In contrast, the parent/ancestor may omit the variables
  `PROCESSOR_ARCHITECTURE` and `PROCESSOR_ARCHITEW6432`, which are
  not obviously needed for locating programs.

- Even if `PROCESSOR_ARCHITE*` variables are preserved, there are
  common cases where they are not sufficient, such as when we are
  an x86_64 build, but the system is ARM64 and Git for Windows is
  ARM64. `gix-path` is a library crate, and it is highly plausible
  that it may be used in an x86_64 program on such a system.

  Also, if it is used in an Arm64EC program, then even though its
  code may be ARM64, the program would still be treated as an
  x86_64 program in its interactions with the system and other
  programs, including in how its environment variables' values are
  populated and how their values are adjusted due to WoW64.

- Although the `PROCESSOR_IDENTIFIER` variable is more reliable if
  present (see actions/partner-runner-images#117 for an example of
  where this is more reliable than `PROCESSOR_ARCHITECTURE`), it
  is more complex to parse. More importantly it may be omitted even
  if a parent/ancestor lets `PROCESSOR_ARCHITE*` through.

- It would sometimes work to look for `ProgramFiles(ARM)`; if set,
  then the Windows system could be treated as ARM64. However, this
  may be omitted even if other program files related environment
  variables including `ProgramFiles(x86)` are passed down. This
  could happen because the `ProgramFiles(ARM)` environment variable
  is less well known. It could also happen if the parent process is
  only passing down variables needed to find Git for Windows, for
  which one should not need to know the 32-bit ARM program files
  directory location, since Git for Windows has never had 32-bit
  ARM builds.

  Relatedly, augmenting the search by checking the filesystem for a
  sibling directory named `Program Files (ARM)` should not be done,
  because this directory has no special meaning outside of ARM64
  Windows. It may therefore be left over from a previous
  installation or migration, or even created by a local user as in
  the CVE-2024-40644 scenario patched in GitoxideLabs#1456.

(Complexities related to deciding whether and in what order to
search `bin` subdirectories of `clangarm64`, `mingw64`, and
`mingw32` would go away if we looked for the shim rather than the
non-shim executable. This is because the path from `(git root)` to
the shim does not contain a directory component named after a build
target. That simplification would carry its own tradeoffs, and it
is unclear if it ought to be done; it is not done here.)
EliahKagan added a commit to EliahKagan/gitoxide that referenced this pull request Aug 18, 2025
`gix-path` looks for and runs an installed `git` executable, when
present, to discover information about how Git is set up. This
is used in several functions in `gix_path::env()`, especially on
Windows, where if `git` is not found in a `PATH` search, then
common installation locations for Git for Windows are checked.

These locations on Windows are resolved based on information from
the current environment, since different systems have different
program files directories. This was implemented in GitoxideLabs#1456 (which
built on GitoxideLabs#1419). Although this was sufficient to find the most
common Git for Windows installation locations, it did not find
ARM64 builds of Git for Windows. Such builds place the non-shim
`git.exe` program in `(git root)\clangarm64\bin`, rather than in
`(git root)\mingw64\bin` or `(git root)\mingw32\bin`. At the time
of GitoxideLabs#1416 and GitoxideLabs#1419, no stable ARM64 builds of Git for Windows were
available. Since then, Git for Windows began releasing such builds.

This modifies the alternative locations examined if `git.exe` is
not found in a `PATH` search on Windows so that, where `(git root)`
is in a 64-bit program files directory, we check for a
`(git root)\clangarm64\bin` directory, in addition to checking for
a `(git root)\mingw64\bin` directory as was already done.

Although 64-bit and 32-bit program files directories are separate,
on ARM64 systems the 64-bit program files directory is used for
both ARM64 programs that the system can run directly and x86_64
programs that the system must run through emulation.

This checks both `clangarm64` and `mingw64`, where `mingw64` was
checked before. It does so in that order, because if both are
available, then we are probably on an ARM64 system, and the ARM64
build of Git for Windows should be preferred, both because it will
tend to perform better and because the user is likely to expect
that to be used. (An x86_64 build, especially if present directly
alongside an ARM64 build, may be left over from a previous version
of Git for Windows that didn't have ARM64 builds and that was only
incompletely uninstalled.)

This checks both, in that order, on all systems where we had
checked `mingw64` before, even on x86_64 systems. This is because:

- To limit production dependencies and code complexity, we have
  been examining only environment variables to ascertain which
  program files directories exist and whether they are 64-bit or
  32-bit program files directories. At least for now, this
  preserves that general approach.

- But determining whether the system is ARM64 or x86_64 is less
  straightforward to ascertain from environment variables, if we
  are to accommodate all reasonable edge cases.

The reason it's less straightforward is that, if our parent process
(or other ancestor) passes down a sanitized environment while still
attempting to let the program files directories be found, then:

- That process should make available all of the `ProgramFiles`,
  `ProgramW6432`, `ProgramFiles(x86)`, and `ProgramFiles(ARM)`
  variables that exist in its own environment.

  (This is because, on 64-bit Windows, the child `ProgramFiles` is
  populated from the parent `ProgramW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)`, depending on the architectures of the parent
  and child, if the parent passes down the relevant variable.)

- Even if the parent/ancestor is not designed with the WoW64 rules
  in mind, it will likely pass down at least `ProgramFiles`.

  This will then be used as the child `ProgramFiles`, if whichever
  of `ProgramFilesW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)` is relevant is not passed down by the parent.

- In contrast, the parent/ancestor may omit the variables
  `PROCESSOR_ARCHITECTURE` and `PROCESSOR_ARCHITEW6432`, which are
  not obviously needed for locating programs.

- Even if `PROCESSOR_ARCHITE*` variables are preserved, there are
  common cases where they are not sufficient, such as when we are
  an x86_64 build but the system is ARM64 and Git for Windows is
  ARM64. `gix-path` is a library crate, and it is likely sometimes
  to be used in an x86_64 program on such a system.

  Also, if `gix-path` is used in an Arm64EC program, then even
  though its code may be ARM64 with the `arm64ec-pc-windows-msvc`
  Rust target, the program would still be treated as an x86_64
  program in its interactions with the system and other programs,
  including in how its environment variables' values are populated
  and how their values are affected by WoW64.

  (`gix-path` may also be x86_64 code where the program is Arm64EC.
  One way this happens is if `gix-path` is used in an x86_64 DLL,
  and an Arm64EC program loads the DLL. Using x86_64 DLLs from
  ARM64 code is one of the reasons a program may use an Arm64EC
  target. In this case, the Rust target will be for x86_64, not
  ARM64 or Arm64EC. So checking our own build target can't fully
  check if the program is Arm64EC.)

- Although the `PROCESSOR_IDENTIFIER` variable is more reliable if
  present--see actions/partner-runner-images#117 for an example of
  where this is more reliable than `PROCESSOR_ARCHITECTURE`--it is
  more complex to parse. More importantly, it may be omitted even
  if a parent/ancestor lets `PROCESSOR_ARCHITE*` through.

- It would sometimes work to look for `ProgramFiles(ARM)`. This
  environment variable, if present, gives the 32-bit ARM program
  files directory location. If set, then the Windows system could
  be treated as ARM64. However, a parent/ancestor process may omit
  this even when passing down program files related environment
  variables, including `ProgramFiles(x86)`. It may do so because
  the `ProgramFiles(ARM)` environment variable is less well known.
  It may also do so intentionally, if the parent process is only
  passing down variables needed to find Git for Windows, for which
  one shouldn't need to know the 32-bit ARM program files directory
  location, since Git for Windows has never had 32-bit ARM builds.

  Relatedly, augmenting the search by checking the filesystem for a
  sibling directory named `Program Files (ARM)` should not be done,
  because this directory has no special meaning outside of ARM64
  Windows. It may therefore be left over from a previous
  installation or migration, or even created by a local user as in
  the CVE-2024-40644 scenario patched in GitoxideLabs#1456.

(Complexities related to deciding whether and in what order to
search `bin` subdirectories of `clangarm64`, `mingw64`, and
`mingw32` would go away if we looked for the shim rather than the
non-shim executable. This is because the path from `(git root)` to
the shim does not contain a directory component named after a build
target. That simplification would carry its own tradeoffs, and it
is unclear if it ought to be done; it is not done here.)
EliahKagan added a commit to EliahKagan/gitoxide that referenced this pull request Aug 18, 2025
`gix-path` looks for and runs an installed `git` executable, when
present, to discover information about how Git is set up. This
is used in several functions in `gix_path::env`, especially on
Windows, where if `git` is not found in a `PATH` search, then
common installation locations for Git for Windows are checked.

These locations on Windows are resolved based on information from
the current environment, since different systems have different
program files directories. This was implemented in GitoxideLabs#1456 (which
built on GitoxideLabs#1419). Although this was sufficient to find the most
common Git for Windows installation locations, it did not find
ARM64 builds of Git for Windows. Such builds place the non-shim
`git.exe` program in `(git root)\clangarm64\bin`, rather than in
`(git root)\mingw64\bin` or `(git root)\mingw32\bin`. At the time
of GitoxideLabs#1416 and GitoxideLabs#1419, no stable ARM64 builds of Git for Windows were
available. Since then, Git for Windows began releasing such builds.

This modifies the alternative locations examined if `git.exe` is
not found in a `PATH` search on Windows so that, where `(git root)`
is in a 64-bit program files directory, we check for a
`(git root)\clangarm64\bin` directory, in addition to checking for
a `(git root)\mingw64\bin` directory as was already done.

Although 64-bit and 32-bit program files directories are separate,
on ARM64 systems the 64-bit program files directory is used for
both ARM64 programs that the system can run directly and x86_64
programs that the system must run through emulation.

This checks both `clangarm64` and `mingw64`, where `mingw64` was
checked before. It does so in that order, because if both are
available, then we are probably on an ARM64 system, and the ARM64
build of Git for Windows should be preferred, both because it will
tend to perform better and because the user is likely to expect
that to be used. (An x86_64 build, especially if present directly
alongside an ARM64 build, may be left over from a previous version
of Git for Windows that didn't have ARM64 builds and that was only
incompletely uninstalled.)

This checks both, in that order, on all systems where we had
checked `mingw64` before, even on x86_64 systems. This is because:

- To limit production dependencies and code complexity, we have
  been examining only environment variables to ascertain which
  program files directories exist and whether they are 64-bit or
  32-bit program files directories. At least for now, this
  preserves that general approach.

- But determining whether the system is ARM64 or x86_64 is less
  straightforward to ascertain from environment variables, if we
  are to accommodate all reasonable edge cases.

The reason it's less straightforward is that, if our parent process
(or other ancestor) passes down a sanitized environment while still
attempting to let the program files directories be found, then:

- That process should make available all of the `ProgramFiles`,
  `ProgramW6432`, `ProgramFiles(x86)`, and `ProgramFiles(ARM)`
  variables that exist in its own environment.

  (This is because, on 64-bit Windows, the child `ProgramFiles` is
  populated from the parent `ProgramW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)`, depending on the architectures of the parent
  and child, if the parent passes down the relevant variable.)

- Even if the parent/ancestor is not designed with the WoW64 rules
  in mind, it will likely pass down at least `ProgramFiles`.

  This will then be used as the child `ProgramFiles`, if whichever
  of `ProgramFilesW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)` is relevant is not passed down by the parent.

- In contrast, the parent/ancestor may omit the variables
  `PROCESSOR_ARCHITECTURE` and `PROCESSOR_ARCHITEW6432`, which are
  not obviously needed for locating programs.

- Even if `PROCESSOR_ARCHITE*` variables are preserved, there are
  common cases where they are not sufficient, such as when we are
  an x86_64 build but the system is ARM64 and Git for Windows is
  ARM64. `gix-path` is a library crate, and it is likely sometimes
  to be used in an x86_64 program on such a system.

  Also, if `gix-path` is used in an Arm64EC program, then even
  though its code may be ARM64 with the `arm64ec-pc-windows-msvc`
  Rust target, the program would still be treated as an x86_64
  program in its interactions with the system and other programs,
  including in how its environment variables' values are populated
  and how their values are affected by WoW64.

  (`gix-path` may also be x86_64 code where the program is Arm64EC.
  One way this happens is if `gix-path` is used in an x86_64 DLL,
  and an Arm64EC program loads the DLL. Using x86_64 DLLs from
  ARM64 code is one of the reasons a program may use an Arm64EC
  target. In this case, the Rust target will be for x86_64, not
  ARM64 or Arm64EC. So checking our own build target can't fully
  check if the program is Arm64EC.)

- Although the `PROCESSOR_IDENTIFIER` variable is more reliable if
  present--see actions/partner-runner-images#117 for an example of
  where this is more reliable than `PROCESSOR_ARCHITECTURE`--it is
  more complex to parse. More importantly, it may be omitted even
  if a parent/ancestor lets `PROCESSOR_ARCHITE*` through.

- It would sometimes work to look for `ProgramFiles(ARM)`. This
  environment variable, if present, gives the 32-bit ARM program
  files directory location. If set, then the Windows system could
  be treated as ARM64. However, a parent/ancestor process may omit
  this even when passing down program files related environment
  variables, including `ProgramFiles(x86)`. It may do so because
  the `ProgramFiles(ARM)` environment variable is less well known.
  It may also do so intentionally, if the parent process is only
  passing down variables needed to find Git for Windows, for which
  one shouldn't need to know the 32-bit ARM program files directory
  location, since Git for Windows has never had 32-bit ARM builds.

  Relatedly, augmenting the search by checking the filesystem for a
  sibling directory named `Program Files (ARM)` should not be done,
  because this directory has no special meaning outside of ARM64
  Windows. It may therefore be left over from a previous
  installation or migration, or even created by a local user as in
  the CVE-2024-40644 scenario patched in GitoxideLabs#1456.

(Complexities related to deciding whether and in what order to
search `bin` subdirectories of `clangarm64`, `mingw64`, and
`mingw32` would go away if we looked for the shim rather than the
non-shim executable. This is because the path from `(git root)` to
the shim does not contain a directory component named after a build
target. That simplification would carry its own tradeoffs, and it
is unclear if it ought to be done; it is not done here.)
EliahKagan added a commit to EliahKagan/gitoxide that referenced this pull request Aug 18, 2025
`gix-path` looks for and runs an installed `git` executable, when
present, to discover information about how Git is set up. This
is used in several functions in `gix_path::env`, especially on
Windows, where if `git` is not found in a `PATH` search, then
common installation locations for Git for Windows are checked.

These locations on Windows are resolved based on information from
the current environment, since different systems have different
program files directories. This was implemented in GitoxideLabs#1456 (which
built on GitoxideLabs#1419). Although this was sufficient to find the most
common Git for Windows installation locations, it did not find
ARM64 builds of Git for Windows. Such builds place the non-shim
`git.exe` program in `(git root)\clangarm64\bin`, rather than in
`(git root)\mingw64\bin` or `(git root)\mingw32\bin`. At the time
of GitoxideLabs#1416 and GitoxideLabs#1419, no stable ARM64 builds of Git for Windows were
available. Since then, Git for Windows began releasing such builds.

This modifies the alternative locations examined if `git.exe` is
not found in a `PATH` search on Windows so that, where `(git root)`
is in a 64-bit program files directory, we check for a
`(git root)\clangarm64\bin` directory, in addition to checking for
a `(git root)\mingw64\bin` directory as was already done.

Although 64-bit and 32-bit program files directories are separate,
on ARM64 systems the 64-bit program files directory is used for
both ARM64 programs that the system can run directly and x86_64
programs that the system must run through emulation.

This checks both `clangarm64` and `mingw64`, where `mingw64` was
checked before. It does so in that order, because if both are
available, then we are probably on an ARM64 system, and the ARM64
build of Git for Windows should be preferred, both because it will
tend to perform better and because the user is likely to expect
that to be used. (An x86_64 build, especially if present directly
alongside an ARM64 build, may be left over from a previous version
of Git for Windows that didn't have ARM64 builds and that was only
incompletely uninstalled.)

This checks both, in that order, on all systems where we had
checked `mingw64` before, even on x86_64 systems. This is because:

- To limit production dependencies and code complexity, we have
  been examining only environment variables to ascertain which
  program files directories exist and whether they are 64-bit or
  32-bit program files directories. At least for now, this
  preserves that general approach.

- But determining whether the system is ARM64 or x86_64 is less
  straightforward to ascertain from environment variables, if we
  are to accommodate all reasonable edge cases.

The reason it's less straightforward is that, if our parent process
(or other ancestor) passes down a sanitized environment while still
attempting to let the program files directories be found, then:

- That process should make available all of the `ProgramFiles`,
  `ProgramW6432`, `ProgramFiles(x86)`, and `ProgramFiles(ARM)`
  variables that exist in its own environment.

  (This is because, on 64-bit Windows, the child `ProgramFiles` is
  populated from the parent `ProgramW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)`, depending on the architectures of the parent
  and child, if the parent passes down the relevant variable.)

- Even if the parent/ancestor is not designed with the WoW64 rules
  in mind, it will likely pass down at least `ProgramFiles`.

  This will then be used as the child `ProgramFiles`, if whichever
  of `ProgramFilesW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)` is relevant is not passed down by the parent.

- In contrast, the parent/ancestor may omit the variables
  `PROCESSOR_ARCHITECTURE` and `PROCESSOR_ARCHITEW6432`, which are
  not obviously needed for locating programs.

  On ARM64 Windows, the `PROCESSOR_ARCHITECTURE` variable will
  still be set automatically in that case. (Even if the parent
  process runs us with an explicitly empty environment, we will get
  this one variable, on ARM64 Windows.) However, in this case, as
  in most others, it will be set according to our process
  architecture (that we are built to target), rather than the
  system or machine architecture.

- Even if `PROCESSOR_ARCHITE*` variables are preserved or set
  correctly, there are common cases where they are not sufficient,
  such as when we are an x86_64 build but the system is ARM64 and
  Git for Windows is ARM64. `gix-path` is a library crate, and it
  is likely sometimes to be used in an x86_64 program on such a
  system.

  Also, if `gix-path` is used in an Arm64EC program, then even
  though its code may be ARM64 with the `arm64ec-pc-windows-msvc`
  Rust target, the program would still be treated as an x86_64
  program in its interactions with the system and other programs,
  including in how its environment variables' values are populated
  and how their values are affected by WoW64.

  (`gix-path` may also be x86_64 code where the program is Arm64EC.
  One way this happens is if `gix-path` is used in an x86_64 DLL,
  and an Arm64EC program loads the DLL. Using x86_64 DLLs from
  ARM64 code is one of the reasons a program may use an Arm64EC
  target. In this case, the Rust target will be for x86_64, not
  ARM64 or Arm64EC. So checking our own build target can't fully
  check if the program is Arm64EC.)

- Although the `PROCESSOR_IDENTIFIER` variable is more reliable if
  present--see actions/partner-runner-images#117 for an example of
  where this is more reliable than `PROCESSOR_ARCHITECTURE`--it is
  more complex to parse. More importantly, it may be omitted even
  if a parent/ancestor lets `PROCESSOR_ARCHITE*` through.

- It would sometimes work to look for `ProgramFiles(ARM)`. This
  environment variable, if present, gives the 32-bit ARM program
  files directory location. If set, then the Windows system could
  be treated as ARM64. However, a parent/ancestor process may omit
  this even when passing down program files related environment
  variables, including `ProgramFiles(x86)`. It may do so because
  the `ProgramFiles(ARM)` environment variable is less well known.
  It may also do so intentionally, if the parent process is only
  passing down variables needed to find Git for Windows, for which
  one shouldn't need to know the 32-bit ARM program files directory
  location, since Git for Windows has never had 32-bit ARM builds.

  Relatedly, augmenting the search by checking the filesystem for a
  sibling directory named `Program Files (ARM)` should not be done,
  because this directory has no special meaning outside of ARM64
  Windows. It may therefore be left over from a previous
  installation or migration, or even created by a local user as in
  the CVE-2024-40644 scenario patched in GitoxideLabs#1456.

(These complexities related to deciding whether and in what order
to search `bin` subdirectories of `clangarm64`, `mingw64`, and
`mingw32` would go away if we looked for the shim rather than the
non-shim executable. This is because the path from `(git root)` to
the shim does not contain a directory component named after a build
target. That simplification would carry its own tradeoffs, and it
is unclear if it ought to be done; it is not done here.)
EliahKagan added a commit to EliahKagan/gitoxide that referenced this pull request Aug 18, 2025
`gix-path` looks for and runs an installed `git` executable, when
present, to discover information about how Git is set up. This
is used in several functions in `gix_path::env`, especially on
Windows, where if `git.exe` is not found in a `PATH` search, then
common installation locations for Git for Windows are checked.

These locations on Windows are resolved based on information from
the current environment, since different systems have different
program files directories. This was implemented in GitoxideLabs#1456 (which
built on GitoxideLabs#1419). Although this was sufficient to find the most
common Git for Windows installation locations, it did not find
ARM64 builds of Git for Windows. Such builds place the non-shim
`git.exe` program in `(git root)\clangarm64\bin`, rather than in
`(git root)\mingw64\bin` or `(git root)\mingw32\bin`. At the time
of GitoxideLabs#1419 and GitoxideLabs#1456, no stable ARM64 builds of Git for Windows were
available. Since then, Git for Windows began releasing such builds.

This modifies the alternative locations examined if `git.exe` is
not found in a `PATH` search on Windows so that, where `(git root)`
is in a 64-bit program files directory, we check for a
`(git root)\clangarm64\bin` directory, in addition to checking for
a `(git root)\mingw64\bin` directory as was already done.

Although 64-bit and 32-bit program files directories are separate,
on ARM64 systems the 64-bit program files directory is used both
for ARM64 programs, which the system can run directly, and for
x86_64 programs, which the system must run through emulation.

This checks both `clangarm64` and `mingw64`, where `mingw64` was
checked before. It does so in that order, because if both are
available, then we are probably on an ARM64 system, and the ARM64
build of Git for Windows should be preferred, both because it will
tend to perform better and because the user is likely to expect
that to be used. (An x86_64 build, especially if present directly
alongside an ARM64 build, may be left over from a previous version
of Git for Windows that didn't have ARM64 builds and that was only
incompletely uninstalled.)

This checks both, in that order, on all systems where we had
checked `mingw64` before, even on x86_64 systems. This is because:

- To limit production dependencies and code complexity, we have
  been examining only environment variables to ascertain which
  program files directories exist and whether they are 64-bit or
  32-bit program files directories. At least for now, this
  preserves that general approach, continuing not to explicitly
  call Windows API functions or access the Windows registry, other
  than in tests.

- But determining from environment variables whether the system is
  ARM64 or x86_64 is less straightforward than determining the
  program files directory locations, in one major case as well as
  various edge cases.

The reason it's less straightforward is that, if our parent process
(or other ancestor) passes down a sanitized environment while still
attempting to let the program files directories be found, then:

- That process should make available all of the `ProgramFiles`,
  `ProgramW6432`, `ProgramFiles(x86)`, and `ProgramFiles(ARM)`
  variables that exist in its own environment.

  (This is because, on 64-bit Windows, the child `ProgramFiles` is
  populated from the parent `ProgramW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)`, depending on the architectures of the parent
  and child, if the parent passes down the relevant variable.)

- Even if the parent/ancestor is not designed with the WoW64 rules
  in mind, it will likely pass down at least `ProgramFiles`.

  This will then be used as the child `ProgramFiles`, if whichever
  of `ProgramFilesW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)` is relevant is not passed down by the parent.

- In contrast, the parent/ancestor may omit the variables
  `PROCESSOR_ARCHITECTURE` and `PROCESSOR_ARCHITEW6432`, which are
  not obviously needed for locating programs.

  In and of itself, this is not necessarily a problem, because on
  ARM64 Windows, at least one of the two will still be set
  automatically. (Even if the parent process runs us with an
  explicitly empty environment, we will get one of these, on ARM64
  Windows.)

  However, in this case, as in others, it will usually be set
  according to our process architecture, rather than the system
  architecture.

- Thus, even if `PROCESSOR_ARCHITE*` variables are preserved or set
  correctly, there are common cases where they are not sufficient.

  The main such case is when we are an x86_64 build, but the system
  is ARM64, and Git for Windows is ARM64. `gix-path` is a library
  crate, and it will sometimes to be used in an x86_64 program on
  such a system.

  Also, if `gix-path` is used in an Arm64EC program, then even
  though its code may be ARM64 with the `arm64ec-pc-windows-msvc`
  Rust target, the program would still be treated as an x86_64
  program in its interactions with the system and other programs,
  including in how its environment variables' values are populated
  and how their values are affected by WoW64.

  (`gix-path` may also be x86_64 code where the program is Arm64EC.
  One way this happens is if `gix-path` is used in an x86_64 DLL,
  and an Arm64EC program loads the DLL. Using x86_64 DLLs from
  ARM64 code is one of the reasons a program may target Arm64EC.
  In this case, the Rust target will be for x86_64, not ARM64 or
  Arm64EC. So checking our own Rust build target can't fully check
  if the program is Arm64EC.)

- Although the `PROCESSOR_IDENTIFIER` variable is more reliable if
  present--see actions/partner-runner-images#117 for an example of
  where this is more reliable than `PROCESSOR_ARCHITECTURE`--it is
  slightly more complex to parse. Much more importantly, unlike
  `PROCESSOR_ARCHITE*`, the `PROCESSOR_IDENTIFIER` variable is not
  set automatically in a child process whose parent/ancestor has
  removed it. Whether or not a parent/ancestor passes down
  `PROCESSOR_ARCHITE*` variables, it may still not choose to let
  `PROCESSOR_IDENTIFIER` through, if it sanitizes the environment.

- It would sometimes work to look for `ProgramFiles(ARM)`. This
  environment variable, if present, gives the 32-bit ARM program
  files directory location on an ARM64 Windows system. If set, then
  the Windows system could be treated as ARM64. However, a
  parent/ancestor process may omit this even when passing down
  program files related environment variables, including
  `ProgramFiles(x86)`. It may do so because the `ProgramFiles(ARM)`
  environment variable is less well known. Or it may omit it
  intentionally, if the parent process is only passing down
  variables needed to find Git for Windows, for which one shouldn't
  need to know the 32-bit ARM program files directory location,
  since Git for Windows has never had 32-bit ARM builds.

  Relatedly, augmenting the search by checking the filesystem for a
  sibling (or hard-coded) directory named `Program Files (ARM)`
  should not be done, because this directory has no special meaning
  outside of ARM66 Windows. It may therefore be left over from a
  previous installation or migration, or even created by a local
  user as in the CVE-2024-40644 scenario patched in GitoxideLabs#1456.

(These complexities all relate to deciding whether and in what
order to search `bin` subdirectories of `clangarm64`, `mingw64`,
and `mingw32`. They would go away if we looked for the shim rather
than the non-shim executable. This is because the path from
`(git root)` to the shim does not contain a directory component
named after a build target. That simplification would carry its own
tradeoffs, and it is unclear if it ought to be done; it is not done
here.)
EliahKagan added a commit to EliahKagan/gitoxide that referenced this pull request Aug 18, 2025
`gix-path` looks for and runs an installed `git` executable, when
present, to discover information about how Git is set up. This
is used in several functions in `gix_path::env`, especially on
Windows, where if `git.exe` is not found in a `PATH` search, then
common installation locations for Git for Windows are checked.

These locations on Windows are resolved based on information from
the current environment, since different systems have different
program files directories. This was implemented in GitoxideLabs#1456 (which
built on GitoxideLabs#1419). Although this was sufficient to find the most
common Git for Windows installation locations, it did not find
ARM64 builds of Git for Windows. Such builds place the non-shim
`git.exe` program in `(git root)\clangarm64\bin`, rather than in
`(git root)\mingw64\bin` or `(git root)\mingw32\bin`. At the time
of GitoxideLabs#1419 and GitoxideLabs#1456, no stable ARM64 builds of Git for Windows were
available. Since then, Git for Windows began releasing such builds.

This modifies the alternative locations examined if `git.exe` is
not found in a `PATH` search on Windows so that, where `(git root)`
is in a 64-bit program files directory, we check for a
`(git root)\clangarm64\bin` directory, in addition to checking for
a `(git root)\mingw64\bin` directory as was already done.

Although 64-bit and 32-bit program files directories are separate,
on ARM64 systems the 64-bit program files directory is used both
for ARM64 programs, which the system can run directly, and for
x86_64 programs, which the system must run through emulation.

This checks both `clangarm64` and `mingw64`, where `mingw64` was
checked before. It does so in that order, because if both are
available, then we are probably on an ARM64 system, and the ARM64
build of Git for Windows should be preferred, both because it will
tend to perform better and because the user is likely to expect
that to be used. (An x86_64 build, especially if present directly
alongside an ARM64 build, may be left over from a previous version
of Git for Windows that didn't have ARM64 builds and that was only
incompletely uninstalled.)

This checks both, in that order, on all systems where we had
checked `mingw64` before, even on x86_64 systems. This is because:

- To limit production dependencies and code complexity, we have
  been examining only environment variables to ascertain which
  program files directories exist and whether they are 64-bit or
  32-bit program files directories. At least for now, this
  preserves that general approach, continuing not to explicitly
  call Windows API functions or access the Windows registry, other
  than in tests.

- But determining from environment variables whether the system is
  ARM64 or x86_64 is less straightforward than determining the
  program files directory locations, in one major case as well as
  various edge cases.

The reason it's less straightforward is that, if our parent process
(or other ancestor) passes down a sanitized environment while still
attempting to let the program files directories be found, then:

- That process should make available all of the `ProgramFiles`,
  `ProgramW6432`, `ProgramFiles(x86)`, and `ProgramFiles(ARM)`
  variables that exist in its own environment.

  (This is because, on 64-bit Windows, the child `ProgramFiles` is
  populated from the parent `ProgramW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)`, depending on the architectures of the parent
  and child, if the parent passes down the relevant variable.)

- Even if the parent/ancestor is not designed with the WoW64 rules
  in mind, it will likely pass down at least `ProgramFiles`.

  This will then be used as the child `ProgramFiles`, if whichever
  of `ProgramFilesW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)` is relevant is not passed down by the parent.

- In contrast, the parent/ancestor may omit the variables
  `PROCESSOR_ARCHITECTURE` and `PROCESSOR_ARCHITEW6432`, which are
  not obviously needed for locating programs.

  In and of itself, this is not necessarily a problem, because on
  ARM64 Windows, at least one of the two will still be set
  automatically. (Even if the parent process runs us with an
  explicitly empty environment, we will get one of these, on ARM64
  Windows.)

  However, in this case, as in others, it will usually be set
  according to our process architecture, rather than the system
  architecture.

- Thus, even if `PROCESSOR_ARCHITE*` variables are preserved or set
  correctly, there are common cases where they are not sufficient.

  The main such case is when we are an x86_64 build, but the system
  is ARM64, and Git for Windows is ARM64. `gix-path` is a library
  crate, and it will sometimes to be used in an x86_64 program on
  such a system.

  Also, if `gix-path` is used in an Arm64EC program, then even
  though its code may be ARM64 with the `arm64ec-pc-windows-msvc`
  Rust target, the program would still be treated as an x86_64
  program in its interactions with the system and other programs,
  including in how its environment variables' values are populated
  and how their values are affected by WoW64.

  (`gix-path` may also be x86_64 code where the program is Arm64EC.
  One way this happens is if `gix-path` is used in an x86_64 DLL,
  and an Arm64EC program loads the DLL. Using x86_64 DLLs from
  ARM64 code is one of the reasons a program may target Arm64EC.
  In this case, the Rust target will be for x86_64, not ARM64 or
  Arm64EC. So checking our own Rust build target can't fully check
  if the program is Arm64EC.)

- Although the `PROCESSOR_IDENTIFIER` variable is more reliable if
  present--see actions/partner-runner-images#117 for an example of
  where this is more reliable than `PROCESSOR_ARCHITECTURE`--it is
  slightly more complex to parse. Much more importantly, unlike
  `PROCESSOR_ARCHITE*`, the `PROCESSOR_IDENTIFIER` variable is not
  set automatically in a child process whose parent/ancestor has
  removed it. Whether or not a parent/ancestor passes down
  `PROCESSOR_ARCHITE*` variables, it may still not choose to let
  `PROCESSOR_IDENTIFIER` through, if it sanitizes the environment.

- It would sometimes work to look for `ProgramFiles(ARM)`. This
  environment variable, if present, gives the 32-bit ARM program
  files directory location on an ARM64 Windows system. If set, then
  the Windows system could be treated as ARM64. However, a
  parent/ancestor process may omit this even when passing down
  program files related environment variables, including
  `ProgramFiles(x86)`. It may do so because the `ProgramFiles(ARM)`
  environment variable is less well known. Or it may omit it
  intentionally, if the parent process is only passing down
  variables needed to find Git for Windows, for which one shouldn't
  need to know the 32-bit ARM program files directory location,
  since Git for Windows has never had 32-bit ARM builds.

  Relatedly, augmenting the search by checking the filesystem for a
  sibling (or hard-coded) directory named `Program Files (ARM)`
  should not be done, because this directory has no special meaning
  outside of ARM64 Windows. It may therefore be left over from a
  previous installation or migration, or even created by a local
  user as in the CVE-2024-40644 scenario patched in GitoxideLabs#1456.

(These complexities all relate to deciding whether and in what
order to search `bin` subdirectories of `clangarm64`, `mingw64`,
and `mingw32`. They would go away if we looked for the shim rather
than the non-shim executable. This is because the path from
`(git root)` to the shim does not contain a directory component
named after a build target. That simplification would carry its own
tradeoffs, and it is unclear if it ought to be done; it is not done
here.)
EliahKagan added a commit to EliahKagan/gitoxide that referenced this pull request Aug 18, 2025
`gix-path` looks for and runs an installed `git` executable, when
present, to discover information about how Git is set up. This
is used in several functions in `gix_path::env`, especially on
Windows, where if `git.exe` is not found in a `PATH` search, then
common installation locations for Git for Windows are checked.

These locations on Windows are resolved based on information from
the current environment, since different systems have different
program files directories. This was implemented in GitoxideLabs#1456 (which
built on GitoxideLabs#1419). Although this was sufficient to find the most
common Git for Windows installation locations, it did not find
ARM64 builds of Git for Windows. Such builds place the non-shim
`git.exe` program in `(git root)\clangarm64\bin`, rather than in
`(git root)\mingw64\bin` or `(git root)\mingw32\bin`. At the time
of GitoxideLabs#1419 and GitoxideLabs#1456, no stable ARM64 builds of Git for Windows were
available. Since then, Git for Windows began releasing such builds.

This modifies the alternative locations examined if `git.exe` is
not found in a `PATH` search on Windows so that, where `(git root)`
is in a 64-bit program files directory, we check for a
`(git root)\clangarm64\bin` directory, in addition to checking for
a `(git root)\mingw64\bin` directory as was already done.

Although 64-bit and 32-bit program files directories are separate,
on ARM64 systems the 64-bit program files directory is used both
for ARM64 programs, which the system can run directly, and for
x86_64 programs, which the system must run through emulation.

This checks both `clangarm64` and `mingw64`, where `mingw64` was
checked before. It does so in that order, because if both are
available, then we are probably on an ARM64 system, and the ARM64
build of Git for Windows should be preferred, both because it will
tend to perform better and because the user is likely to expect
that to be used. (An x86_64 build, especially if present directly
alongside an ARM64 build, may be left over from a previous version
of Git for Windows that didn't have ARM64 builds and that was only
incompletely uninstalled.)

This checks both, in that order, on all systems where we had
checked `mingw64` before, even on x86_64 systems. This is because:

- To limit production dependencies and code complexity, we have
  been examining only environment variables (and information
  available at build time) to ascertain which program files
  directories exist and whether they are 64-bit or 32-bit program
  files directories. At least for now, this preserves that general
  approach, continuing not to explicitly call Windows API functions
  or access the Windows registry, other than in tests.

- But determining from environment variables whether the system is
  ARM64 or x86_64 is less straightforward than determining the
  program files directory locations, in one major case as well as
  various edge cases.

The reason it's less straightforward is that, if our parent process
(or other ancestor) passes down a sanitized environment while still
attempting to let the program files directories be found, then:

- That process should make available all of the `ProgramFiles`,
  `ProgramW6432`, `ProgramFiles(x86)`, and `ProgramFiles(ARM)`
  variables that exist in its own environment.

  (This is because, on 64-bit Windows, the child `ProgramFiles` is
  populated from the parent `ProgramW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)`, depending on the architectures of the parent
  and child, if the parent passes down the relevant variable.)

- Even if the parent/ancestor is not designed with the WoW64 rules
  in mind, it will likely pass down at least `ProgramFiles`.

  This will then be used as the child `ProgramFiles`, if whichever
  of `ProgramFilesW6432`, `ProgramFiles(x86)`, or
  `ProgramFiles(ARM)` is relevant is not passed down by the parent.

- In contrast, the parent/ancestor may omit the variables
  `PROCESSOR_ARCHITECTURE` and `PROCESSOR_ARCHITEW6432`, which are
  not obviously needed for locating programs.

  In and of itself, this is not necessarily a problem, because on
  ARM64 Windows, at least one of the two will still be set
  automatically. (Even if the parent process runs us with an
  explicitly empty environment, we will get one of these, on ARM64
  Windows.)

  However, in this case, as in others, it will generally be set
  according to our process architecture, rather than the system
  architecture.

- Thus, even if `PROCESSOR_ARCHITE*` variables are preserved or set
  correctly, there are common cases where they are not sufficient.

  The main such case is when we are an x86_64 build, but the system
  is ARM64, and Git for Windows is ARM64. `gix-path` is a library
  crate, and it will sometimes to be used in an x86_64 program on
  such a system.

  Also, if `gix-path` is used in an Arm64EC program, then even
  though its code may be ARM64 with the `arm64ec-pc-windows-msvc`
  Rust target, the program would still be treated as an x86_64
  program in its interactions with the system and other programs,
  including in how its environment variables' values are populated
  and how their values are affected by WoW64.

  (`gix-path` may also be x86_64 code where the program is Arm64EC.
  One way this happens is if `gix-path` is used in an x86_64 DLL,
  and an Arm64EC program loads the DLL. Using x86_64 DLLs from
  ARM64 code is one of the reasons a program may target Arm64EC.
  In this case, the Rust target will be for x86_64, not ARM64 or
  Arm64EC. So checking our own Rust build target can't fully check
  if the program is Arm64EC.)

- Although the `PROCESSOR_IDENTIFIER` variable is more reliable if
  present--see actions/partner-runner-images#117 for an example of
  where this is more reliable than `PROCESSOR_ARCHITECTURE`--it is
  slightly more complex to parse. Much more importantly, unlike
  `PROCESSOR_ARCHITE*`, the `PROCESSOR_IDENTIFIER` variable is not
  set automatically in a child process whose parent/ancestor has
  removed it. Whether or not a parent/ancestor passes down
  `PROCESSOR_ARCHITE*` variables, it may still not choose to let
  `PROCESSOR_IDENTIFIER` through, if it sanitizes the environment.

- It would sometimes work to look for `ProgramFiles(ARM)`. This
  environment variable, if present, gives the 32-bit ARM program
  files directory location on an ARM64 Windows system. If set, then
  the Windows system could be treated as ARM64. However, a
  parent/ancestor process may omit this even when passing down
  program files related environment variables, including
  `ProgramFiles(x86)`. It may do so because the `ProgramFiles(ARM)`
  environment variable is less well known. Or it may omit it
  intentionally, if the parent process is only passing down
  variables needed to find Git for Windows, for which one shouldn't
  need to know the 32-bit ARM program files directory location,
  since Git for Windows has never had 32-bit ARM builds.

  Relatedly, augmenting the search by checking the filesystem for a
  sibling (or hard-coded) directory named `Program Files (ARM)`
  should not be done, because this directory has no special meaning
  outside of ARM64 Windows. It may therefore be left over from a
  previous installation or migration, or even created by a local
  user as in the CVE-2024-40644 scenario patched in GitoxideLabs#1456.

(These complexities all relate to deciding whether and in what
order to search `bin` subdirectories of `clangarm64`, `mingw64`,
and `mingw32`. They would go away if we looked for the shim rather
than the non-shim executable. This is because the path from
`(git root)` to the shim does not contain a directory component
named after a build target. That simplification would carry its own
tradeoffs, and it is unclear if it ought to be done; it is not done
here.)
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.

2 participants