Skip to content

fix(libkernel): report EISCONN for an address on a connected socketpair end as FreeBSD does - #14043

Open
Ragbie wants to merge 2 commits into
boykopovar:mainfrom
Ragbie:fix/socketpair-connect-eisconn
Open

Ragbie wants to merge 2 commits into
boykopovar:mainfrom
Ragbie:fix/socketpair-connect-eisconn

Conversation

@Ragbie

@Ragbie Ragbie commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

What

An address on a socketpair end is handled as FreeBSD 11.4 handles it on a connected AF_UNIX socket. Before this PR every such call reported EAFNOSUPPORT without reaching the host, on every host. Three cases differ:

  • connect on a stream or sequenced-packet end whose peer is open now returns EISCONN. soconnectat returns it for a connected socket of a connection-oriented protocol before it looks at the address. shutdown leaves the end connected, so this also holds after it. After the peer closes, unp_disconnect disconnects the end, and connect still reports EAFNOSUPPORT, as unp_connectat does for a non-AF_UNIX address.
  • sendto and sendmsg with an address on a datagram end whose peer is open now return EISCONN, as uipc_send does when unp_conn is set. The check comes after the flag, length and buffer checks and before waiting for send space, which a datagram end never needs in uipc_send. After the end's own shutdown(SHUT_WR) the send goes to the host and fails with EPIPE, as sosend_generic checks SBS_CANTSENDMORE first.
  • sendto and sendmsg with an address on a stream or sequenced-packet end now ignore the address and send, as uipc_send does on a connected end. Stream ends already did this while the host reported a peer; sequenced-packet ends reported EAFNOSUPPORT. After the peer closes the host send gives EPIPE, as FreeBSD's soisdisconnected sets SBS_CANTSENDMORE.

The pair end counts as connected while its guest peer object is alive. dup shares that object, so closing one alias keeps the end connected, as FreeBSD's file reference does.

bind keeps EAFNOSUPPORT, which uipc_bindat returns for a non-AF_UNIX address. connect on a datagram end is not changed.

The libkernel socketpair debt entry said all three calls should report the console's EISCONN. That entry came from #1711, which has no console measurement for it, and FreeBSD's source contradicts it for bind and for an addressed stream sendto. The entry now states the remaining differences:

  • An AF_UNIX path is not resolved: bind, and a connect or addressed sendto that FreeBSD passes to unp_connect, report EAFNOSUPPORT.
  • connect on a datagram end keeps it connected. FreeBSD disconnects it first and then fails.
  • A datagram over FreeBSD's 2048-byte limit (unpdg_sendspace) is not refused with EMSGSIZE.

The entry was over the 1000-character limit, so it is reworded to fit. Two sentences that describe no difference from the console are dropped: read/write going to the host, and the reason the loopback connection reports no POLLHUP.

Tested

guest_socket_pair gets CheckAddressOnPairEnd:

  • Stream:
    • connect with an AF_INET address returns EISCONN, also after shutdown(SHUT_RDWR), and EINVAL for a 1-byte address.
    • bind returns EAFNOSUPPORT.
    • An addressed sendto is delivered to the peer.
    • On a fresh pair whose peer is closed: connect returns EAFNOSUPPORT, and addressed sends end in EPIPE.
  • Datagram:
    • Addressed sendto and sendmsg return EISCONN, and the pair still exchanges data.
    • bind returns EAFNOSUPPORT.
    • After shutdown(SHUT_WR), sends with and without an address return EPIPE, also once the peer is closed.
  • Sequenced packet (Linux only, as the existing cases): connect returns EISCONN, an addressed sendto is delivered, and after the peer closes it returns EPIPE.

