github GitoxideLabs/gitoxide gix-path-v0.10.21
gix-path v0.10.21

latest releases: gix-v0.74.1, gix-status-v0.21.1, gix-protocol-v0.52.1...
one day ago

New Features

  • Extend ALTERNATIVE_LOCATIONS for per-user installations
    When git is not found in a PATH search on Windows, common
    locations where Git for Windows is often installed are checked.
    But only systemwide installations had been checked before.

    This extends the locations examined to include the current user's
    own program files directory. This directory, if present, is
    expected to be the Programs subdirectory of the user's local
    application data directory, typically:

    C:\Users\<user>\AppData\Local\Programs
    

    When Git for Windows is installed for a user rather than
    systemwide, it is typically installed a Git subdirectory of that
    Programs directory (much as it is typically installed in a Git
    subdirectory of a directory such as C:\Program Files when
    installed systemwide). This looks for a suitable Git for Windows
    directory tree at such a location.

    It is possible for Git for Windows installations to be present both
    in the current user's own program files directory and systemwide.
    If that happens, such that both kinds of installations are able to
    be found, then we choose the per-user installation.

    This is based on the idea that an individual user may choose to
    install a newer or otherwise different version of Git for Windows,
    or that a developer may even test out a custom build by manually
    placing it in that directory. Because, in either case, the
    architecture of the Git for Windows executable may differ from what
    is currently installed systemwide or even from what is typically
    preferred, we will attempt to use a per-user build of any of the
    architectures Git for Windows has published, before then using the
    systemwide installations as done before.

    Although the user program files directory is under the local rather
    than roaming application data directory, and is thus not shared
    across machines on a domain, it is possible that some techniques of
    backing up and restoring data may restore a per-user installation
    of Git for Windows that is for a different machine architecture
    that cannot run, or that the user would not want to be used. In
    that case, this enhancement may actually break something that was
    working before.

    That seems fairly unlikely to occur, and it can be worked around by
    making a git command available in a PATH search. Nonetheless,
    if this is found to happen in practice, then further refinement of
    the ALTERNATIVE_LOCATIONS enumeration order may be warranted.

  • Extend EXEPATH optimization to support ARM64 Windows
    On Windows, the gix_path::env::system_prefix() function has two
    strategies. The first is an optimization that doesn't always work,
    where the EXEPATH environment variable is consulted. In Git Bash,
    this variable is usually available, pointing somewhere into the
    Git for Windows installation. Most commonly, but in practice not
    universally, it points to the top-level directory of the Git for
    Windows installation. system_prefix() on Windows looks for a
    subdirectory of this directory that is named for one of the MSYS2
    environment https://www.msys2.org/docs/environments prefixes, of
    those Git for Windows has builds for. (Otherwise, it falls back to
    traversing to such a location upward from what git --exec-path
    reveals, which is often slower as git must sometimes be run.)

    However, this EXEPATH optimization had only checked for the
    mingw64 and mingw32 environment prefixes. This was ideal
    before ARM64 builds of Git for Windows were released. But since
    then, it is useful to allow the clangarm64 environment prefix
    as well, to allow the EXEPATH optimization to work on ARM64
    builds of Git for Windows. This makes that change.

