Skip to content

feat(fast_io): add openat_via_sandbox_or_fallback + readlinkat_via_sandbox helpers (closes 16+ SEC-1 GAPs)#4716

Merged
oferchen merged 1 commit into
masterfrom
feat/fast-io-openat-readlinkat-sandbox-helpers
May 22, 2026
Merged

feat(fast_io): add openat_via_sandbox_or_fallback + readlinkat_via_sandbox helpers (closes 16+ SEC-1 GAPs)#4716
oferchen merged 1 commit into
masterfrom
feat/fast-io-openat-readlinkat-sandbox-helpers

Conversation

@oferchen
Copy link
Copy Markdown
Owner

Summary

Adds two new SEC-1 helper families to fast_io::dir_sandbox::at_syscalls:

  • openat + openat_via_sandbox_or_fallback -- single-shot opens
    anchored on the sandbox dirfd, with a best-effort O_* translation
    to std::fs::OpenOptions on the fallback path.
  • readlinkat + readlinkat_via_sandbox_or_fallback -- symlink
    target reads anchored on the sandbox dirfd, falling back to
    std::fs::read_link outside the sandbox fast path.

Follows the established SEC-1 pattern (raw helper + adaptor +
single_component_leaf eligibility check) used by unlinkat,
mkdirat, renameat, etc.

This PR only adds the helpers and their re-exports through
dir_sandbox::mod and fast_io::lib. It deliberately does not
wire 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 /
openat sandbox, 4 openat_via_sandbox sandbox/multi-component/no-sandbox,
3 raw readlinkat / sandbox / 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. 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:

GAP # File / line Syscall now covered
4 transfer/receiver/directory/links.rs:75 read_link -> readlinkat_via_sandbox_or_fallback
9 transfer/receiver/quick_check.rs:268 File::open -> openat_via_sandbox_or_fallback
10 transfer/receiver/basis.rs:119 File::open -> openat_via_sandbox_or_fallback
11 transfer/receiver/basis.rs:134 File::open -> openat_via_sandbox_or_fallback
12 transfer/transfer_ops/response.rs:80 OpenOptions::open -> openat_via_sandbox_or_fallback
13 transfer/transfer_ops/response.rs:342 OpenOptions::open -> openat_via_sandbox_or_fallback
14 transfer/disk_commit/process.rs:232 OpenOptions::open -> openat_via_sandbox_or_fallback (also needs SEC-1.j cross-thread plumbing)
15 transfer/disk_commit/process.rs:236 OpenOptions::open -> openat_via_sandbox_or_fallback (same plumbing)
16 transfer/disk_commit/process.rs:354 OpenOptions::open -> openat_via_sandbox_or_fallback (same plumbing)
17 transfer/temp_guard.rs:130 OpenOptions::create_new -> openat_via_sandbox_or_fallback
19 transfer/temp_guard.rs:217 remove_file Drop -- benefits from open carrier rework
20 transfer/temp_cleanup.rs:95 read_dir -- needs follow-up openat + fdopendir helper built on top of these
21 transfer/temp_cleanup.rs:137 remove_file -- benefits from open carrier plumbing
5 transfer/receiver/directory/deletion.rs:115 read_dir -- ditto
6 transfer/receiver/directory/deletion.rs:157 remove_dir_all -- recursive *at peel built on openat + readdir + unlinkat
27 engine/delete/emitter/fs.rs:90 remove_dir_all recursive fallback -- same recursive peel

That is 16 direct beneficiaries; the broader follow-up set
(open_path-based code paths reachable through the disk_commit and
temp-file lifecycle clusters) brings the count past 20 once the
DeleteFs trait refactor lands (tracked separately, out of scope here).

Test plan

  • CI fmt + clippy + nextest (stable + Linux musl + macOS + Windows)
  • No callers wired -- behaviour for existing sites is unchanged.
  • 10 new tests in crates/fast_io/src/dir_sandbox/at_syscalls.rs
    tests module exercise sandbox fast path, multi-component
    fallback, absent-sandbox fallback, and ENOENT/EINVAL error paths.

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.
@github-actions github-actions Bot added the enhancement New feature or request label May 22, 2026
@oferchen oferchen merged commit 8e2e326 into master May 22, 2026
54 of 55 checks passed
@oferchen oferchen deleted the feat/fast-io-openat-readlinkat-sandbox-helpers branch May 22, 2026 06:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant