Cross-referencing the syscall constants in lib/grate-rs/src/constants/syscall_numbers.rs against the FD_HANDLER_TABLE in lib/grate-rs/src/fd_support.rs. Several syscalls lind-wasm exposes are unrouted, and three path-only syscalls are registered with a DIRFD_ARG_1 spec that doesn't match their argument shape.
Missing handlers
The syscall constant exists; no entry in FD_HANDLER_TABLE:
| Syscall |
# |
Why it matters |
Suggested spec |
SYS_POLL |
7 |
The POLL_1 spec is already defined in the file but never wired into a handler |
POLL_1 |
SYS_PIPE |
22 |
Creates two new fds; every test using pipe() is unrouted |
new spec — see below |
SYS_SELECT |
23 |
Needs fd_set translation; no spec or handler today |
new spec |
SYS_FCHMOD |
91 |
Plain fd arg |
FD_ARG_1 |
SYS_PPOLL |
271 |
Same shape as poll |
POLL_1 |
SYS_PIPE2 |
293 |
Like pipe but with cloexec/nonblock flag |
new spec |
SYS_PREADV |
295 |
Plain fd arg |
FD_ARG_1 |
SYS_PWRITEV |
296 |
Plain fd arg |
FD_ARG_1 |
pipe/pipe2 need a new FdArgKind (or extension of the existing SOCKPAIR shape) that allocates two new vfds for kernel fds returned in an int[2] cage buffer. select needs an fd_set-translating shape; the simplest implementation is to walk the three fd_sets and remap each set bit using the existing translator, then forward.
Wrongly registered (path-only syscalls flagged as having a dirfd)
These three are path-based — arg[0] is a const char *, not a vfd. They are currently registered with DIRFD_ARG_1, which makes the handler try to translate the path pointer as a virtual fd; that will fail fdtables::translate_virtual_fd and return EBADF (or whatever errno fd_support converts that to) without ever reaching the kernel.
| Syscall |
Currently |
Should be |
SYS_MKDIR |
DIRFD_ARG_1 |
&[] (no fd args) — or remove from the table if nothing in the fd-routing path needs to see it |
SYS_MKNOD |
DIRFD_ARG_1 |
&[] |
SYS_ACCESS |
DIRFD_ARG_1 |
&[] |
The dirfd-bearing siblings (openat, unlinkat, symlinkat, readlinkat) are correctly registered separately — mkdir/mknod/access are the plain path-only forms.
If these are passing tests today it's because the fs-routing layer above intercepts them before they reach fd_support's translator. The mis-registered specs are still latent bugs that bite as soon as a path happens to land near a low integer.
Bugs / dead code in fd_translation_handler_impl (worth fixing while in here)
should_dup2 block (lines 360–377): flags is computed but never used; get_specific_virtual_fd is called with a hardcoded 0 for cloexec instead of should_cloexec. dup3's O_CLOEXEC flag is therefore ignored on the resulting vfd entry.
should_poll block (lines 409–411): args[0] = pollfds_ptr as u64 runs after the syscall, on a local. Either dead code, or it was meant to write the pollfds back to cage memory with translated vfds (and should be moved to before the syscall, operating on cage memory via copy_data_between_cages, not the local).
- EPFD branch (lines 313–317):
.unwrap().get(...).unwrap() panics if the cage hasn't initialized epoll state. Should return EBADF.
- socketpair (lines 395–407):
get_unused_virtual_fd(...).unwrap() can panic on EMFILE; should error out and roll back the partial allocation.
- Error path (line 357
if ret < 0 { return ret; }): any pollfds we translated in-place to underfds remain corrupted from the cage's perspective when the syscall fails. Should restore the original vfds on the error path.
SYS_MMAP fd handling (line 209): checks kernel_fd == u64::MAX to detect MAP_ANONYMOUS, but the wasm32 ABI may pass -1 differently (sign-extended 0xFFFFFFFF vs 0xFFFFFFFFFFFFFFFF). Worth confirming.
Cross-referencing the syscall constants in
lib/grate-rs/src/constants/syscall_numbers.rsagainst theFD_HANDLER_TABLEinlib/grate-rs/src/fd_support.rs. Several syscalls lind-wasm exposes are unrouted, and three path-only syscalls are registered with aDIRFD_ARG_1spec that doesn't match their argument shape.Missing handlers
The syscall constant exists; no entry in
FD_HANDLER_TABLE:SYS_POLLPOLL_1spec is already defined in the file but never wired into a handlerPOLL_1SYS_PIPEpipe()is unroutedSYS_SELECTSYS_FCHMODFD_ARG_1SYS_PPOLLpollPOLL_1SYS_PIPE2pipebut with cloexec/nonblock flagSYS_PREADVFD_ARG_1SYS_PWRITEVFD_ARG_1pipe/pipe2need a newFdArgKind(or extension of the existingSOCKPAIRshape) that allocates two new vfds for kernel fds returned in anint[2]cage buffer.selectneeds an fd_set-translating shape; the simplest implementation is to walk the three fd_sets and remap each set bit using the existing translator, then forward.Wrongly registered (path-only syscalls flagged as having a dirfd)
These three are path-based —
arg[0]is aconst char *, not a vfd. They are currently registered withDIRFD_ARG_1, which makes the handler try to translate the path pointer as a virtual fd; that will failfdtables::translate_virtual_fdand returnEBADF(or whatever errno fd_support converts that to) without ever reaching the kernel.SYS_MKDIRDIRFD_ARG_1&[](no fd args) — or remove from the table if nothing in the fd-routing path needs to see itSYS_MKNODDIRFD_ARG_1&[]SYS_ACCESSDIRFD_ARG_1&[]The dirfd-bearing siblings (
openat,unlinkat,symlinkat,readlinkat) are correctly registered separately —mkdir/mknod/accessare the plain path-only forms.If these are passing tests today it's because the fs-routing layer above intercepts them before they reach fd_support's translator. The mis-registered specs are still latent bugs that bite as soon as a path happens to land near a low integer.
Bugs / dead code in
fd_translation_handler_impl(worth fixing while in here)should_dup2block (lines 360–377):flagsis computed but never used;get_specific_virtual_fdis called with a hardcoded0for cloexec instead ofshould_cloexec.dup3'sO_CLOEXECflag is therefore ignored on the resulting vfd entry.should_pollblock (lines 409–411):args[0] = pollfds_ptr as u64runs after the syscall, on a local. Either dead code, or it was meant to write the pollfds back to cage memory with translated vfds (and should be moved to before the syscall, operating on cage memory viacopy_data_between_cages, not the local)..unwrap().get(...).unwrap()panics if the cage hasn't initialized epoll state. Should returnEBADF.get_unused_virtual_fd(...).unwrap()can panic onEMFILE; should error out and roll back the partial allocation.if ret < 0 { return ret; }): any pollfds we translated in-place to underfds remain corrupted from the cage's perspective when the syscall fails. Should restore the original vfds on the error path.SYS_MMAPfd handling (line 209): checkskernel_fd == u64::MAXto detect MAP_ANONYMOUS, but the wasm32 ABI may pass-1differently (sign-extended0xFFFFFFFFvs0xFFFFFFFFFFFFFFFF). Worth confirming.