Skip to content

fix: require chroot write grants for open side effects - #277

Open
jamesboyzj-design wants to merge 1 commit into
multikernel:mainfrom
jamesboyzj-design:fix/open-side-effects-standalone
Open

jamesboyzj-design wants to merge 1 commit into
multikernel:mainfrom
jamesboyzj-design:fix/open-side-effects-standalone

Conversation

@jamesboyzj-design

Copy link
Copy Markdown
Contributor

The virtual chroot checked the requested descriptor mode but missed open side effects. A read-only grant could therefore allow O_RDONLY | O_TRUNC to erase an existing file or O_RDONLY | O_CREAT to create a file. This change requires write authority for creation/truncation before any COW or host-open work, while preserving read-only descriptor access and read-only mount protection.

This is exactly commit 70e258b from #274, cherry-picked onto main a0eb434 as 9d32abc, as requested in #274 (comment). It contains only the open-side-effect implementation, helper and regressions; no access-query or credential-checker changes. It does not close #150. #274 still contains the original shared commit; its effective diff will exclude this fix once this standalone change lands.

Validation on Linux aarch64, kernel 7.0.14-orbstack-00380-ga7e0a2dc9535, Landlock ABI 8, Rust 1.96.1, Docker --init --security-opt seccomp=unconfined without privileged mode:

  • Core unit: 887 passed, 0 failed.
  • Core integration: 525 passed, 0 failed. The two open-effect regressions include 90 queries across ordinary/COW views, read-only grants/mounts, writable controls, and open/openat/openat2 spellings, checking actual bytes and file creation.
  • Both regression tests fail on latest main a0eb434 with identical test/helper sources; both pass here.
  • All 23 workspace test binaries completed. One existing learner HOME-variable test fails under uid 65534 with inherited HOME=/root; the exact case passes separately as root. The profile HOME case was explicitly run separately as root and passed. This is not a fully green unprivileged workspace claim.
  • git diff --check passed. aarch64 uses the raw *at fallback for legacy open; distinct native x86_64 legacy coverage remains for this PR's CI. Checkpoint restore is unverified.

Build: cargo test --offline --locked --workspace --no-run --message-format=json; execute Cargo-reported test binaries with --test-threads=1 as uid/gid 65534 with supplementary groups cleared. C smoke runs as root. Baseline regression filter: readonly_open_cannot_mutate in the core integration executable. Tests use native Linux /tmp for process output.

@congwang-mk congwang-mk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for splitting this out. One blocker:

O_CREAT on an existing file is now denied in read-only trees (can_open). Native Landlock allows open(existing, O_RDONLY|O_CREAT) there, and real tools rely on it: flock(1) opens its lock file with O_RDONLY|O_NOCTTY|O_CREAT, so flock /ro/existing.lock cmd now fails with EACCES under chroot. The tests only cover O_CREAT on missing files. Suggested fix: when the path is not writable and O_EXCL is absent, open with O_CREAT stripped and map ENOENT to EACCES. The supervisor then never creates, so there is no race. O_TRUNC/O_EXCL keep requiring write.

Minor: the path-only case expects EINVAL for all spellings, but native open/openat silently drop non-O_PATH flags and succeed; only openat2 returns EINVAL. That divergence predates this PR, but please don't pin it in the test: either mask the flags for legacy spellings or expect EINVAL only for openat2.

This branch has not been deployed

No deployments
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.

Feature: write-only / append-only filesystem grants (fs_write_only / fs_append)

2 participants