Skip to content

fix(libkernel): return ENOTCONN from getpeername once a socketpair peer closed - #8344

Merged
oneandonlydean merged 3 commits into
boykopovar:mainfrom
Retr0G28:fix/socketpair-getpeername-enotconn
Oct 25, 2026
Merged

oneandonlydean merged 3 commits into
boykopovar:mainfrom
Retr0G28:fix/socketpair-getpeername-enotconn

Conversation

@Retr0G28

@Retr0G28 Retr0G28 commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

What

getpeername on a socketpair end kept returning the unnamed AF_UNIX address after the other end was closed. On FreeBSD 11, closing an end runs uipc_detach, whose unp_disconnect (datagram) and unp_drop (stream) clear SS_ISCONNECTED on the remaining end (sys/kern/uipc_usrreq.c), and kern_getpeername then fails with ENOTCONN (sys/kern/uipc_syscalls.c).

Each pair end now holds a std::weak_ptr to the other end's socket. The reference expires when the peer's last descriptor is closed, dup'd copies included, and getpeername then fails with ENOTCONN (57). shutdown leaves the ends connected, as soshutdown does. The getpeername clause is removed from the socketpair entry in docs/dev/debt/libkernel.md, and "like on every guest socket" becomes "as on every guest socket" to keep the entry under the 1000-character limit.

guest_socket_pair now checks:

  • a stream end after its peer's shutdown(SHUT_WR) still reports the unnamed peer;
  • after the peer's close, getpeername fails with ENOTCONN and leaves the address and length untouched, while getsockname still succeeds;
  • on a datagram pair, closing one end while a dup'd copy stays open keeps the pair connected, and closing the copy disconnects it.

Tested

Windows 11, WinLibs MinGW-w64 GCC 15.2.0 r7, CMake + Ninja, Release, -DBUILD_TESTING=ON, rebased on main 9d6adeb.

  • guest_socket_pair with main's Udp.cpp fails (exit 0xc0000409 from the failed Require); with this PR it passes.
  • ctest -R "socket|dup|descriptor|fcntl|pipe|poll|select|recvmsg" -E "^agc_": 41 of 41 passed after the rebase onto 47fa6ee; guest_socket_pair passes again after the rebases onto 11f976b and 9d6adeb, whose only conflict each time was the socketpair debt entry.
  • After merging main 6cd36e6 (head 8f66466, clean merge): ctest -R "socket|recvmsg|net_dontwait|^guest_poll$", 40 of 40 pass when run serially, including guest_socket_pair and guest_recvmsg_memory. With main's Udp.cpp on that tree, guest_socket_pair fails again (exit 0xc0000409) and guest_recvmsg_memory still passes.
  • python tools/check_conventions.py --base origin/main reports no errors. Its one warning is the length of the socketpair debt entry, 1382 characters on main, which this PR cuts to 1278.
  • Linux: @xXPBSXx ran the pre-rebase head c4450bc on WSL2 Ubuntu 24.04 (GCC 13.3.0): guest_socket_pair and guest_recvmsg_memory pass, and guest_socket_pair fails with main's Udp.cpp (comment below). Not run on bare-metal Linux.
  • No title or homebrew run.

Overlap:

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 20, 2026 •

