Harden openat2 validation and /dev/fd resolution#90
Conversation
jserv
left a comment
There was a problem hiding this comment.
Solid direction: the validation rejections match Linux semantics for the cases they cover, the new tests are tight, and the 39-case fidelity suite stays green on this branch. A few gaps and one validation/translation inconsistency worth addressing before merge; details inline.
There was a problem hiding this comment.
2 issues found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/syscall/syscall.c">
<violation number="1" location="src/syscall/syscall.c:1408">
P1: openat2_flags_valid() unconditionally rejects all O_TMPFILE usage instead of only invalid combinations, breaking legitimate O_TMPFILE calls</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| (flags & ~(LINUX_O_PATH | LINUX_O_CLOEXEC | LINUX_O_DIRECTORY | | ||
| LINUX_O_NOFOLLOW))) | ||
| return 0; | ||
| if (flags & LINUX___O_TMPFILE) |
There was a problem hiding this comment.
P1: openat2_flags_valid() unconditionally rejects all O_TMPFILE usage instead of only invalid combinations, breaking legitimate O_TMPFILE calls
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/syscall/syscall.c, line 1408:
<comment>openat2_flags_valid() unconditionally rejects all O_TMPFILE usage instead of only invalid combinations, breaking legitimate O_TMPFILE calls</comment>
<file context>
@@ -1405,16 +1405,14 @@ static int openat2_flags_valid(uint64_t flags, uint64_t mode)
return 0;
- if ((flags & LINUX___O_TMPFILE) &&
- (flags & LINUX_O_TMPFILE) != LINUX_O_TMPFILE)
+ if (flags & LINUX___O_TMPFILE)
return 0;
- if ((flags & LINUX_O_TMPFILE) == LINUX_O_TMPFILE &&
</file context>
|
commit 1: Fix openat2 magic-link path normalization
I also consolidated the commit 2: Tighten openat2 flag validation
commit 3: Add fidelity tests for reviewed edge cases
One detail: I did not keep the literal Thank you for the detailed review. I tried to keep the fixes narrowly scoped, separate the behavior changes from the tests, and make the validation path closer to Linux semantics while avoiding unsupported |
Please don't create tables like this. We're here to discuss and improve the software together, not to act as task trackers whose sole purpose is to clear action items and close pending items without genuine interaction. |
|
Understood. I removed the checklist-style table and will keep the discussion focused on the reasoning and tradeoffs instead. |
jserv
left a comment
There was a problem hiding this comment.
Squash the commits and revise the commit messages to comply with the guidelines at https://cbea.ms/git-commit/
openat2 rejects invalid flag combinations before path lookup. Match that for RESOLVE_BENEATH with RESOLVE_IN_ROOT and O_DIRECTORY with O_CREAT. Reject __O_TMPFILE until elfuse can preserve Linux's unnamed temporary-file semantics instead of forwarding a lossy host flag set. Resolve-policy prechecks also need to catch fd magic-link anchors before the generic path translation can follow them. Normalize the dirfd or cwd base lexically for relative paths and treat /dev/fd, /proc/self/fd, and /proc/<self-pid>/fd as the same anchor class for resolve flags. The open_how size check follows Linux's extensible syscall ABI: the v0 layout is three u64 fields, all-zero extension tails are ignored, and nonzero tails fail with E2BIG. Extend syscall fidelity coverage for these edge cases and restore cwd through a saved fd in the cwd-relative cases so failed setup cannot leave the process in a different directory.
b74317c to
988364d
Compare
|
I have pushed the cleaned branch with the The latest review comments are addressed as well: Local validation passes, including format/security/shellcheck/cppcheck, clang-tidy, scan-build, and the syscall fidelity test. |
You don't need to summarize your changes, as we can always review them using Git. Instead, simply click "Resolve conversation" via GitHub web when the issue is addressed, or leave comments wherever you have concerns. Please avoid imitating the workflow or actions of AI agents. |
Summary
Harden
openat2(2)emulation to better match Linux semantics:open_howextension fields withE2BIGopen_howstructures withE2BIGhow.flagsand invalidhow.modewithEINVAL/dev/fd/Nmagic-link traversal underRESOLVE_NO_XDEVRESOLVE_NO_SYMLINKS/RESOLVE_NO_MAGICLINKSalso reject/dev/fd/NLinux intentionally defines
openat2(2)as a stricter API thanopenat(2):unknown or conflicting
how.flagsmust fail, invalidhow.modemust fail, nonzero unsupported extension fields must fail withE2BIG, andRESOLVE_NO_XDEVmust reject mount-point crossings during path resolution.Reference: https://man7.org/linux/man-pages/man2/openat2.2.html
Motivation
Before this change, elfuse accepted some invalid
open_howinputs that Linux rejects. It also allowed a/devdirfd-relativefd/Npath to reach host/dev/fd/NbeforeRESOLVE_NO_XDEVenforcement, which could bypass the intended guest path-resolution constraints.Tests
Summary by cubic
Hardened openat2(2) emulation to match Linux: strict
open_howABI checks and early flag/resolve validation, plus blocking fd magic-link escapes via/proc/*/fdand/dev/fd. This avoids cross-mount and magic-link bypasses and rejects invalid inputs before path lookup./proc/self/fd/N,/proc/<self_pid>/fd/N, and/dev/fd/Nas fd magic-link anchors.RESOLVE_NO_SYMLINKS/RESOLVE_NO_MAGICLINKSreturnELOOPfor those anchors;RESOLVE_NO_XDEVreturnsEXDEV.open_howABI: nonzero tails fail withE2BIG; sizes >4096 fail withE2BIG.07777, mode withoutO_CREAT,RESOLVE_BENEATH|RESOLVE_IN_ROOT,O_DIRECTORY|O_CREAT,O_PATHmisuse, and__O_TMPFILEusage withEINVAL.LINUX_O_DSYNC,LINUX_O_SYNC,LINUX___O_TMPFILE,LINUX_O_TMPFILE), expanded tests for/proc/<pid>/fdand/dev/fdvariants, and stabilized cwd-sensitive tests usingfchdir.Written for commit 988364d. Summary will update on new commits.