Skip to content

The shim steps past a PATH shim of somebody else's that hands the command back to it, rather than looping through it - #193

Merged
HackingGate merged 1 commit into
mainfrom
the-shim-steps-past-a-path-shim-that-would-loop-through-it
Sep 20, 2026
Merged

HackingGate merged 1 commit into
mainfrom
the-shim-steps-past-a-path-shim-that-would-loop-through-it

Conversation

@HackingGate

Copy link
Copy Markdown
Owner

The shim finds the real command by walking PATH past its own file and
past any link that lands on another uphold. A third kind of file is
neither: a shim that belongs to somebody else and resolves the command
the same way, by walking PATH. mise installs one named for every tool
it has heard of, a link to a binary called mise, and it walks PATH
skipping only its own directory. With that directory ahead of the
shim's, the walk returned it as the real gh and exec'd it, mise found
the uphold link first and exec'd that, and every arrival was a fresh
invocation: nothing in the loop was an uphold probe, so
UPHOLD_SHIM_INNER was never set and its limit never tripped. Each
round ran the checks again, and the alias probe each round made spawned
the same loop under itself, which is where the twenty-nine thousand
gh alias list processes came from.

The exec of the real command is now marked as well. UPHOLD_SHIM_HANDED
carries the shim's own pid, the command's name, and the file identities
the walk has already handed that command to. The pid is the key because
it is what the loop repeats and a genuine nested use cannot: an exec
keeps the pid, so the shim, the foreign shim and the shim again are one
process, while a hook that the real command runs, which runs the
command again, is a fork and arrives under a pid of its own and is
judged like any other invocation. The name is part of the key because a
wrapper exec'd as gh that execs git keeps the pid too, and that
git has not been checked. An invocation that carries its own pid
under its own name is the hand-off having come back around: it is
handed straight through rather than checked twice, and the walk it
makes steps past every identity the marker lists, printing one line on
stderr with the path it stepped past so the extra hop is not read as a
slow gate. Nothing is named, so the next shim manager needs no entry.

The alias probe ran the resolved path directly with no marker, and the
resolved path can be the foreign shim, which resolves the name again.
It is marked UPHOLD_SHIM_INNER through inner_tool like every other
probe, which now takes a path as well as a name.

Measured against the installed mise shim with PATH=<link dir>: ~/.local/share/mise/shims:/usr/bin: timeout 20 gh --version was
killed at exit 124 under the released binary, and returns the version
in about fifty milliseconds under this one, with the notice naming
~/.local/share/mise/shims/gh. mise passes the environment and argv
through intact and execs rather than forks, so the marker and the pid
both survive the hop.

Two hand-off tests reproduce the shape with a PATH-walking stub ahead of
the real command and the uphold link ahead of both, under the process
group and timeout the suite already uses: one shows the real command
runs once and the notice is printed, where the released binary ran
until timeout killed it; the other shows a nested invocation from the
real command is refused by the rule rather than handed through as the
loop's second arrival.

Closes #188

…mand back to it, rather than looping through it

The shim finds the real command by walking PATH past its own file and
past any link that lands on another uphold. A third kind of file is
neither: a shim that belongs to somebody else and resolves the command
the same way, by walking PATH. mise installs one named for every tool
it has heard of, a link to a binary called `mise`, and it walks PATH
skipping only its own directory. With that directory ahead of the
shim's, the walk returned it as the real `gh` and exec'd it, mise found
the uphold link first and exec'd that, and every arrival was a fresh
invocation: nothing in the loop was an uphold probe, so
`UPHOLD_SHIM_INNER` was never set and its limit never tripped. Each
round ran the checks again, and the alias probe each round made spawned
the same loop under itself, which is where the twenty-nine thousand
`gh alias list` processes came from.

The exec of the real command is now marked as well. `UPHOLD_SHIM_HANDED`
carries the shim's own pid, the command's name, and the file identities
the walk has already handed that command to. The pid is the key because
it is what the loop repeats and a genuine nested use cannot: an exec
keeps the pid, so the shim, the foreign shim and the shim again are one
process, while a hook that the real command runs, which runs the
command again, is a fork and arrives under a pid of its own and is
judged like any other invocation. The name is part of the key because a
wrapper exec'd as `gh` that execs `git` keeps the pid too, and that
`git` has not been checked. An invocation that carries its own pid
under its own name is the hand-off having come back around: it is
handed straight through rather than checked twice, and the walk it
makes steps past every identity the marker lists, printing one line on
stderr with the path it stepped past so the extra hop is not read as a
slow gate. Nothing is named, so the next shim manager needs no entry.

The alias probe ran the resolved path directly with no marker, and the
resolved path can be the foreign shim, which resolves the name again.
It is marked `UPHOLD_SHIM_INNER` through `inner_tool` like every other
probe, which now takes a path as well as a name.

Measured against the installed mise shim with `PATH=<link dir>:
~/.local/share/mise/shims:/usr/bin`: `timeout 20 gh --version` was
killed at exit 124 under the released binary, and returns the version
in about fifty milliseconds under this one, with the notice naming
`~/.local/share/mise/shims/gh`. mise passes the environment and argv
through intact and execs rather than forks, so the marker and the pid
both survive the hop.

Two hand-off tests reproduce the shape with a PATH-walking stub ahead of
the real command and the uphold link ahead of both, under the process
group and timeout the suite already uses: one shows the real command
runs once and the notice is printed, where the released binary ran
until `timeout` killed it; the other shows a nested invocation from the
real command is refused by the rule rather than handed through as the
loop's second arrival.

Closes #188
@coderabbitai

coderabbitai Bot commented Sep 20, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 48 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 6bc567ee-e7b6-4014-a481-b57a497a0975

📥 Commits

Reviewing files that changed from the base of the PR and between 2a543a5 and f4587df.

📒 Files selected for processing (4)
  • docs/REFERENCE.md
  • src/main.rs
  • src/shim.rs
  • tests/shim_handoff_cli.rs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.90698% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.61%. Comparing base (2a543a5) to head (f4587df).

Files with missing lines Patch % Lines
src/shim.rs 77.90% 19 Missing ⚠️

❌ Your patch status has failed because the patch coverage (77.90%) is below the target coverage (100.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #193      +/-   ##
==========================================
- Coverage   93.68%   93.61%   -0.08%     
==========================================
  Files          44       44              
  Lines       16934    17013      +79     
==========================================
+ Hits        15865    15927      +62     
- Misses       1069     1086      +17     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@HackingGate
HackingGate merged commit 310ebab into main Sep 20, 2026
11 checks passed
@HackingGate
HackingGate deleted the the-shim-steps-past-a-path-shim-that-would-loop-through-it branch September 20, 2026 06:18
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.

The shim execs a third-party PATH shim as the real command and loops through it

2 participants