Repository navigation
fix(ext/node): allow adopting inherited extra stdio TCP fds - #35805
Conversation
Follow-up to #34133, which registered inherited extra stdio fds in the FdTable so node:fs can use them. That registration broke adopting an inherited fd that is a TCP socket: net.Socket({ fd }) routes through TCPWrap::open, whose duplicate-fd check was not taught about InheritedExtraStdio entries and rejected the fd with EEXIST (PipeWrap got the exemption, TCPWrap did not). Mirror the PipeWrap logic in TCPWrap::open: allow inherited extra stdio fds and consume their entry (dropping the node:fs dup) only once uv_tcp_open actually claims the fd. Also create the startup dup with F_DUPFD_CLOEXEC instead of dup(). The dup exists purely for in-process node:fs use; without close-on-exec it leaked into spawned grandchildren, holding pipe ends open and delaying EOF for the parent.
|
Reviewed β the fix itself looks correct: the exemption is a faithful mirror of
|
- Add a regression test for the SocketType::Server path: the parent
passes a listening TCP fd through stdio slot 3 and the child adopts
it with net.createServer().listen({ fd: 3 }), accepting a connection
queued by the kernel while the parent's copy of the socket is closed.
- Extract the duplicate-fd policy shared by PipeWrap::open and
TCPWrap::open into FdTable::begin_uv_adopt / finish_uv_adopt so the
two wraps can't drift again (the original bug existed because the
exemption was added to one copy and not the other).
- finish_uv_adopt's doc comment notes that dropping the inherited entry
releases an Rc, so the dup's close is deferred while an in-flight
node:fs stream still holds a clone.
|
Addressed in a006a44:
Full |
Follow-up to #34133, which registers inherited extra stdio fds in the child's
FdTable so node:fs fd APIs can use them. That registration broke adopting an
inherited fd that is a TCP socket:
net.Socket({ fd })routes throughTCPWrap::open, whose duplicate-fd check was not taught aboutInheritedExtraStdioentries and rejected the fd withEEXIST(PipeWrapgot the exemption,
TCPWrapdid not). This mirrors thePipeWraplogic inTCPWrap::open: inherited extra stdio fds are allowed, and their entry (withthe node:fs dup it holds) is consumed only once
uv_tcp_openactually claimsthe fd. The new regression test fails with
EEXISTwithout this change.The startup dup is now also created with
F_DUPFD_CLOEXECinstead ofdup(). It exists purely for in-process node:fs use, but withoutclose-on-exec it leaked into spawned grandchildren, holding pipe ends open
and delaying EOF for the parent past what Node exhibits.
AI assistance was used to prepare this PR.