unix-ancillary
Safe, ergonomic Unix socket ancillary data (SCM_RIGHTS file descriptor passing) for Rust.
Features
- Safe
OwnedFd/BorrowedFdAPI — no raw file descriptors in the public API - Automatic cleanup — received FDs are
OwnedFd, closed on drop - No fd leaks on truncation — the high-level API sizes the receive cmsg buffer past every Unix kernel's per-message fd cap. Surplus fds beyond the caller's
Nare auto-closed; truncation cannot leak fds into the process - CLOEXEC errors surfaced — if
fcntl(FD_CLOEXEC)fails on macOS, every received fd is closed and the error is returned - Ergonomic extension traits —
send_fds()/recv_fds()onUnixStreamandUnixDatagram
Quick Start
use UnixStream;
use UnixStreamExt;
let = pair.unwrap;
// Send a file descriptor
let file = open.unwrap;
tx.send_fds.unwrap;
// Receive it
let recv = rx..unwrap;
assert_eq!;
assert_eq!;
// recv.fds[0] is an OwnedFd — automatically closed on drop
Bring-your-own buffer
use UnixStream;
use UnixStreamExt;
let = pair.unwrap;
let mut buf = ;
let = rx..unwrap;
Low-Level API
use ;
use IoSlice;
use AsFd;
let file = open.unwrap;
let mut buf = vec!;
let mut ancillary = new;
ancillary.add_fds.unwrap;
How fd-leak protection works
The high-level recv_fds sizes the receive cmsg buffer to a platform-specific
upper bound the kernel cannot exceed for a single SCM_RIGHTS message:
- *Linux / BSD: fixed
SCM_MAX_FD = 253. The peer's kernel rejects oversized sends withEINVALbefore they hit the wire. - macOS: the receiver's current
RLIMIT_NOFILE, queried per recv call. The kernel must allocate an fd table entry per delivered fd and physically cannot exceed that limit.
Result: truncation is kernel-impossible on every supported platform.
- Every fd the receiving kernel deposits is wrapped in
OwnedFdimmediately. - Caller gets the first
N; the rest drop and close on the spot. Zero leak. - If
MSG_CTRUNCsomehow fires anyway, every extracted fd is closed and an error is returned — caller never sees partial state.
Low-level callers using SocketAncillary directly manage their own buffer
and must size it correctly; the is_truncated() flag is exposed for that
path.
CLOEXEC race on macOS
macOS lacks MSG_CMSG_CLOEXEC. This crate sets FD_CLOEXEC via fcntl
immediately after recvmsg returns, but a concurrent fork+exec between
the two calls can leak the fd into the child. If your workload forks
concurrently with fd-receiving threads, hold a fork lock around the receive.
Platform Support
- Linux — full support with
MSG_CMSG_CLOEXEC - macOS — supported with
fcntlCLOEXEC fallback (see caveat above) - FreeBSD, OpenBSD, NetBSD, DragonFly — supported with
MSG_CMSG_CLOEXEC
License
MIT