Copy link
Copy Markdown

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 6149 docs/dev/debt/libkernel.md
PR 6740 docs/dev/debt/libkernel.md
PR 6830 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 7149 docs/dev/debt/libkernel.md
PR 7192 docs/dev/debt/libkernel.md
PR 7201 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 7464 docs/dev/debt/libkernel.md
PR 7502 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 7509 docs/dev/debt/libkernel.md
PR 7511 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 7565 docs/dev/debt/libkernel.md
PR 8010 docs/dev/debt/libkernel.md
PR 8463 core/libs/prx/libkernel/Socket/src/Udp.cpp
core/libs/tests/GuestSocketPair.cpp
PR 8520 docs/dev/debt/libkernel.md
PR 8553 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 8725 docs/dev/debt/libkernel.md
PR 8916 docs/dev/debt/libkernel.md
PR 9133 docs/dev/debt/libkernel.md
PR 9165 docs/dev/debt/libkernel.md
PR 9187 docs/dev/debt/libkernel.md
PR 9199 docs/dev/debt/libkernel.md
PR 9242 docs/dev/debt/libkernel.md
PR 9284 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 9378 docs/dev/debt/libkernel.md
PR 9442 docs/dev/debt/libkernel.md
PR 9482 docs/dev/debt/libkernel.md
PR 9499 docs/dev/debt/libkernel.md
PR 9509 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 9515 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 9557 docs/dev/debt/libkernel.md
PR 9561 docs/dev/debt/libkernel.md
PR 9566 docs/dev/debt/libkernel.md
PR 9573 docs/dev/debt/libkernel.md
PR 9575 docs/dev/debt/libkernel.md
PR 9578 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 9669 docs/dev/debt/libkernel.md
PR 9682 docs/dev/debt/libkernel.md
PR 9693 docs/dev/debt/libkernel.md
PR 9713 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 9732 docs/dev/debt/libkernel.md
PR 9843 docs/dev/debt/libkernel.md
PR 9882 docs/dev/debt/libkernel.md
PR 9938 docs/dev/debt/libkernel.md
PR 9947 docs/dev/debt/libkernel.md
PR 9948 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 10061 docs/dev/debt/libkernel.md
PR 10133 docs/dev/debt/libkernel.md
PR 10192 docs/dev/debt/libkernel.md
PR 10305 docs/dev/debt/libkernel.md
PR 10385 docs/dev/debt/libkernel.md
PR 10442 docs/dev/debt/libkernel.md
PR 10456 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 10499 docs/dev/debt/libkernel.md
PR 10501 docs/dev/debt/libkernel.md
PR 10645 docs/dev/debt/libkernel.md
PR 10671 docs/dev/debt/libkernel.md
PR 10745 docs/dev/debt/libkernel.md
PR 10749 docs/dev/debt/libkernel.md
PR 10767 core/libs/prx/libkernel/Socket/src/Udp.cpp
core/libs/tests/GuestSocketPair.cpp
docs/dev/debt/libkernel.md

@xXPBSXx

xXPBSXx commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Linux data point for c4450bc82b: WSL2 Ubuntu 24.04 (virtualised kernel, not bare-metal Linux), GCC 13.3.0, Release, BUILD_TESTING=ON, PR head alone (not merged onto main; its merge-base is 6bd02dd91).

  • With the PR: ctest -R '^(guest_socket_pair|guest_recvmsg_memory)$' passes 3 of 3 (both tests).
  • With core/libs/prx/libkernel/Socket/src/Udp.cpp restored to the merge-base (new test kept): guest_socket_pair fails (Subprocess aborted); guest_recvmsg_memory still passes.
    Not tested: macOS, Windows, a title run.
    AI-assisted (Claude Code).

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 21, 2026
@Retr0G28
Retr0G28 force-pushed the fix/socketpair-getpeername-enotconn branch from c4450bc to aeedce4 Compare October 21, 2026 12:22
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 21, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 22, 2026
@Retr0G28
Retr0G28 force-pushed the fix/socketpair-getpeername-enotconn branch from aeedce4 to 8e992c3 Compare October 22, 2026 17:04
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 22, 2026
@oneandonlydean oneandonlydean self-assigned this Oct 22, 2026
…er closed

