Repository navigation
fix: require chroot write grants for open side effects - #277
jamesboyzj-design wants to merge 1 commit into
Conversation
congwang-mk
left a comment
There was a problem hiding this comment.
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.
The virtual chroot checked the requested descriptor mode but missed open side effects. A read-only grant could therefore allow
O_RDONLY | O_TRUNCto erase an existing file orO_RDONLY | O_CREATto 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
70e258bfrom #274, cherry-picked onto maina0eb434as9d32abc, 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=unconfinedwithout privileged mode:a0eb434with identical test/helper sources; both pass here.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 --checkpassed. 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=1as uid/gid 65534 with supplementary groups cleared. C smoke runs as root. Baseline regression filter:readonly_open_cannot_mutatein the core integration executable. Tests use native Linux/tmpfor process output.