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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions