fix(redox): translate POSIX F_DUPFD_CLOEXEC (1030) to Redox syscall ABI (5)

fcntl(F_DUPFD_CLOEXEC) reached redox_rt/kernel with the POSIX/C value 1030
instead of the Redox syscall value (syscall::F_DUPFD_CLOEXEC=5). redox_rt did
not recognize it as a dup, forwarded the raw command to the kernel, and the
kernel's fcntl fell through to its EINVAL catch-all. That made
OwnedFd::try_clone() return EINVAL, which broke tokio Runtime::build() (its I/O
reactor try_clones the epoll fd) for every tokio user on Redox: all the zbus
system daemons (sessiond/polkit/udisks/upower) and the login shell. F_DUPFD and
plain dup worked because they share the same value in both namespaces;
F_DUPFD_CLOEXEC was the sole untranslated command.

Also drop the ineffective epoll_ctl EINVAL/EBADF retry loop: it was based on a
disproven 'transient scheme churn' theory; epoll registrations always succeed
immediately. The real dup failure is fixed above.
This commit is contained in:
Red Bear OS
2026-07-17 22:59:55 +09:00
parent 415fa3c8c2
commit 733da06876
2 changed files with 19 additions and 31 deletions
+1 -30
View File
@@ -64,36 +64,7 @@ impl PalEpoll for Sys {
// systems. If this is needed, use a box or something
data: unsafe { (*event).data.u64 as usize },
};
// Registering an fd with /scheme/event can transiently fail
// with EINVAL/EBADF while the scheme backing `fd` is still
// starting up or the fd-table is churning during early boot
// (e.g. a tokio/mio reactor built by a daemon that races ahead
// of ipcd's uds_stream scheme). Without recovery, tokio's
// Runtime::build aborts the whole runtime, killing every
// zbus-based daemon and any tokio program (including the login
// shell). Retry with a short backoff so the registration
// succeeds once the scheme settles; a genuinely permanent
// failure still surfaces after the bounded window. This mirrors
// the park-path recovery in the P0-epoll-redox-recovery tokio
// patch, but at the registration site the patch cannot reach.
let mut attempts: u32 = 0;
loop {
match Sys::write(epfd, &ev) {
Ok(_) => break,
Err(Errno(err))
if (err == EINVAL || err == EBADF) && attempts < 400 =>
{
attempts += 1;
let req = syscall::data::TimeSpec {
tv_sec: 0,
tv_nsec: 5_000_000, // 5ms; up to ~2s total
};
let mut rem = syscall::data::TimeSpec::default();
let _ = syscall::nanosleep(&req, &mut rem);
}
Err(err) => return Err(err),
}
}
Sys::write(epfd, &ev)?;
}
EPOLL_CTL_DEL => {
Sys::write(
+18 -1
View File
@@ -477,7 +477,24 @@ impl Pal for Sys {
_ => {}
}
Ok(redox_rt::sys::fcntl(fd as usize, cmd as usize, args as usize)? as c_int)
// The POSIX/C value of `F_DUPFD_CLOEXEC` exposed by <fcntl.h> (1030,
// matching Linux so that libc/std/mio hardcode it) does NOT match the
// Redox syscall-ABI value that redox_rt and the kernel understand
// (`syscall::F_DUPFD_CLOEXEC`, 5). Every other fcntl command that reaches
// redox_rt shares the same numeric value in both namespaces, so
// F_DUPFD_CLOEXEC is the single command that must be translated here.
// Without this, redox_rt does not recognize 1030 as a dup, forwards the
// raw command to the kernel, and the kernel's fcntl falls through to its
// catch-all EINVAL arm. That made `OwnedFd::try_clone()` fail with
// EINVAL, which in turn broke `tokio::runtime::Builder::build()` (its I/O
// reactor try_clones the epoll fd) for every tokio user on Redox — all
// the zbus system daemons and the login shell.
let redox_cmd = if cmd == fcntl::F_DUPFD_CLOEXEC {
syscall::F_DUPFD_CLOEXEC
} else {
cmd as usize
};
Ok(redox_rt::sys::fcntl(fd as usize, redox_cmd, args as usize)? as c_int)
}
fn fdatasync(fd: c_int) -> Result<()> {