Skip to content

Fix cargo home with HOME unset (#1135) - #1779

Merged
Mikola Lysenko (mikolalysenko) merged 5 commits into
mainfrom
agent/fix-cargo-home-passwd-fallback
Oct 28, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 5 commits into
mainfrom
agent/fix-cargo-home-passwd-fallback

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #1135

Summary

With HOME and CARGO_HOME both unset (env -i, systemd units, stripped
CI/container entrypoints), cargo still builds from the account's passwd home
(e.g. /root/.cargo). socket-patch found no cargo home at all, so
scan --mode agent reported the crate lockfile-only and exited 0 while cargo
compiled the unpatched copy, and scan -g -e cargo found no packages. It now
resolves the cargo home the way cargo does and patches the copy cargo builds.

Root cause

Four places resolved the cargo home on their own, each as $CARGO_HOME, else
utils::fs::home_dir()/.cargo: the cargo crawler, the [patch] config chain
(vendor/cargo_config.rs), the build-cache cleaner
(utils/cargo_build_cache.rs) and the self-update cargo-install check
(update/channel.rs). home_dir() reads only HOME/USERPROFILE. Cargo's
home crate reads HOME on Unix (USERPROFILE on Windows) and, on Unix,
falls back to the passwd entry when HOME is unset.

Fix

  • New utils::cargo_home::cargo_home(): $CARGO_HOME when set and non-blank,
    else <home>/.cargo, where <home> is HOME (USERPROFILE on Windows) or,
    with that variable unset or empty on Unix, the passwd entry (as std's
    home_dir does). A relative HOME still means no home (unchanged), so we never patch a shared
    passwd-home cache cargo isn't using.
  • All four readers go through it.
  • passwd_home() moved from crawlers::gradle_cache to utils::fs (re-exported
    from gradle_cache) and is looked up once per process.
  • docs/ecosystems.md (Cargo: shared registry cache) states the rule.

Test evidence

  • Red on main: cargo test -p socket-patch-core --lib cargo_home_without_home_env_uses_passwd_home
    failed before the fix (CargoCrawler::cargo_home() returned None), passes after.
  • New resolver unit tests in utils/cargo_home.rs (CARGO_HOME wins, blank is unset,
    HOME wins over passwd, unset HOME falls back to passwd (Agent-mode cargo with HOME unset finds no registry cache, reports the crate as lockfile-only and exits 0, while cargo builds the unpatched copy from the passwd home's ~/.cargo #1135), empty HOME falls back to passwd, relative HOME is no home, USERPROFILE ignored on Unix, process-env end to end).
  • Binary repro (the issue's global variant): env -u HOME -u CARGO_HOME socket-patch scan -g -e cargo --json
    now reports scannedPackages: 238 from /root/.cargo (the issue reported 0 on main).
  • cargo fmt --all -- --check: clean. cargo clippy --workspace --all-features -- -D warnings: clean.
  • cargo test -p socket-patch-core --all-features --lib: 6809 passed, 4 failed. The 4
    (copy_tree::relax_loop_must_not_traverse_symlinked_root,
    vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,
    pypi_poetry::wire_write_failure_maps_error_and_leaves_lock_untouched,
    pypi_requirements::wire_failure_rolls_back_already_written_files) depend on
    permission denials and fail only because this container runs as root. They
    don't touch this change and pass in CI.
  • cargo test -p socket-patch-core --all-features --test '*': 655 passed.
  • cargo test -p socket-patch-cli --all-features --bins --lib: 935 passed.
  • CLI cargo suites: e2e_safety_cargo_build -- --ignored 10/10, e2e_vendor_cargo_build 40/40,
    e2e_redirect_cargo_build 23/23, e2e_redirect_cargo_shapes 34/34, mode_migration_cargo 30/30.
  • The full cargo test --workspace build ran out of local disk while linking every CLI
    test binary, so the cargo-related CLI suites above were run one at a time instead.
    CI runs the full matrix.
Issue Test
#1135 (agent scan, global scan, config chain) cargo_crawler::tests::cargo_home_without_home_env_uses_passwd_home, utils::cargo_home::tests::*

/code-review high

Fixed: Unix read USERPROFILE (and Windows read HOME), unlike cargo; a relative
HOME fell through to the passwd home; update/channel.rs was a fifth resolver;
the passwd lookup repeated on every call; utils depended on the Gradle crawler;
the test env guard wasn't panic-safe.
Not changed:

  • A whitespace-only CARGO_HOME is still treated as unset. That keeps the existing
    behavior and the repo's env_non_empty convention.
  • The crawler-level test overlaps the resolver test. It stays as the regression test
    showing the crawler itself is fixed.
  • Other per-user caches (Python, Deno, NuGet) with the same HOME-unset gap are out
    of scope here. Each tool has its own home rule, so they need separate fixes.
  • On Windows with USERPROFILE unset, cargo uses the known-folder API. We don't
    replicate that. It is untested in the issue too.

https://claude.ai/code/session_011f9aeNZ3vKbaLRi7eRFxTp


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
When HOME and CARGO_HOME are both unset (env -i, systemd units,
stripped CI or container entrypoints), cargo still builds from the
account's passwd home, e.g. /root/.cargo. socket-patch looked only at
HOME/USERPROFILE, so an agent scan probed no registry, reported the
crate as lockfile-only and exited 0 while cargo compiled the unpatched
copy. Global cargo scans found nothing, and the [patch] config chain
skipped $CARGO_HOME/config.toml.

Resolve the cargo home in one place the way cargo does: CARGO_HOME,
then HOME/USERPROFILE, then the passwd entry on Unix. The crawler,
the cargo config chain and the build-cache cleaner all use it.

Fixes #1135

Assisted-by: Claude Code:claude-opus-5-5
The two #1135 regression tests now use one helper to clear HOME,
USERPROFILE and CARGO_HOME. docs/ecosystems.md says how the cargo
cache is found when CARGO_HOME is unset.

Assisted-by: Claude Code:claude-opus-5-5
Review follow-ups for the cargo home fix:

- Read only HOME on Unix and only USERPROFILE on Windows, as cargo
  does, so a stray USERPROFILE on Linux can't redirect the cache.
- Use the passwd entry only when HOME is unset. A set but empty or
  relative HOME still means no home, so we never patch the shared
  passwd-home cache that cargo isn't using.
- Look the passwd entry up once per process, from utils::fs, so cargo
  no longer depends on the Gradle crawler.
- Self-update's cargo-install check uses the same rule.
- The test env guard now restores HOME even if a test panics.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 28, 2026 12:23
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor

cursor Bot commented Oct 28, 2026

Copy link
Copy Markdown

Bugbot is paused — your team has an unpaid invoice

Bugbot is paused because your team has an unpaid invoice.

Pay it from the billing page in the Cursor dashboard and Bugbot resumes automatically.

(requestId: 2df4790e-1391-452f-afb5-bd06211571ae)

@cursor

cursor Bot commented Oct 28, 2026

Copy link
Copy Markdown

Bugbot is paused — your team has an unpaid invoice

Bugbot is paused because your team has an unpaid invoice.

Pay it from the billing page in the Cursor dashboard and Bugbot resumes automatically.

(requestId: 99ed0440-467b-4417-ab1a-541a6e93f3d7)

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] CI is green on be7e923 (31 success, 50 skipped by path filters). Bugbot did not review: cursor[bot] replied that Bugbot is paused because the team has an unpaid invoice. I'll re-trigger @bugbot review once billing is restored.


Generated by Claude Code

Cargo's home lookup (std::env::home_dir) skips an empty HOME and
falls back to the passwd entry, the same as an unset HOME. We treated
HOME= as "no home", so a run with an empty HOME still found no cargo
cache and reported every crate lockfile-only. A relative HOME is
still refused, since cargo uses it as is.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor

cursor Bot commented Oct 28, 2026

Copy link
Copy Markdown

Bugbot is paused — your team has an unpaid invoice

Bugbot is paused because your team has an unpaid invoice.

Pay it from the billing page in the Cursor dashboard and Bugbot resumes automatically.

(requestId: 731d67ec-bb47-4791-8bdb-a07152df6889)

@cursor

cursor Bot commented Oct 28, 2026

Copy link
Copy Markdown

Bugbot is paused — your team has an unpaid invoice

Bugbot is paused because your team has an unpaid invoice.

Pay it from the billing page in the Cursor dashboard and Bugbot resumes automatically.

(requestId: 587d318e-f4bd-443b-80b4-0a87cbc37490)

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

2 similar comments
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 6566a42. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 28, 2026
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator Author

Ready for review at 6566a42 (burn-down agent).

  • CI: 50 check runs: 31 success, 19 skipped, all green (Cursor Bugbot included).
  • Bugbot reviewed 6566a42: no issues found. No unresolved review threads.
  • Slack announcement not sent this run (Slack posting unavailable); the next run retries.

Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Ready for review at 6566a42 (burn-down agent).

  • CI: 49/49 check runs on 6566a42 green or skipped (30 success, 19 skipped); mergeable into main.
  • Bugbot reviewed 6566a42 (now that billing is restored): no issues found. No open review threads.
  • Slack announcement not sent: the Slack send tool was unavailable this run.

Generated by Claude Code

@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator Author

Final review brief

What it does: Adds one cargo-home resolver, utils::cargo_home::cargo_home(). It uses a non-blank $CARGO_HOME first, then <home>/.cargo. On Unix, an unset or empty HOME now falls back to the passwd entry. Four call sites used to work this out separately (the cargo crawler, the vendor [patch] config, the build-cache cleaner and the self-update channel check), and they all call it now. So env -i and systemd runs patch the ~/.cargo registry that cargo actually builds from (#1135).

Risk: low. Behaviour only changes when HOME is unset or empty on Unix, or on Windows when HOME and USERPROFILE differ (it now follows cargo and uses USERPROFILE). The unsafe getpwuid_r code was moved byte-for-byte from gradle_cache.rs, and gradle_cache still re-exports it.

Look here

Verified

  • Reviewed the unsafe FFI: pointers are read only after a successful call, the buffer is resized on ERANGE and capped at 1 MiB, and pw_dir is null-checked.
  • cfg(unix) gating with a non-unix stub.
  • No other CARGO_HOME resolvers remain in crates/*/src.
  • Affected-module tests: 155/155 pass.
  • With the old resolver put back, 3 of the new tests fail, so they do catch the bug.
  • cargo clippy -p socket-patch-core --all-features -D warnings (the CI form): clean.
  • CI on the head: skipped:19 success:31. Bugbot: success. CHANGELOG.md is untouched. The description matches the code.

Changes I made: none.

Open questions (non-blocking nits)

  • NoHomeEnv (cargo_home.rs:62-91) is #[cfg(test)], but all of its users are #[cfg(unix)]. A Windows cargo test build gets a dead_code warning, not a failure. #[cfg(all(test, unix))] would fix it.
  • ChannelEnv::from_env has no direct test, and a whitespace-only CARGO_HOME now counts as unset.

Auto-merge (squash) is armed, so approving sends this PR straight to the merge queue.


Generated by Claude Code

Merged via the queue into main with commit 230d493 Oct 28, 2026
50 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-cargo-home-passwd-fallback branch October 28, 2026 21:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

3 participants