Repository navigation
host: locate private files and directories without reading them on musl (O_PATH) - #1522
Merged
Pedro Henrique Penna (ppenna) merged 2 commits intoNov 12, 2026
Merged
Conversation
Contributor
This pull request changes existing testsIt modifies or removes 1 existing test asset(s), listed below. Review each with care: a change to a test can hide a regression that the test caught. See doc/contribute.md#changes-to-existing-tests. Test code
Relocated unchanged: 0; added: 3. Compared the merge base |
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues remain for the Linux metadata-only open update.
No open issues 🎉
What changed in this PR
This PR completes the musl O_PATH follow-up from #1500 within the #1076 workstream. It ensures Linux metadata-only opens preserve O_PATH, avoiding unnecessary read-permission checks and blocking FIFO opens without changing the public API.
Changes:
- Introduces a shared
nix::fcntl::openhelper for three production path-lookup sites. - Fixes the metadata-only test helper to preserve
O_PATHon musl. - Adds regression tests for restrictive directory permissions and nonblocking FIFO handling.
| File | Description |
|---|---|
| nvx-rs/src/host/seal/tests.rs | Uses nix to open metadata-only test files with O_PATH. |
| nvx-rs/src/host/platform/linux/tests.rs | Adds restrictive-umask and FIFO regression tests. |
| nvx-rs/src/host/platform/linux/private.rs | Adds the shared path-lookup helper and updates file-mode and directory operations. |
| nvx-rs/src/host/platform/linux/identity.rs | Routes directory search through the shared helper. |
Review effort: Balanced
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/slim-musl-opath
branch
from
November 12, 2026 11:51
9f2ec8a to
3b698a7
Compare
Pedro Henrique Penna (ppenna)
marked this pull request as ready for review
November 12, 2026 11:51
restore_private_file_mode and open_plain_dir open their path only to locate it, with O_PATH, through OpenOptionsExt::custom_flags, which masks out the bits of O_ACCMODE. On musl, O_ACCMODE is 0o10000003 and includes O_PATH, so the musl build of nvx-rs, which CI and the releases ship, opened the file or directory for reading instead, which needs the permission to read it and waits for a writer of a FIFO. A store directory created under a umask that withholds its owner's permission to read it failed with EACCES instead of getting its mode 0700, a lock file that its owner may neither read nor write never got its mode back, and a FIFO in place of a lock file blocked the restore of its mode until a writer came. Open them with nix::fcntl::open, which passes the flags on as they are, through a helper, open_to_locate, as c19dc62 did for the restore-ready socket (#1500). Nothing changes on glibc, whose O_ACCMODE is 0o3. On nvx-kvm-test (Linux 6.6) and nvx-mshv-test (Linux 6.18), a musl build without this fails the existing test lock_files_get_back_the_permissions_that_the_umask_cleared, of restore_private_file_mode, and two new tests in host::platform::linux, of open_plain_dir under such a umask and of a FIFO, which waits for a writer until the test's timeout. With this, the three pass on musl; they pass on glibc either way. CI runs the unit tests only on glibc. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
open_metadata_only, the helper with which tests open a file for its metadata alone, opened it with O_PATH through OpenOptionsExt::custom_flags, which masks out the bits of O_ACCMODE. On musl, O_ACCMODE includes O_PATH, so a musl build opened the file for reading, and the tests that check that no read goes through such a file, a_seal_needs_no_access_to_the_content and the_staged_check_reads_no_content, failed. Open it with nix::fcntl::open, as the host's own opens with O_PATH do. On nvx-kvm-test and nvx-mshv-test, the two tests fail on musl without this and pass with it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/slim-musl-opath
branch
from
November 12, 2026 12:57
3b698a7 to
a31ef77
Compare
Pedro Henrique Penna (ppenna)
merged commit Nov 12, 2026
9bb42b2
into
feature/nvx-refactor
45 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #1076 (slice musl-opath: the
O_PATHfollow-up that SL5.1c, #1500, recorded).What and why
SL5.1c (#1500) found that the musl build of
nvx-rs, which CI and the releases ship, could restore no named snapshot. It fixed that in c19dc62 ("host: open the restore-ready socket with O_PATH on musl too"). The cause: std'sOpenOptionsExt::custom_flagsmasks out the bits ofO_ACCMODE, and musl'sO_ACCMODEis0o10000003, which includesO_PATH; glibc's is0o3. So on musl,custom_flags(O_PATH | …)silently dropsO_PATH, and the open becomes an ordinary read open. #1500 named three more such sites as a follow-up. SL6.1 (#1520) has since deleted one of them,Directory::search. This PR fixes the other two, and a test helper with the same bug.The production sites.
restore_private_file_modeandopen_plain_dir, innvx-rs/src/host/platform/linux/private.rs, open their path only to locate it. On musl, they opened it for reading instead. That needs the permission to read it, and waits for a writer of a FIFO:create_store_dir, with which the CLI creates its store, failed withEACCESunder a umask that withholds the owner's permission to read the new directory, though its docs say that it sets the mode again for that case.check_store_dirandcheck_snapshot_dirfailed the same way on a directory that its owner may not read.open_lockfailed withEACCES.restore_private_file_modeuntil a writer came.The fix. Both sites open with
nix::fcntl::open, which passes the flags on as they are, as c19dc62 did, through a private helper,open_to_locate(path, flags), which opens withO_PATH | O_CLOEXEC | flags. They keep their error handling. Nothing changes on glibc: std addsO_CLOEXECto every open, andO_RDONLYis 0. std retries an open onEINTRand nix doesn't, but NVX's one signal handler, SIGINT's, hasSA_RESTART.The test helper.
open_metadata_onlyinnvx-rs/src/host/seal/tests.rs, with which tests open a file for its metadata alone, had the same bug. So on musl, the tests that check that no read goes through such a file,a_seal_needs_no_access_to_the_contentandthe_staged_check_reads_no_content, failed. It now opens withnix::fcntl::opentoo.No other site. No other
custom_flagscall innvx-rs/src, host or guest, on any platform, passesO_PATHor another bit of musl'sO_ACCMODE(O_WRONLY,O_RDWR), including those of the helpers that SL6.0d (#1518) moved intohost::platform. The others passO_NONBLOCK,O_NOFOLLOWorO_DIRECTORY, or Windows'FILE_FLAG_*values.Tests. Nothing caught this because CI runs the unit tests only on glibc; a CI run of them on musl stays the follow-up that #1500 recorded. The existing test
lock_files_get_back_the_permissions_that_the_umask_clearedfails on musl without this PR. It is the regression test ofrestore_private_file_modewithout the permission to read the file, so this PR adds no duplicate of it. It adds tests, inhost::platform::linux::tests, only for what no test covered:a_store_directory_gets_mode_700_even_where_the_umask_withholds_reading_it:open_plain_dir, throughcreate_store_dirunder a umask of 0477.a_fifo_in_place_of_a_private_file_is_left_as_it_is_without_waiting_for_a_writer:restore_private_file_modeof a FIFO, behindclias that function is. The call runs in a thread of its own, so that a regression fails after 10 s instead of hanging.Commits.
host: locate private files and directories with O_PATH on musl too: the fix and the two tests.host: open a file for its metadata alone with O_PATH on musl too: the test helper.Size. 13 core lines in 1 counted file,
private.rs, against an estimate of about 30 lines in 2 or 3 files. The public API doesn't change, since the helper is private. Neither does the case matrix: no test that it lists or names changes, and it doesn't inventory unit tests.Validation
At the head
3b698a76cb3, on the base00c55b965f0.On Windows:
cargo fmt --all --check: passed.cargo clippy --locked --all-targets --all-features -- -D warningsandcargo clippy --locked -p nvx --lib --no-default-features -- -D warnings: passed. The changed code is Linux-only, so this shows only that Windows still builds.python scripts/check_test_layout.py: passed.python scripts/check_test_changes.py --base origin/feature/nvx-refactor: one change to existing test code (see Test changes) and three additions (the two tests and a helper,permission_bits).check_matrix.py, against99d2022085f(the last revision with the adapter's inventory): passed.On Linux: nvx-kvm-test (KVM, virtual machine, kernel 6.6) and nvx-mshv-test (MSHV, virtual machine, kernel 6.18), Rust 1.95.0, the glibc target and
x86_64-unknown-linux-musl, as an unprivileged user, since root would pass the permission checks.The two new tests and the three existing ones that fail on musl at the base, with
cargo test --locked --lib --all-features [--target x86_64-unknown-linux-musl] -- --exact <the five>:Also at the head, on nvx-kvm-test:
cargo test --locked --lib --all-features, glibc and musl: the same 28guest::tests fail on both targets, which fail on that host's kernel 6.6 without this PR too. Before the rebase, on07c508c74a5, the whole suite's failures were the same tests at the base and the head on glibc on both hosts. On musl, they were the same as glibc's at the head, and the base failed only these sites' tests more. nvx-mshv-test passed all of its tests on both targets.cargo test --locked --lib, with the default features and so withoutcli: builds without warnings in the changed files, and the store-directory test passes.check-nvx-rs's Lint step, with-D warnings: no diagnostics. Likewise for musl,cargo clippywith--all-targets --all-features,--lib,--lib --bins --features cliand--lib --no-default-features.CI:
nvx-rs / ubuntu-latestandNVX CLI / Linuxfail on this head exactly as on the base's own push run, 43212226280 at00c55b965f0. Both ran on theubuntu-26.04image, whereubuntu-latestnow points:guest::exec::tests::a_stalled_sink_stalls_only_its_own_execution_and_loses_nothingfails its assertion atsrc/guest/exec/tests.rs:318, andtest_linux_runners_terminate_openvmm_left_by_earlier_jobsfails because that image's multi-call coreutils refuses to run under another name (coreutils: unknown program 'nvx-leak-…'). The previous base run, 43208645731 at671a192d0a5, passed both onubuntu-24.04.Left to CI: the Windows unit tests, as no Windows code changes; the guest binaries' checks, as no guest code changes; and the microVM scenarios. On glibc, nothing changes. On musl, the opens now match glibc's, and the scenarios, which run the musl build, reach these paths only with permissions that both opens allow.
Test changes
check_test_changes.pylists one change to existing test code, hencetests-modified. The second commit changes the helperopen_metadata_onlyinhost::seal::teststo open withnix::fcntl::openin place ofcustom_flags(O_PATH), so that it opens a file for its metadata alone on musl too. No test's assertions change.