Fix file descriptor leaks in enclave_proc_command_send_all - #754
Open
torbensen wants to merge 1 commit into
Open
Fix file descriptor leaks in enclave_proc_command_send_all#754torbensen wants to merge 1 commit into
torbensen wants to merge 1 commit into
Conversation
enclave_proc_command_send_all() creates an epoll instance via
nix::sys::epoll::epoll_create() but never closes it. With nix 0.26 the
returned RawFd is not closed when it goes out of scope, so every call
leaks one descriptor. A long-running process that polls enclave state on
an interval (for example a periodic Describe-based health check)
therefore accumulates descriptors until it hits its RLIMIT_NOFILE and
subsequent syscalls fail with EMFILE ("No file descriptors available").
A second, related leak occurs on the epoll_wait timeout path: each
connection is detached from its UnixStream via into_raw_fd() and handed
to epoll, but a socket that does not reply before
ENCLAVE_PROC_WAIT_TIMEOUT_MSEC is never turned back into a UnixStream and
is neither removed from epoll nor closed, leaking one descriptor per
unanswered connection.
Wrap the epoll fd and the detached connection fds in a small RAII guard
so that every descriptor is closed on all return paths, including early
`?` propagations and epoll_wait timeouts. Descriptors that are reclaimed
into UnixStreams are released from the guard to avoid a double close.
Signed-off-by: torbensen <torbenmagne@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue #, if available:
N/A
Description of changes:
Summary
enclave_proc_command_send_all()insrc/enclave_proc_comm.rsleaks file descriptors on every invocation. For a one-shot CLI command this is harmless, but any long-running process that links this crate and calls the function on an interval (for example, a service that periodically issues aDescribeto check enclave state) steadily accumulates descriptors until it reaches itsRLIMIT_NOFILE. After that, every subsequent socket/accept syscall in the process fails withEMFILE("No file descriptors available").Root cause
There are two distinct leaks in the function:
epoll instance (leaks on every call). The function obtains an epoll fd via
nix::sys::epoll::epoll_create()and never closes it:With
nix0.26,epoll_createreturns a bareRawFdthat is not closed when it goes out of scope, so exactly one descriptor is leaked per call regardless of outcome.Connection sockets (leaks on the
epoll_waittimeout path). Each connection is detached from itsUnixStreamwithinto_raw_fd()and registered with epoll. A descriptor is only turned back into aUnixStream(and thus eventually closed) whenepoll_waitreports an event for it. When a peer does not reply withinENCLAVE_PROC_WAIT_TIMEOUT_MSEC, the loop hitsif num_events == 0 { continue; }and that connection's fd is never reclaimed, never removed from epoll, and never closed — one leaked descriptor per unanswered connection. The early?returns inside the loop have the same problem.The first leak is deterministic and constant-rate, so a periodic caller exhausts its fd table after a fixed, predictable runtime.
Fix
Introduce a small RAII guard, local to the function, that owns the epoll fd and the set of detached connection fds, and closes all of them in
Drop. This guarantees cleanup on every return path — the normal return, the early?propagations during setup and in the wait loop, and theepoll_waittimeout branch. Descriptors that are successfully reclaimed intoUnixStreams are released from the guard first, so there is no double close.The change is confined to
enclave_proc_command_send_all; the function's signature and observable behaviour are unchanged.Testing
cargo checkpasses (the crate sets#![deny(warnings)], so the build also confirms the change is warning-free).UnixStreamdrops; timed-out and registration-failed sockets plus the epoll fd are closed by the guard.By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.