Rationale
The FD table uses two ranges: low_fds[1024] for FDs 0..1023 (populated only via dup2/dup3) and entries[4096] for FDs 32768+ (auto-allocated). fd_lookup() returns NULL for FDs in [1024, 32768), so dup2/dup3 targeting this range fails with EBADF.
Normal auto-allocation never enters the gap -- only explicit FD placement is affected (e.g., exec 1025>/dev/null).
Proposed Changes
Option A: Extend low_fds[] to cover [0, 32768), eliminating the gap entirely. ~1MB overhead per instance (32 bytes × 31K entries). Simple, no algorithmic changes.
Option B: Sparse hash map for gap range, keep existing arrays for hot paths. Zero overhead when gap FDs are unused, added code complexity and potential cache misses on lookup.
Option C: Log/debug-trace when dup2 targets the gap and document the limitation. Externally still EBADF -- dup2 semantics cannot surface a differentiated errno.
Considerations
- Most real-world programs never dup2 into [1024, 32768); the gap primarily affects edge cases
- Must preserve
KBOX_FD_BASE=32768 auto-allocation strategy since seccomp ADDFD injection depends on it
- Unit tests in
tests/unit/ cover fd_lookup, insert_at, and removal; new tests needed for gap-range FDs
- Option A is simplest but adds ~1MB per kbox instance
- Option B preserves memory efficiency but adds complexity to a hot path
References
src/fd-table.h : constants and data structure definition
src/fd-table.c : fd_lookup() returns NULL for gap range; insert_at() fails when lookup returns NULL
src/seccomp-dispatch.c : forward_dup2() returns EBADF on insert failure
Rationale
The FD table uses two ranges:
low_fds[1024]for FDs 0..1023 (populated only via dup2/dup3) andentries[4096]for FDs 32768+ (auto-allocated).fd_lookup()returns NULL for FDs in [1024, 32768), so dup2/dup3 targeting this range fails with EBADF.Normal auto-allocation never enters the gap -- only explicit FD placement is affected (e.g.,
exec 1025>/dev/null).Proposed Changes
Option A: Extend
low_fds[]to cover [0, 32768), eliminating the gap entirely. ~1MB overhead per instance (32 bytes × 31K entries). Simple, no algorithmic changes.Option B: Sparse hash map for gap range, keep existing arrays for hot paths. Zero overhead when gap FDs are unused, added code complexity and potential cache misses on lookup.
Option C: Log/debug-trace when dup2 targets the gap and document the limitation. Externally still EBADF -- dup2 semantics cannot surface a differentiated errno.
Considerations
KBOX_FD_BASE=32768auto-allocation strategy since seccomp ADDFD injection depends on ittests/unit/coverfd_lookup,insert_at, and removal; new tests needed for gap-range FDsReferences
src/fd-table.h: constants and data structure definitionsrc/fd-table.c:fd_lookup()returns NULL for gap range;insert_at()fails when lookup returns NULLsrc/seccomp-dispatch.c:forward_dup2()returns EBADF on insert failure