Skip to content

Host fd usage no fixed reserve can bound #208

Description

@Max042004

Summary

PR #204 raises the host process's RLIMIT_NOFILE so the guest's 1024-entry
FD table is actually reachable, and adds a fixed HOST_FD_RESERVE for
elfuse's own runtime overhead. Three pre-existing host-fd amplification paths
scale with guest behavior instead of being fixed overhead — no constant added
to FD_TABLE_SIZE can bound them. All three reproduce on main too (checked
with a manually-set ulimit -Sn 1088), so they predate #204; #204 just makes
a full 1024-entry guest table reliably reachable for the first time, which is
what makes these paths reachable in practice.

1. Fork duplicates the entire live fd table before sending

fork_ipc_send_fd_table() dup() every live fd_table entry before sending
it over SCM_RIGHTS, closing each dup only after the whole batch is sent.
A fork momentarily needs ~2× the guest's live fd count in host fds.

2. Guest directory fds cost 2 host fds each

Opening a directory allocates one host fd, then clone_dir_stream()
dup()s it again for fdopendir() (same pattern in the fork-restore path). The guest
table advertises 1024 capacity uniformly, but a directory-fd-heavy workload
(recursive walkers, build systems, find) hits the host wall at roughly
half the entries a pipe/file-heavy workload would reach.

3. ppoll/pselect6 hold one host dup per polled fd, for the whole wait — stacks with MAX_THREADS

Every blocking read/write already dups its one fd for the wait's duration
(io_block_wait()host_fd_ref_open(),. ppoll
and pselect6 do the same once per polled fd, all held simultaneously for the entire
call. ppoll caps nfds at 256; pselect6 covers up to FD_TABLE_SIZE
(1024) bits. With up to MAX_THREADS = 64 (src/runtime/thread.h:30)
concurrent guest threads each potentially in their own poll/select call, this
path's host-fd cost is O(threads × poll-set size)

Aggravating factor common to all three

When the dup underlying host_fd_ref_open()/host_fd_ref_open_io() fails,
the guest-facing syscall returns -EBADF, since the fd itself is
fine — and nothing is logged. This makes every amplifier above present as an
unattributable failure indistinguishable from an app use-after-close bug,
instead of a clear "elfuse ran out of host fd headroom" signal.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions