Skip to content

fix(libkernel): disconnect a connected datagram socket on connect to port 0 - #9105

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
Retr0G28:fix/connect-port0-connected-dgram
Oct 24, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
Retr0G28:fix/connect-port0-connected-dgram

Conversation

@Retr0G28

@Retr0G28 Retr0G28 commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

What

connect to port 0 on a datagram socket that was already connected kept the old peer. FreeBSD 11.4's soconnectat() (sys/kern/uipc_socket.c) disconnects a connected socket of a protocol without PR_CONNREQUIRED before it calls pru_connect. udp_disconnect() (sys/netinet/udp_usrreq.c) and udp6_disconnect() (sys/netinet6/udp6_usrreq.c) drop the peer, reset the local address to the wildcard address and clear SS_ISCONNECTED. Then udp_connect() reaches in_pcbconnect_setup() (sys/netinet/in_pcb.c), and udp6_connect() reaches in6_pcbladdr() (sys/netinet6/in6_pcb.c). Both fail port 0 with EADDRNOTAVAIL (49) before anything is bound, so the socket ends up unconnected: getpeername gives ENOTCONN (57) and send without an address gives EDESTADDRREQ (39) from sosend_dgram().

ZeroPortConnect in Udp.cpp now dissolves the association of a connected AF_INET or AF_INET6 datagram socket through Disconnect, the helper that connect to AF_UNSPEC already uses, and then fails with 49. A failed host disconnect returns that error. Stream sockets are unchanged: a connected one still gets the host's EISCONN. The gap is cut from the connect entry in docs/dev/debt/libkernel.md.

Test change: guest_socket_port_zero (CheckDatagram) now connects a UDP socket to a bound loopback peer, for AF_INET and AF_INET6. It then expects connect to port 0 to give -1/49, getpeername to give -1/57 and send to give -1/39. If the host has no IPv6 loopback, the IPv6 case is skipped the same way CheckStream skips it.

Tested

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

  • Before the fix, guest_socket_port_zero with the new checks fails with connect to port 0 on a connected datagram socket must fail with EADDRNOTAVAIL (49) (errno 57). With this PR it passes, and IPv6 loopback is available on this machine, so the IPv6 case ran.
  • Socket tests run serially (guest_inet_address, guest_refused_connect, guest_sce_net, guest_socket_* (13 tests), guest_tcp, guest_udp): 18 of 18 pass. guest_refused_connect timed out once in the full run and passed twice when run alone.
  • After the rebase onto fix(libkernel): fail sendto to port 0 from an unbound IPv4 datagram socket with EADDRNOTAVAIL #8499: guest_socket_* (12 tests, guest_socket_port_zero among them) pass 12 of 12.
  • python tools/check_conventions.py --base origin/main reports no errors. Its one warning is the length of the connect debt entry, 1141 characters on main, which this PR cuts to 1086.
  • Second Windows machine: @xXPBSXx ran the PR head on Windows 10 (same toolchain): guest_socket_port_zero passes 3 of 3, and fails 3 of 3 with main's Udp.cpp (comment below).
  • Not tested on Linux. The new path only calls Disconnect, which connect to AF_UNSPEC already runs on Linux (host AF_UNSPEC connect, then a rebind when the port was dropped). Linux CI must confirm that the test passes there.
  • No title or homebrew run.

Overlap: tools/pr_overlap.py lists #8499, #8682, #8728 and #8921, and none of them implements this. #8499 (now merged) added a sendto port-0 check right next to ZeroPortConnect and rewrote the sendto half of the same debt sentence; this PR is rebased onto it, keeps ZeroPortSend where it is and keeps its sendto sentence word for word. #8682 changes Disconnect to take a non-const Socket&. If it merges first, ZeroPortConnect has to drop its const too, and the textual conflict around the moved function will show this. #8728 and #8921 only touch neighbouring debt lines. The tool could not compare some open PRs. I checked the ones that touch Udp.cpp (#5021, #6977, #7088, #7201, #8344, #8483, #8658, #8670), and none changes connect to port 0 or Disconnect.

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 21, 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 8728 docs/dev/debt/libkernel.md L225-226
PR 8921 docs/dev/debt/libkernel.md L224-225
PR 10299 core/libs/prx/libkernel/Socket/src/Udp.cpp L315-325
core/libs/tests/GuestSocketPortZero.cpp L47-48
docs/dev/debt/libkernel.md L225

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
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 7049 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 7201 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 7653 docs/dev/debt/libkernel.md
PR 8010 docs/dev/debt/libkernel.md
PR 8520 docs/dev/debt/libkernel.md
PR 8553 docs/dev/debt/libkernel.md
PR 9074 docs/dev/debt/libkernel.md
PR 9133 docs/dev/debt/libkernel.md
PR 9156 docs/dev/debt/libkernel.md
PR 9160 docs/dev/debt/libkernel.md
PR 9169 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 9199 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 9557 docs/dev/debt/libkernel.md
PR 9561 docs/dev/debt/libkernel.md
PR 9566 docs/dev/debt/libkernel.md
PR 9575 docs/dev/debt/libkernel.md
PR 9669 docs/dev/debt/libkernel.md
PR 9693 docs/dev/debt/libkernel.md
PR 10319 docs/dev/debt/libkernel.md

