The dup2('refresh') added for O_CLOEXEC/stale-fd correctness fails for some
exec'd children (observed: a uid-dropped, namespace-restricted login shell)
and propagated as a spawn ENOENT. Fall back to the plain lseek when the
refresh syscall errors so process startup always succeeds; the refresh
still applies wherever the kernel permits it. Prevents any spawn
regression while the refresh path is stabilized for restricted children.
Backport of upstream relibc 580eab8b ('Refresh filetable data before
filetable creation'). FdTbl::from_binary_fd reconstructs an exec'd
process's fd table from the inherited filetable fd. A plain lseek(0) did
not force the backing scheme to re-materialize the current descriptor
set, so an exec'd child (e.g. a login shell) could read a STALE snapshot
and end up with a desynchronized fd -- observed as an interactive shell
whose pty-slave stdin (fd 0) returns nothing while writes to fd 1/2 work.
Issue a SYS_DUP2 'refresh' on the filetable fd before reading it.
The kernel proc scheme returns EBADF for writes to Filetable/NewFiletable
handles, so the bulk FileTableVerb::Close call was silently failing. Close
each O_CLOEXEC fd with SYS_CLOSE instead, which updates both the kernel file
table and the userspace FILETABLE. This unblocks Command::spawn() parents
waiting on the CLOEXEC sync pipe.
The two-step pattern (open full path as scheme root, then open reference
relative to root_fd) failed for namespace-internal paths like
/scheme/namespace/scheme-creation-cap. The namespace scheme resolves the
full path and returns OtherScheme pointing to SchemeList (a kernel scheme
that does not implement kopenat). The second call then hits
SchemeList.kopenat() → EOPNOTSUPP.
The namespace scheme already handles reference resolution internally via
open_scheme_resource(), so a single SYS_OPENAT call with the full path
resolves everything. This also bypasses FILETABLE.insert_upper (which
previously collided with the untracked ns_fd at UPPER index 0).
The open() function used openat_into_upper which calls
FILETABLE.insert_upper() to allocate UPPER fd slots. This can collide
with the namespace fd (ns_fd) that is stored in DYNAMIC_PROC_INFO,
causing the kernel to overwrite the namespace fd entry. All subsequent
open() calls then resolve to the wrong scheme.
Fix: use raw SYS_OPENAT syscalls (which auto-assign POSIX fds) and
register them with register_external_fd, bypassing insert_upper entirely.
validate_kfmap_flags in kernel proc scheme requires exactly one of
MAP_SHARED or MAP_PRIVATE. Without it, shared == private == false,
and the check fails with EINVAL. This was the root cause of the
fexec_impl crash at step 62 (first PT_LOAD segment mapping).
redox-scheme Socket and RawEventQueue create fds via libredox raw
syscalls (SYS_DUP, openat) that bypass the redox-rt userspace
FILETABLE. When FdGuard::dup later reserves a POSIX fd slot via
FILETABLE.add_posix, it can reserve a position occupied by the
untracked socket fd. SYS_DUP_INTO then closes the socket and
replaces it, corrupting the procmgr's fd table.
Add register_external_fd() to redox-rt::sys that marks an
existing kernel fd as occupied in the userspace FILETABLE.
Call it from procmgr::run() for the socket fd and event queue
fd.
Removes all syscall::write(1, b'FK:...') debug trace lines from
fork_impl/fork_inner in redox-rt/src/proc.rs that were polluting
serial output during every fork() call.