Host fd usage no fixed reserve can bound

Open
#208 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Quiet
Tech stack
c

Research direction

Start by reading fork_ipc_send_fd_table(), clone_dir_stream(), io_block_wait(), host_fd_ref_open(), and host_fd_ref_open_io(); also check src/runtime/thread.h:30 for MAX_THREADS. Trace how each duplicated descriptor is acquired and released, then determine a bounded accounting or failure-reporting approach for fork, directory, and poll/select paths. Done means the host-fd amplification is bounded or explicitly handled and failures are distinguishable from guest EBADF causes.

Written by the indexing model from the issue text.

Description

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.

Dominant language
C
Stars
268
Forks
26
Avg merge
1d 13h
Merged PRs (30d)
48

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from sysprog21/elfuse

All issues in sysprog21/elfuse

Similar issues

More C issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.