getpeername on a socketpair end returned the unnamed AF_UNIX address
even after the other end was closed. In FreeBSD 11, closing an end runs
uipc_detach, whose unp_disconnect and unp_drop clear SS_ISCONNECTED on
both stream and datagram ends, and kern_getpeername then fails with
ENOTCONN. Each end now keeps a weak reference to its peer's socket,
which expires when the peer's last descriptor, duplicates included, is
closed. shutdown leaves the ends connected, as soshutdown does.
@Retr0G28
Retr0G28 force-pushed the fix/socketpair-getpeername-enotconn branch from 8e992c3 to 0ca2e0b Compare October 23, 2026 05:23

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed the peer-lifetime change and its focused tests. Holding only a weak_ptr to the peer lets dup share the same socket object while ensuring the peer expires after the last descriptor owner closes; shutdown leaves the object alive, matching the behavior described. The tests cover stream shutdown/close, untouched getpeername outputs, and a datagram peer retained by a duplicated descriptor. The PR reports a Windows control that fails on main and passes with the fix, plus WSL2 results; I have not rerun the native tests locally.

One integration item remains: against current upstream main 16319642a4c84e861188b75583ce853dfbe0a101, tools/check_main_sync.py --base upstream/main --head upstream/pr-8344-current reports 1 commit ahead / 10 behind (merge base 9d6adeb55), with a clean merge. Please merge current main and rerun guest_socket_pair plus the focused socket tests. Preserve the adjacent null-length ordering change from open #7502 in Udp.cpp; that PR changes neighboring lines but has a separate behavior. This request is for branch freshness and integration, not PR volume.

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 23, 2026
@Retr0G28

Copy link
Copy Markdown
Contributor Author

@MarcZX10 Thanks. I merged current main 6cd36e6 into the branch (head 8f66466). The merge was clean and the branch is no longer behind. On Windows, ctest -R "socket|recvmsg|net_dontwait|^guest_poll$" passes 40 of 40 when run serially, including guest_socket_pair and guest_recvmsg_memory. With main's Udp.cpp on the merged tree, guest_socket_pair fails (0xc0000409) and guest_recvmsg_memory still passes.

#7502's null-length change in getpeername is untouched. This PR only adds the expired-peer check on the line next to it, so whichever merges second needs a one-line rebase.

check_conventions reports no errors. Its one warning is the length of the socketpair debt entry, which this PR shortens from 1383 to 1278 characters. Other open PRs edit that entry too, so I left the rest of its wording alone.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@Retr0G28 I confirmed that 8f66466 is a clean merge of main with the reviewed patch unchanged, including the dup case and adjacent null-length handling. The Windows run is useful supporting evidence; guest_socket_pair still needs to pass in the full Linux/Windows run on this head.

@Retr0G28

Copy link
Copy Markdown
Contributor Author

@oneandonlydean Thanks. The local Windows run that passed was on this exact head 8f66466: 40 of 40 socket, recvmsg, net_dontwait and poll tests, guest_socket_pair included. The hosted linux and windows legs are skipped by eligibility on this head, so the full run is waiting on that.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@Retr0G28 the "skipped by eligibility" legs were the automatic build on push. We ran the full Linux and Windows build separately on 8f66466 (run 40101561852): both green, 0 failed tests, and guest_socket_pair passed on both lanes.

@oneandonlydean
oneandonlydean dismissed MarcZX10’s stale review October 24, 2026 18:41

The requested integration is done: head 8f66466 merges current main, keeps the adjacent null-length handling in Udp.cpp, and the full Linux and Windows run (40101561852) is green with guest_socket_pair passing on both.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 24, 2026
…rname-enotconn

# Conflicts:
#	core/libs/tests/GuestSocketPair.cpp
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 24, 2026
@Retr0G28

Copy link
Copy Markdown
Contributor Author

Merged main f26434a to clear a conflict in GuestSocketPair.cpp, where main moved the test's declarations into the shared Api.hpp headers and renamed Msghdr to GuestMsghdr. The resolution keeps main's side; the diff against main is unchanged. Head 3af8e6d, Windows 11, GCC 15.2.0: 49 of 49 socket, recvmsg, net_dontwait and poll tests pass when run serially.

@oneandonlydean
oneandonlydean merged commit b7865bb into boykopovar:main Oct 25, 2026
10 checks passed
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.

4 participants