@xXPBSXx

xXPBSXx commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Independent Windows run of this PR (data only).

Environment: Windows 10 Pro 10.0.19045, Ryzen 7 5800X3D, WinLibs GCC 15.2.0 r7 (MinGW-w64 UCRT), Release, BUILD_TESTING=ON. Merge-tree of the PR head against main is clean. I built only guest_socket_port_zero_tests; ctest -N -R guest_socket_port_zero lists exactly one test.

PR head tested: 144470f04d3797be44fd0ce00fe8767c797ef5d9.

Results (3 runs each):

  • PR head: guest_socket_port_zero passes 3/3.
  • Baseline: I kept the PR's test and replaced only core/libs/prx/libkernel/Socket/src/Udp.cpp with the version from the PR's merge base (11bb53a347), then rebuilt. The test fails 3/3 (ctest reports Failed). I restored the PR's files afterwards.

GPU: the run output has no Selected GPU line; no GPU claim is made.

Not run: other platforms, the other tests in the same family, the full repository CTest, and any title or console run.

AI-assisted (Claude Code).

…port 0

FreeBSD 11.4's soconnectat() (sys/kern/uipc_socket.c) disconnects a
connected socket of a protocol without PR_CONNREQUIRED before it calls
pru_connect. udp_disconnect() and udp6_disconnect() drop the peer and
reset the local address, then in_pcbconnect_setup() and in6_pcbladdr()
fail port 0 with EADDRNOTAVAIL, so the socket is left unconnected.
connect to port 0 on a connected datagram socket now dissolves the
association through the AF_UNSPEC disconnect path before failing with 49,
where it kept the old peer. Stream sockets keep EISCONN.
@Retr0G28
Retr0G28 force-pushed the fix/connect-port0-connected-dgram branch from 144470f to be4fd86 Compare October 22, 2026 17:04
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 22, 2026
@oneandonlydean oneandonlydean self-assigned this Oct 22, 2026
@Dextroid17

Copy link
Copy Markdown
Contributor

Linux run of head be4fd86249, on its own and merged into main at db6af9e0fd (clean merge; main has also changed Udp.cpp since the branch base). CI's build jobs, linux included, were skipped on this head, and @xXPBSXx's Windows run was on the earlier head 144470f04d.

Setup: CachyOS, kernel 7.3.0-4, GCC 16.2.1, glibc 2.44, CMake 4.4.3, Ninja, -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=OFF.

  • guest_socket_port_zero. ctest --test-dir build -R '^guest_socket_port_zero$' -V --no-tests=error --repeat until-fail:3 --timeout 300 passes in each of 3 runs, on both trees. The IPv6 case is skipped silently when there is no IPv6 loopback, so I ran the merged test under strace. Both AF_INET (127.0.0.1) and AF_INET6 (::1) take the path the PR body describes: host connect to the peer, then connect(AF_UNSPEC) = 0, a bind to the wildcard address (0.0.0.0 / ::), getpeername = ENOTCONN and sendto without an address = EDESTADDRREQ.
  • The new test catches the bug. On the head, I reverse-applied only this PR's change to core/libs/prx/libkernel/Socket/src/Udp.cpp, keeping the new test. guest_socket_port_zero then fails 3 of 3 with connect to port 0 on a connected datagram socket must fail with EADDRNOTAVAIL (49) (errno 57). Restoring the change makes it pass again.
  • Socket tests, run serially on the merge (guest_inet_address, guest_refused_connect, guest_sce_net, guest_socket_*, guest_tcp, guest_udp): 18 of 18 pass.
  • Full suite on the merge. ctest -j8 after building everything: 1,973 passed, 29 skipped and 3 failed of 2,005. guest_socket_port_zero and guest_refused_connect passed in that run too. The 3 failures are the agc_driver_sample_order* tests on my RTX 3080 Ti. They fail on plain main db6af9e0fd too (3 runs on each tree), and on lavapipe, the CI device, they pass or skip on the merge.

Overlap: #10299 (opened later, broader address-family and length checks) also makes ZeroPortConnect call Disconnect for a connected datagram socket and adds the same check to GuestSocketPortZero.cpp; its body lists #9105 only as touching nearby lines.

No title or console run.

AI-assisted: yes (Claude Code).

@oneandonlydean
oneandonlydean merged commit 83a25ce into boykopovar:main Oct 24, 2026
11 of 12 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