Repository navigation
fix(netlink): request unique port identifiers - #910
stephenlclarke wants to merge 3 commits into
Conversation
2ee4d23 to
ed533ab
Compare
| guard let ptr = buffer.bind(as: sockaddr.self, size: buffer.count) else { | ||
| throw NetlinkSocketError.bindFailure(rc: 0) | ||
| } | ||
| guard osBind(socketFD, ptr, UInt32(buffer.count)) >= 0 else { |
There was a problem hiding this comment.
I think we should use socklen_t throughout if you can.
There was a problem hiding this comment.
Updated in d35cb51acce19b7df5ea5d0cf75e88e88a303331 and rebased onto current upstream. Both the bind buffer length and returned-address length comparison now use socklen_t; only the kernel netlink port identifier remains UInt32. The current ContainerizationNetlink production target cross-compiles successfully for aarch64 Linux/musl with Swift 6.3 and warnings as errors. This is compilation proof; I have not relabelled the earlier VM test failures as passes.
ed533ab to
d35cb51
Compare
jglogan
left a comment
There was a problem hiding this comment.
@stephenlclarke just one question on error handling.
| } | ||
|
|
||
| var addrLength = socklen_t(buffer.count) | ||
| guard osGetsockname(socketFD, ptr, &addrLength) >= 0 else { |
There was a problem hiding this comment.
nit: A length, family or zero-pid failure might all report socketNameFailure(rc: EINVAL). Should we distinguish these by error message?
There was a problem hiding this comment.
Thanks Jon, agreed. Using socketNameFailure(rc: EINVAL) for all three checks hides which part of the returned address was invalid.
I’ll keep socketNameFailure with the actual errno for a failed getsockname() call, and distinguish unexpected address length, incorrect address family, and a zero port identifier with specific error descriptions. I’ll include the returned values where useful and add focused tests for those diagnostics.
There was a problem hiding this comment.
Implemented in f119fc1: failed getsockname calls retain their actual errno, while invalid address length, address family, and zero port identifier now have distinct errors and descriptions. Focused tests cover each diagnostic, including expected and actual values.
The full review also identified a pre-existing pointer-lifetime issue in the shared byte-buffer helper. Addressed in 52c0f9d by removing the pointer-returning helper, keeping bind and getsockname inside withUnsafeMutableBytes, and parsing error codes by value. The PR description and source comments document the reason and both caller changes.
Tests:
- macOS: all 57 Netlink and buffer tests passed.
- ARM64 Linux: all 58 Netlink and buffer tests passed using the official
swift:6.4.1-nobleimage. The extra Linux test opens eight Netlink sockets at the same time and checks that each gets a different, nonzero ID. - The Netlink library also cross-compiles for ARM64 Linux with Swift 6.4.1 and the matching Static Linux SDK.
I also put the old generic errors back to check that the new tests catch the problem. The three error tests failed as expected. I restored the fix and ran the tests again. They all passed.
The official Linux image includes the Testing module, so the Linux tests now build and run. No extra package was needed.
d35cb51 to
c4b4609
Compare
Remove the package-scoped byte-array bind helper because its pointer escaped the withUnsafeMutableBytes access. Keep bind and getsockname inside that access and parse Netlink error codes as copied values. Retain existing ACK and buffer bounds tests and cover negative NLMSG_ERROR and NLMSG_DONE replies. Document the pointer-lifetime invariant beside the shared helpers and socket calls.
52c0f9d to
9320c9c
Compare
Summary
Allow multiple Netlink route sockets in one process by requesting kernel-assigned port identifiers. Binding every socket with
nl_pid = getpid()makes a second concurrent socket collide with the first. Bind with identifier zero and read the assigned identifier usinggetsockname.Close the descriptor on every post-
socket()initialization failure. Preserve actualerrnofor failed socket-name syscalls. Following Jon Logan's review, report distinct errors for invalid returned address length, address family, and zero port identifier, with expected and actual values where applicable.The address validation helper is shared by the Linux initializer and portable unit tests, so the validation branches can also be exercised on macOS.
Buffer pointer lifetime
The shared
[UInt8].bindhelper returned a pointer after itswithUnsafeMutableBytesclosure had ended. That pointer was no longer guaranteed to be valid, even while the array remained alive. Both the socket initializer and Netlink error parser used it.Remove this package-scoped helper and migrate both callers. The socket initializer now runs
bindandgetsocknamewithin one mutable-byte access closure, then validates the copied address after the closure returns. The error parser usescopyOut(as: Int32.self)to obtain a value and the next offset. Source comments record the lifetime requirement to prevent reintroducing an escaping pointer.Remove tests of the deleted pointer API, retain value-copy bounds tests and successful ACK tests, and add negative
NLMSG_ERRORandNLMSG_DONEresponse coverage. Pointer lifetime itself is enforced by the scoped call structure and reviewed statically; the unit tests validate behavior.Testing
make test 'FILTER=ContainerizationNetlinkTests|BufferTest'with warnings treated as errors, coverage enabled, andSWIFT_CONFIGURATION_EXTRA='--build-system native --jobs 6'. This includes both negative error-response formats, existing successful ACKs, and buffer bounds tests.EINVALerrors caused the three diagnostic tests to fail across five parameter cases. Restored the fix and reran the Netlink suite successfully.swift:6.4.1-nobleimage at commit52c0f9df90a99b8e83b698e6d0754120abec80a5, with repository build prerequisites installed, warnings treated as errors, and coverage enabled. This includes the Linux-only regression opening eight simultaneous real route sockets and checking nonzero, distinct assigned identifiers.ContainerizationNetlinktarget foraarch64-swift-linux-muslusing Swift 6.4.1 and its matching Static Linux SDK with warnings treated as errors.Testing. Native Linux testing is now complete using the full toolchain in the official image, which supplies the module without an extra package dependency. Image digest:sha256:19cbe56bdaec1918f7cf5fdeba42b9bd55bdd8a6149c9990d0e0e3a89a127d63.git diff --checkpass.Compatibility
The public property remains
pid, but represents a Netlink socket port identifier and is not necessarily the process ID. New public error enum cases can require updates to exhaustive downstream switches.The package-scoped pointer-returning
[UInt8].bindhelper has been removed. Its two production callers now keep socket syscall pointers insidewithUnsafeMutableBytesand copy out error-code values. This also removes the pre-existing pointer-lifetime hazard. The helper was package-scoped, and no usages remain in the package.