Windows 11 Pro, Intel Core i5-14600KF, WinLibs GCC 15.2.0, full Release build:

  • Without the fix (main's Udp.cpp): guest_socket_pair aborts.
  • With the fix:
    • guest_socket_pair passes with --repeat until-fail:5, and the 156 tests matching socket|tcp|udp|kqueue|equeue|guest_net|select|poll|net_|recv|send|shutdown (without the select_tests_* tests of the test-selection tool) pass with --repeat until-fail:3.
    • Full ctest on main 474030e58: 99% tests passed, 2 tests failed out of 3191. Both fail on main on this machine too:
    • check_skips.py reports one skip, guest_fopen_errors. It skips itself on this machine because creating symbolic links needs Developer Mode.

Linux (Ubuntu 24.04 container, GCC 13.3.0): without the fix guest_socket_pair aborts; with it, it passes with --repeat until-fail:5, and the 117 tests matching the same pattern pass with --repeat until-fail:3. Full Linux ctest was not run.

macOS: not tested. The changed code is shared, and macOS has no sequenced-packet pair.

Overlaps: none. #7090 (SCM_RIGHTS) and #11896 (MSG_WAITALL) change other parts of Udp.cpp; #13742 changes socketpair's result-pointer check.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 8463 core/libs/tests/GuestSocketPair.cpp L166-167

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 4630 docs/dev/debt/libkernel.md
PR 5021 core/libs/prx/libkernel/Socket/src/Udp.cpp
core/libs/tests/GuestSocketPair.cpp
docs/dev/debt/libkernel.md
PR 6936 docs/dev/debt/libkernel.md
PR 7088 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 7105 docs/dev/debt/libkernel.md
PR 7121 docs/dev/debt/libkernel.md
PR 7135 docs/dev/debt/libkernel.md
PR 7149 docs/dev/debt/libkernel.md
PR 7192 docs/dev/debt/libkernel.md
PR 7447 docs/dev/debt/libkernel.md
PR 7464 docs/dev/debt/libkernel.md
PR 7507 core/libs/prx/libkernel/Socket/src/Udp.cpp
core/libs/tests/GuestSocketPair.cpp
PR 7509 docs/dev/debt/libkernel.md
PR 7804 docs/dev/debt/libkernel.md
PR 8605 core/libs/prx/libkernel/Socket/src/Udp.cpp
core/libs/tests/GuestSocketPair.cpp
docs/dev/debt/libkernel.md
PR 9156 docs/dev/debt/libkernel.md
PR 9199 docs/dev/debt/libkernel.md
PR 9242 docs/dev/debt/libkernel.md
PR 9293 docs/dev/debt/libkernel.md
PR 9297 docs/dev/debt/libkernel.md
PR 9302 docs/dev/debt/libkernel.md
PR 9326 docs/dev/debt/libkernel.md
PR 9389 docs/dev/debt/libkernel.md
PR 9557 docs/dev/debt/libkernel.md
PR 9566 docs/dev/debt/libkernel.md
PR 9843 docs/dev/debt/libkernel.md
PR 9938 docs/dev/debt/libkernel.md
PR 10025 docs/dev/debt/libkernel.md
PR 10319 docs/dev/debt/libkernel.md
PR 10442 docs/dev/debt/libkernel.md
PR 10558 docs/dev/debt/libkernel.md
PR 10645 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 11002 docs/dev/debt/libkernel.md
PR 11042 docs/dev/debt/libkernel.md
PR 11215 docs/dev/debt/libkernel.md
PR 11896 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 11939 docs/dev/debt/libkernel.md
PR 12682 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 12688 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 12700 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 12925 docs/dev/debt/libkernel.md
PR 12965 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 13582 docs/dev/debt/libkernel.md
PR 13587 docs/dev/debt/libkernel.md
PR 14128 docs/dev/debt/libkernel.md
PR 14212 docs/dev/debt/libkernel.md

@bunbeans2-arch

Copy link
Copy Markdown
Contributor

A Windows run with GCC 16: Windows 11 Pro 10.0.22631, i9-9900K, MSYS2 UCRT64 GCC 16.1.0, Release, this PR (3714f2a16a) merged into main 16452da.

  • With this PR: guest_socket_pair passed 5 of 5.
  • Same tree with only Udp.cpp put back to main (this PR's tests kept): guest_socket_pair aborted (SIGABRT, exit 0xc0000409) in 5 of 5 runs.
  • main 16452da alone (its own tests and source): the same test passed 3 of 3.

AI-assisted: yes (Claude Code)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants