Add file descriptor passing and fix socket teardown races - #24
Merged
Conversation
colemancda
force-pushed
the
fix/descriptor-reuse-race
branch
2 times, most recently
from
August 7, 2026 01:56
d35e59b to
00af41b
Compare
The SCM_RIGHTS shims are needed on Apple platforms too, because the CMSG_* accessors are macros, and nothing else in the file needs libc: errno now goes through Errno.current, whose Android branch the Bionic import did not provide.
Adds a native aarch64 job on the ubuntu-24.04-arm runner, and drops the 6.0.3 and 6.1.2 containers now that 6.2 is the minimum.
Matches the Swift 6.2 minimum used by the other workflows.
SOCK_STREAM is imported as an enum by Glibc but as a plain CInt by Darwin, so the test target failed to build on macOS. SocketType and SocketAddressFamily already paper over that difference.
colemancda
force-pushed
the
fix/descriptor-reuse-race
branch
from
August 7, 2026 02:03
2aa3f2e to
3ed3011
Compare
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.
Adds Unix file descriptor passing (
SCM_RIGHTS) and fixes two socket teardown races found while building a pure-Swift D-Bus client on top of this package.File descriptor passing
CMSG_FIRSTHDR,CMSG_NXTHDR,CMSG_DATA,CMSG_LENandCMSG_SPACEare macros, so they are unavailable from Swift on every platform.CSocketgains two shims —c_socket_send_descriptorsandc_socket_receive_descriptors— wrappingsendmsg/recvmsgwith an ancillary buffer. These are the only C shims compiled on Darwin as well as Linux and Android.On top of them:
SocketMessage— received payload, accompanying descriptors, and whether the ancillary buffer was truncated.SocketDescriptor.send(_:fileDescriptors:)andSocketDescriptor.receive(_:maximumDescriptors:)— blocking,Result-returning, matching the existing descriptor API.Socket.write(_:fileDescriptors:)andSocket.receiveMessage(_:maximumDescriptors:)— async wrappers going throughAsyncSocketManager, so a descriptor-carrying send waits for writability like any other write.Two details worth calling out:
maximumDescriptorsare closed rather than leaked. The kernel has already installed them in this process by the timerecvmsgreturns, so dropping the buffer would leak them.EINVAL. A zero-length message can be discarded before its ancillary data is delivered, so the descriptors would vanish silently.Teardown fixes
AsyncSocketManagerkeys its state by file descriptor number, and those numbers are reused as soon as they are closed. Two consequences:Deferred poll results applied to a reused descriptor. A poll result computed for the old socket could be applied to the new one occupying the same number, tearing down a healthy connection. Poll results are now checked against the current
SocketStateidentity before they take effect, and a descriptor missing from the table is skipped instead of trapping.A never-connected socket polls as
POLLHUP. AnAF_UNIXstream socket that has neither connected nor listened reports hangup — verifiable outside Swift entirely. BecauseSocket.initregisters with the manager beforeconnect, the monitor could hang up a socket that was about to be used.SocketStatenow tracks whether the socket was ever established, and hangup is only acted on for established or listening sockets.Both were reproducible as intermittent
ESHUTDOWNon a freshly opened socket under a concurrent test suite.Tests
Eight tests covering round trips of one and several descriptors, descriptors surviving with payload data, truncation reporting, over-capacity descriptors being closed, and the empty-payload rejection.
Notes
NetworkInterface.swift:58, and the fixed-port and fixed-path tests colliding with leftovers from earlier runs) are untouched by this branch and fail the same way onmain.