Bug Fixes

  • Don't use EXEPATH unless it is absolute
    The first of two strategies used by the system_prefix() function
    on Windows is an optimization that looks for clangarm64,
    mingw64, or mingw32 subdirectories of a directory whose path is
    given by the value of the EXEPATH environment variable. If
    EXEPATH is absent, can't be interpreted as a path, or can be
    interpreted as a path but does not point to a directory that can be
    verified to contain such a subdirectory, then this optimization was
    already being skipped and the more reliable but sometimes slower
    git --exec-path-based method used.

    However, when EXEPATH contains a relative path, including in the
    case where it is an empty string (which could be set by accident or
    if it is being used with some meaning other than what we and Git
    for Windows recognize), then it was still used, even though that
    would not usually be correct and would sometimes not be safe.

    Although an attacker is not expected to be able to control the
    value of EXEPATH (nor most environment variables), a value such
    as an empty string would cause clangarm64, mingw64, and
    mingw32 subdirectories of the current directory to be used.

    A user might intentionally set EXEPATH to a relative path to
    leverage the EXEPATH optimization of system_prefix() with that
    path. But EXEPATH is a short variable name with many plausible
    meanings that is not named with a distinctive prefix such GIT_ or
    GIX_. So it would not be a good tradeoff to continue recognizing
    it for system_prefix() anytime it is non-absoliute.

    Thus, as a stability fix and possible security enhancement, this
    adds a check that the value of EXEPATH is an absolute path before
    proceeding with the EXEPATH optimization.

  • Skip EXEPATH optimization if it finds no best path
    The EXEPATH optimization for gix_path::env::system_prefix() on
    Windows previously tried https://www.msys2.org/docs/environments
    prefixes used by Git for Windows, returning a successful result on
    the first subdirectory found. However, if EXEPATH points to a
    directory that contains more than one such subdirectory, then the
    subdirectory returned would not necessarily be the correct one.

    Getting more than one subdirectory is unlikely. It is expected that
    at most one of clangarm64, mingw64, and mingw32 will be
    present. However, there are at least three ways it could happen:

    • EXEPATH has the intended meaning as the root of a Git for
      Windows installation, but the directory it refers to contains
      multiple directories left over from a previous installation. A
      corresponding scenario applies to ALTERNATIVE_LOCATIONS, but
      it is resolved by checking all the directories in a reasonable
      order. Here, we are not checking the contents of the directories,
      so no matter what order we look for them in, picking one when
      there are others risks picking the wrong one.

    • EXEPATH has the intended meaning as the root of a Git for
      Windows installation, but it is a custom installation produced
      from local build or custom distribution rather than an official
      build, and it contains multiple such directories because it
      carries binaries built against more than one of the targets (even
      if only one of them has git itself). A corresponding scenario
      is fairly unlikely for ALTERNATIVE_LOCATIONS, because a custom
      MSYS2 tree, even if created using the Git for Windows SDK, would
      probably not be installed in one of the common Git for Windows
      installation locations, without considering the effects of doing
      so. In contrast, EXEPATH will often be set in a Git Bash
      environment even in a highly customized Git for Windows tree.

    • EXEPATH has a different meaning from what is intended. For
      example, the user might set it to the root of an ordinary MSYS2
      installation. (Note that it is also likely to have various other
      meanings even more different from these, but those won't likely
      cause the EXEPATH optimization to be used when it shouldn't,
      because most possible meanings of EXEPATH won't involve a
      subdirectory of any of the names we look for.

    Instead of using the first existing subdirectory we fine, this
    checks for all of them and requires that exactly one exist. If more
    than one exist, then that is now treated the same as if none exist,
    falling back to the git --exec-path-based strategy, which is
    sometimes slower but more robust.

  • Extend ALTERNATIVE_LOCATIONS for ARM64 Windows
    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 #1456 (which
    built on #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 #1419 and #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 #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.)

  • Use nul instead of NUL on Windows
    NULL_DEVICE is /dev/null except on Windows, where there are
    several possible choices. Previously we were using NUL because
    the more modern full path \\.\NUL is not supported by git.

    However, git also rejects NUL, when capitalized, on some
    Windows systems. This can be observed on Windows 11 ARM64 builds.
    In contrast, the lower-case nul, which Windows itself treats the
    same as NUL, is always accepted by Git for Windows.

    Although it's not readily apparent why Git for Windows accepts
    NUL on some Windows operating systems and/or platforms and not
    others, the preferential treatment of nul (not extending to
    NUL) can be seen in a few places in the Git for Windows source
    code, including in mingw_access (strcmp is case-sensitive):

Commit Statistics

  • 39 commits contributed to the release over the course of 79 calendar days.
  • 79 days passed between releases.
  • 6 commits were understood as conventional.
  • 0 issues like '(#ID)' were seen in commit messages

Commit Details

view details
  • Uncategorized
    • Merge pull request #2217 from GitoxideLabs/copilot/update-msrv-to-rust-1-82 (4da2927)
    • Update MSRV to 1.82 and replace once_cell with std equivalents (6cc8464)
    • Merge pull request #2202 from GitoxideLabs/dependabot/cargo/cargo-4a7155215a (9365cc3)
    • Bump the cargo group across 1 directory with 64 updates (838ff95)
    • Merge pull request #2134 from EliahKagan/user-program-files (2fffc50)
    • Extend ALTERNATIVE_LOCATIONS for per-user installations (7e77d40)
    • Migrate remaining known-folders usage to windows (082e22e)
    • Clarify safety at and around SHGetKnownFolderPath call (998fb48)
    • Get UserProgramFiles with KF_FLAG_DONT_VERIFY for test (1c7a34e)
    • Update alternative location unit tests for user program files (c9ff0ac)
    • Fix user program files alternative locations expectations (cab4c85)
    • Update alternative locations integration tests for user program files (9e71d55)
    • Merge pull request #2133 from EliahKagan/program-files-next (e365244)
    • Add a "SAFETY:" comment in PlatformBitness::current() (1f3edb5)
    • Move the vacuous case outside the "ordinary" group (eec407f)
    • Rename cases to clarify "ordinary" and "strange" (edc0f3c)
    • Divide up locations_under_program_files_* assertions (35beea1)
    • Clarify which global program files paths are used and why (fd000f5)
    • Merge pull request #2115 from EliahKagan/run-ci/arm-windows (c2c8c2f)
    • Don't use EXEPATH unless it is absolute (9000a84)
    • Test that relative EXEPATH doesn't trigger the optimization (f4bb773)
    • Factor out shared code to a helper (84f9672)
    • Test that empty EXEPATH doesn't trigger the optimization (24c11ac)
    • Skip EXEPATH optimization if it finds no best path (5ac8cff)
    • Extend EXEPATH optimization to support ARM64 Windows (2d2c11b)
    • Refactor slices in test cases for readability (d5f4c9f)
    • Test the EXEPATH optimization of system_prefix() (2b0639b)
    • Extract system_prefix() Windows strategies to helpers (3b304fa)
    • Update outdated expect() message in test helper (571ca6e)
    • Rename PlatformArchitecture helper PlatformBitness (5bf265b)
    • Further refactor rules loop in locations_under_program_files (2c6dcb2)
    • Clarify rules loop in locations_under_program_files (d06b89d)
    • Extend ALTERNATIVE_LOCATIONS for ARM64 Windows (1fa24cd)
    • Tweak formatting to fix lint (4662233)
    • Update alternative locations unit tests for ARM64 (ff3df53)
    • Update alternative locations integration tests for ARM64 (e9770a7)
    • Copyedit gix_path::env::git comments (8d2c262)
    • Use nul instead of NUL on Windows (b24783a)
    • Merge pull request #2100 from GitoxideLabs/release (202bc6d)

Don't miss a new gitoxide release

NewReleases is sending notifications on new releases.