feat(fast_io): add openat_via_sandbox_or_fallback + readlinkat_via_sandbox helpers (closes 16+ SEC-1 GAPs)#4716
Merged
Conversation
Adds four new SEC-1 helpers in `fast_io::dir_sandbox::at_syscalls`: - `openat(dirfd, name, flags, mode) -> File` raw libc wrapper. - `openat_via_sandbox_or_fallback(...)` adaptor that takes the sandbox fast path on a single-component leaf beneath `dest_dir` and falls back to `std::fs::OpenOptions` against `link_path` with best-effort `O_*` bit translation otherwise. - `readlinkat(dirfd, name) -> PathBuf` raw libc wrapper with a growing buffer (256B -> PATH_MAX, doubling each round trip). - `readlinkat_via_sandbox_or_fallback(...)` adaptor mirroring the openat shape but falling back to `std::fs::read_link`. The helpers follow the established SEC-1 pattern: pinning the parent via the sandbox dirfd closes the TOCTOU window between path walk and the kernel reaching the inode. The `*_via_sandbox_or_fallback` adaptors keep behaviour byte-identical for callers that have not yet plumbed a `DirSandbox`, so individual call sites can be cut over one at a time. This PR adds the helpers and re-exports them through `dir_sandbox::mod` and `fast_io::lib`. It deliberately does NOT wire any caller; per-site wiring lands in follow-up PRs so each cutover gets its own diff and review. Extends the existing tests module with 10 new tests covering: - raw `openat` success and ENOENT, - `openat_via_sandbox_or_fallback` fast path, multi-component fallback, and absent-sandbox fallback, - raw `readlinkat` success and EINVAL on a non-symlink, - `readlinkat_via_sandbox_or_fallback` fast path, EINVAL, and multi-component fallback. All helpers are `#[cfg(unix)]` (the `dir_sandbox` module is itself Unix-only); Windows continues to use path-based stdlib opens per the SEC-1.l NTFS handle audit. Closes (post-wire) 16+ GAPs from the SEC-1 path-syscall coverage audit in PR #4710: #4 (readlinkat) plus the 9 direct openat sites (#9-#17) and lays foundations for #5, #20 (open + readdir-loop) and #6, #27 (recursive *at peel). Refs PR #4710.
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.
Summary
Adds two new SEC-1 helper families to
fast_io::dir_sandbox::at_syscalls:openat+openat_via_sandbox_or_fallback-- single-shot opensanchored on the sandbox dirfd, with a best-effort
O_*translationto
std::fs::OpenOptionson the fallback path.readlinkat+readlinkat_via_sandbox_or_fallback-- symlinktarget reads anchored on the sandbox dirfd, falling back to
std::fs::read_linkoutside the sandbox fast path.Follows the established SEC-1 pattern (raw helper + adaptor +
single_component_leafeligibility check) used byunlinkat,mkdirat,renameat, etc.This PR only adds the helpers and their re-exports through
dir_sandbox::modandfast_io::lib. It deliberately does notwire any of the 16+ identified call sites -- per-site wiring lands in
follow-up PRs so each cutover gets its own diff and review.
Extends the existing tests module with 10 new tests (3 raw
openat/openatsandbox, 4openat_via_sandboxsandbox/multi-component/no-sandbox,3 raw
readlinkat/ sandbox / multi-component fallback).All helpers are
#[cfg(unix)](thedir_sandboxmodule is itselfUnix-only); Windows continues to use path-based stdlib opens per the
SEC-1.l NTFS handle audit. No Windows unused-import risk.
GAPs closed (post-wire) from PR #4710 audit
Per the GAP table in
docs/audits/sec-1-path-syscall-audit-2026-05-22.md,the new helpers unblock the following sites once their respective
follow-up wiring PRs land:
transfer/receiver/directory/links.rs:75read_link->readlinkat_via_sandbox_or_fallbacktransfer/receiver/quick_check.rs:268File::open->openat_via_sandbox_or_fallbacktransfer/receiver/basis.rs:119File::open->openat_via_sandbox_or_fallbacktransfer/receiver/basis.rs:134File::open->openat_via_sandbox_or_fallbacktransfer/transfer_ops/response.rs:80OpenOptions::open->openat_via_sandbox_or_fallbacktransfer/transfer_ops/response.rs:342OpenOptions::open->openat_via_sandbox_or_fallbacktransfer/disk_commit/process.rs:232OpenOptions::open->openat_via_sandbox_or_fallback(also needs SEC-1.j cross-thread plumbing)transfer/disk_commit/process.rs:236OpenOptions::open->openat_via_sandbox_or_fallback(same plumbing)transfer/disk_commit/process.rs:354OpenOptions::open->openat_via_sandbox_or_fallback(same plumbing)transfer/temp_guard.rs:130OpenOptions::create_new->openat_via_sandbox_or_fallbacktransfer/temp_guard.rs:217remove_fileDrop -- benefits from open carrier reworktransfer/temp_cleanup.rs:95read_dir-- needs follow-upopenat+fdopendirhelper built on top of thesetransfer/temp_cleanup.rs:137remove_file-- benefits from open carrier plumbingtransfer/receiver/directory/deletion.rs:115read_dir-- dittotransfer/receiver/directory/deletion.rs:157remove_dir_all-- recursive*atpeel built onopenat+ readdir +unlinkatengine/delete/emitter/fs.rs:90remove_dir_allrecursive fallback -- same recursive peelThat is 16 direct beneficiaries; the broader follow-up set
(
open_path-based code paths reachable through the disk_commit andtemp-file lifecycle clusters) brings the count past 20 once the
DeleteFs trait refactor lands (tracked separately, out of scope here).
Test plan
crates/fast_io/src/dir_sandbox/at_syscalls.rstests module exercise sandbox fast path, multi-component
fallback, absent-sandbox fallback, and ENOENT/EINVAL error paths.