Skip to content

fix(libkernel): report the wildcard local address after a UDP association is dissolved - #9392

Closed
levgiorg wants to merge 1 commit into
boykopovar:mainfrom
levgiorg:fix/libkernel-udp-disconnect-wildcard-address
Closed

levgiorg wants to merge 1 commit into
boykopovar:mainfrom
levgiorg:fix/libkernel-udp-disconnect-wildcard-address

Conversation

@levgiorg

@levgiorg levgiorg commented Oct 21, 2026 •

Copy link
Copy Markdown

What

getsockname on a UDP socket reported the old local address after the association was dissolved. connect(s, AF_UNSPEC) resets the local address to the wildcard address on the console, keeping the port; AnyPS5 kept reporting the address the socket was bound to.

FreeBSD 11.4's udp_disconnect (sys/netinet/udp_usrreq.c) does inp->inp_laddr.s_addr = INADDR_ANY; and leaves inp->inp_lport untouched, so the local address becomes the wildcard and the port is kept, on every host.

No portable host call produces that. Linux's connect(AF_UNSPEC) resets the local address only when the socket was not explicitly bound (SOCK_BINDADDR_LOCK), and a bound socket cannot be bound again (EBUSY); Windows keeps the address unconditionally. The socket now records that its association was dissolved and getsockname reports the wildcard address (IPv4 and IPv6) until the next bind or connect.

The host socket keeps the address it was bound to, so it still receives only what that address delivers, where the console's socket, whose local address is the wildcard address, receives on any address of its port. That difference stays recorded in the technical debt.

core/libs/prx/libkernel/Socket/src/Udp.cpp, core/libs/tests/GuestUdp.cpp, docs/dev/debt/libkernel.md.

Tested

Windows 11 26200, WinLibs MinGW-w64 GCC 15.2.0 r7 (posix-seh-ucrt), CMake + Ninja, Release, -DBUILD_TESTING=ON.

  • guest_udp passes with the change. Reverting Udp.cpp to its state on main and rebuilding makes it fail: the check aborts at the wildcard-address assertion (Exit code 0xc0000409), while the assertions before it pass, so the map succeeded and only the reported address was stale.
  • 30/30 socket tests pass: guest_udp, guest_socket*, guest_poll, guest_tcp, guest_socket_host_write_*.
  • python3 tools/check_conventions.py --base origin/main reports no errors.
  • No title or homebrew run.

Ubuntu 24.04.3, GCC 13.3.0, CMake 3.28.3, Ninja 1.11.1: guest_udp passes, and the relinker-only suite is 239/239 on that host. The change only affects the address getsockname reports, and the host socket's own binding is left alone, so the host behaviour is unchanged on every platform.

The guest_udp coverage now runs on Windows as well as Linux. It adds the never-associated case (a socket that was never associated keeps its bound address), the explicit-port case the report names, and the IPv6 case.

Overlap: tools/pr_overlap.py reports no shared export and no shared new file. The pull requests it lists share Udp.cpp and docs/dev/debt/libkernel.md on other lines; the change is in the Socket member, Disconnect, connect, bind and getsockname, which none of them touches. PR #8682 implemented the same behaviour for this issue and was closed on 2026-10-21 after a review that asked for the IPv6 case; this branch covers that case, and adds the never-associated and explicit-port cases. It does not depend on any open pull request.

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: libkernel: getsockname still returns the old local address after a UDP association is dissolved #8681
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 21, 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
docs/dev/debt/libkernel.md
PR 6044 core/libs/tests/GuestUdp.cpp
PR 6149 docs/dev/debt/libkernel.md
PR 6469 docs/dev/debt/libkernel.md
PR 6586 docs/dev/debt/libkernel.md
PR 6740 docs/dev/debt/libkernel.md
PR 6830 docs/dev/debt/libkernel.md
PR 6879 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 7090 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 7201 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 7299 docs/dev/debt/libkernel.md
PR 7447 docs/dev/debt/libkernel.md
PR 7464 docs/dev/debt/libkernel.md
PR 7475 core/libs/prx/libkernel/Socket/src/Udp.cpp
core/libs/tests/GuestUdp.cpp
PR 7485 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 7502 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 7507 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 7530 docs/dev/debt/libkernel.md
PR 7532 docs/dev/debt/libkernel.md
PR 7565 docs/dev/debt/libkernel.md
PR 7605 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 7653 docs/dev/debt/libkernel.md
PR 7804 docs/dev/debt/libkernel.md
PR 7853 docs/dev/debt/libkernel.md
PR 7937 docs/dev/debt/libkernel.md
PR 8010 docs/dev/debt/libkernel.md
PR 8344 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 8359 docs/dev/debt/libkernel.md
PR 8373 docs/dev/debt/libkernel.md
PR 8408 docs/dev/debt/libkernel.md
PR 8448 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 8463 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 8520 docs/dev/debt/libkernel.md
PR 8533 docs/dev/debt/libkernel.md
PR 8537 docs/dev/debt/libkernel.md
PR 8544 docs/dev/debt/libkernel.md
PR 8547 docs/dev/debt/libkernel.md
PR 8553 docs/dev/debt/libkernel.md
PR 8605 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 8641 docs/dev/debt/libkernel.md
PR 8658 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 8700 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 8725 docs/dev/debt/libkernel.md
PR 8726 docs/dev/debt/libkernel.md
PR 8728 docs/dev/debt/libkernel.md
PR 8742 docs/dev/debt/libkernel.md
PR 8866 docs/dev/debt/libkernel.md
PR 8872 docs/dev/debt/libkernel.md
PR 8890 docs/dev/debt/libkernel.md
PR 8894 docs/dev/debt/libkernel.md
PR 8906 docs/dev/debt/libkernel.md
PR 8910 docs/dev/debt/libkernel.md
PR 8916 docs/dev/debt/libkernel.md
PR 8921 docs/dev/debt/libkernel.md
PR 8977 docs/dev/debt/libkernel.md
PR 8985 docs/dev/debt/libkernel.md
PR 8986 docs/dev/debt/libkernel.md
PR 8988 docs/dev/debt/libkernel.md
PR 9001 docs/dev/debt/libkernel.md
PR 9005 docs/dev/debt/libkernel.md
PR 9059 docs/dev/debt/libkernel.md
PR 9060 docs/dev/debt/libkernel.md
PR 9074 docs/dev/debt/libkernel.md
PR 9095 docs/dev/debt/libkernel.md
PR 9099 docs/dev/debt/libkernel.md
PR 9104 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 9105 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 9108 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 9109 docs/dev/debt/libkernel.md
PR 9123 docs/dev/debt/libkernel.md
PR 9127 docs/dev/debt/libkernel.md
PR 9133 docs/dev/debt/libkernel.md
PR 9134 docs/dev/debt/libkernel.md
PR 9153 docs/dev/debt/libkernel.md
PR 9156 docs/dev/debt/libkernel.md
PR 9160 docs/dev/debt/libkernel.md
PR 9164 docs/dev/debt/libkernel.md
PR 9165 docs/dev/debt/libkernel.md
PR 9167 docs/dev/debt/libkernel.md
PR 9169 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 9187 docs/dev/debt/libkernel.md
PR 9199 docs/dev/debt/libkernel.md
PR 9218 docs/dev/debt/libkernel.md
PR 9239 docs/dev/debt/libkernel.md
PR 9242 docs/dev/debt/libkernel.md
PR 9256 docs/dev/debt/libkernel.md
PR 9263 docs/dev/debt/libkernel.md
PR 9265 docs/dev/debt/libkernel.md
PR 9273 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 9298 docs/dev/debt/libkernel.md
PR 9302 docs/dev/debt/libkernel.md
PR 9314 docs/dev/debt/libkernel.md
PR 9326 docs/dev/debt/libkernel.md
PR 9359 docs/dev/debt/libkernel.md
PR 9363 docs/dev/debt/libkernel.md
PR 9365 docs/dev/debt/libkernel.md
PR 9378 docs/dev/debt/libkernel.md
PR 9389 docs/dev/debt/libkernel.md
PR 9461 docs/dev/debt/libkernel.md
PR 9477 core/libs/prx/libkernel/Socket/src/Udp.cpp
core/libs/tests/GuestUdp.cpp
docs/dev/debt/libkernel.md
PR 9482 docs/dev/debt/libkernel.md
PR 9491 docs/dev/debt/libkernel.md
PR 9499 docs/dev/debt/libkernel.md
PR 9506 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 9509 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 9512 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 9582 docs/dev/debt/libkernel.md
PR 9625 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 9632 docs/dev/debt/libkernel.md
PR 9669 docs/dev/debt/libkernel.md
PR 9682 docs/dev/debt/libkernel.md
PR 9684 docs/dev/debt/libkernel.md
PR 9693 docs/dev/debt/libkernel.md
PR 9732 docs/dev/debt/libkernel.md
PR 9736 docs/dev/debt/libkernel.md
PR 9770 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 9794 docs/dev/debt/libkernel.md
PR 9840 docs/dev/debt/libkernel.md
PR 9843 docs/dev/debt/libkernel.md

@levgiorg

Copy link
Copy Markdown
Author

The overlap table lists no shared export and no shared new file for any of these pull requests, so none of them implements this behaviour: the co-touches are textual.

The change is in the Socket member, Disconnect, connect, bind and getsockname of core/libs/prx/libkernel/Socket/src/Udp.cpp, and in core/libs/tests/GuestUdp.cpp. The pull requests above touch other parts of the same file or the debt entry next to it.

Issue #8681 was worked on by PR #8682, which was closed on 2026-10-21 after a review that asked for the IPv6 case. This branch covers that case, and adds a never-associated socket and a socket bound to an explicit port. It does not depend on any open pull request.

@github-actions github-actions Bot added the fix error Closes an issue (Fixes #N) label Oct 21, 2026
@levgiorg

Copy link
Copy Markdown
Author

Second platform, since upstream merges pull requests that report both.

Ubuntu 24.04.3 (GCC 13.3.0, CMake 3.28.3, Ninja 1.11.1, WSL2): guest_udp passes, and the socket tests around it pass. The relinker-only suite is 239/239 on that host.

The change only affects the address getsockname reports, and the host socket's own binding is left alone, so the host behaviour is unchanged on every platform.

A contributor's independent run on a second machine appears to help a pull request along, so this is offered the same way.

@ycbuzi

ycbuzi commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Windows run of 555a9e5 merged onto main a6dcea9 (59c50b7eb6, clean), the platform the description lists as not tested. Windows 11 Home 10.0.26200, MinGW-w64 GCC 15.2.0 (ucrt-posix-seh r7), Release, BUILD_TESTING=ON.

Built libkernel and guest_udp_tests with no errors or warnings. ctest --repeat until-fail:2 on guest_udp: passes on both repeats. Not run: a title.

@levgiorg
levgiorg force-pushed the fix/libkernel-udp-disconnect-wildcard-address branch from 555a9e5 to cc1f42c Compare October 21, 2026 23:08
@levgiorg

Copy link
Copy Markdown
Author

Rebased onto the current main (a6dcea927), which the branch had fallen 364 commits behind. The change and its test are unchanged: guest_udp passes on the rebased base and tools/check_conventions.py --base origin/main reports no errors.

@jotitadev jotitadev 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.

Linux run of the current change, since the description only lists Windows. Head cc1f42ccd (a rebase of 555a9e5b9 onto newer main; the diff for Udp.cpp and GuestUdp.cpp is identical).

Machine: Linux x86_64, i7-8700 (6 cores / 12 threads), Ubuntu 26.04, GCC 15.2, Release, BUILD_TESTING=ON, full build. Validated at 555a9e5b9 merged onto main 0c6a5bd59.

  • Build ok. guest_udp passes with the change.
  • Restoring Socket/src/Udp.cpp to main and keeping the test aborts guest_udp (SIGABRT at the wildcard-address assertion), so the test covers the change.

Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.

@levgiorg

Copy link
Copy Markdown
Author

Rebased onto main a6dcea9279 (head cc1f42ccd8); the branch had fallen 364 commits behind, and git merge-tree against main is clean.

Windows 11 26200, WinLibs GCC 15.2.0 r7 (posix-seh-ucrt), Release: ctest -R "^guest_(udp|socket|poll|tcp)" passes 30 of 30; with libkernel/Socket/src/Udp.cpp restored to main and rebuilt, guest_udp fails (0xc0000409) at the wildcard-address assertion while the assertions before it pass. guest_udp now also runs the never-associated, explicit-port and IPv6 cases.

Ubuntu 24.04.3, GCC 13.3.0, CMake 3.28.3, Ninja 1.11.1: guest_udp passes, and the relinker-only suite is 239 of 239.

check_conventions passes.

AI-assisted (Claude Code).

…tion is dissolved

FreeBSD 11.4's udp_disconnect resets the local address to the wildcard
address and keeps the port. The host socket keeps the address it was
bound to, so getsockname reported 127.0.0.1 on Windows and on Linux for
a socket bound to an explicit port.

The socket now remembers that its association was dissolved and
getsockname reports the wildcard address for IPv4 and IPv6 until the
next bind or connect.
@levgiorg
levgiorg force-pushed the fix/libkernel-udp-disconnect-wildcard-address branch from cc1f42c to e4e7b9c Compare October 22, 2026 02:27
@levgiorg

Copy link
Copy Markdown
Author

Rebased onto main 44661db263 (head e4e7b9ca40); the branch had fallen 10 commits behind. git merge-tree against main is clean, guest_udp passes, and check_conventions passes.

@levgiorg

Copy link
Copy Markdown
Author

About the relation to #8921, which documents the same connect to AF_UNSPEC debt entry and whose text says that on Linux and on Windows a socket bound to an explicit port keeps its local address and getsockname reports that host address.

That is the host's behaviour, not the console's. FreeBSD 11.4's udp_disconnect (sys/netinet/udp_usrreq.c) is unconditional:

in_pcbdisconnect(inp);
inp->inp_laddr.s_addr = INADDR_ANY;

The local address is reset to the wildcard whatever the socket was bound to, and only the port is kept. The other half of #8921's sentence says the same — "where FreeBSD 11.4's udp_disconnect() resets the local address to the wildcard address and keeps the port" — so the entry describes the FreeBSD behaviour in one clause and the host behaviour in the other, and this change follows the former.

So this pull request makes getsockname report the wildcard address after the association is dissolved, on every host, and keeps the port.

What the host socket still does is unchanged: the socket keeps the address it was bound to, so it still receives only what that address delivers and a sendto to a non-loopback address still fails with ENETUNREACH (Winsock 10051). That is the part #8921 measures, and the technical debt entry records it.

The change is in core/libs/prx/libkernel/Socket/src/Udp.cpp and core/libs/tests/GuestUdp.cpp; docs/dev/debt/libkernel.md is trimmed to what remains open.

@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 prior resolution of this same behavior in #8682. After the author measured that an explicitly bound Winsock socket remains bound to 127.0.0.1 and sendto to a LAN address still fails after the disconnect, the collaborator closed that PR and asked for a documentation-only debt update unless a title requiring the behavior is identified. The author said no such title was known. See the maintainer resolution and the author's measurement and agreement.

This PR reintroduces the same mismatch: Disconnect sets localAddressCleared, and getsockname zeros the reported address, while the host socket remains bound to its original address. The new tests assert the reported name but do not check that send/receive behavior now matches a wildcard-bound socket. The PR names no title (Unblocks: none) and explicitly says the host socket is unchanged. The IPv6 coverage improves the test matrix but does not resolve this semantic blocker. Please convert this to the docs-only debt update requested in #8682, or provide a specific title and measured call sequence that requires this behavior together with an implementation that keeps the host socket semantics consistent. Keep #8681 open unless the full behavior is resolved; Fixes #8681 is not supported by the current code.

There are also required queue updates:

  • Current main is d6732b51a64f; this head is 91 commits behind it. Please rebase and rerun the conventions and overlap checks.
  • The refreshed overlap scan finds active UDP/debt overlaps with #8344, #8499, #8921, #9105, #9108 and #9169, plus #7511/#8448 in the debt entry. It could not compare the GuestUdp.cpp changes with #6044. Please coordinate the affected hunks and update the merge-order/Depends on checklist after rebase. In particular, #8921 is the existing docs-only treatment requested by the #8682 resolution.

I configured and built guest_udp_tests from this head successfully. guest_udp could not execute on this host: startup aborts because the CPU lacks LZCNT, BMI1/2, MOVBE, AVX2 and FMA. I did not count that as a test pass. tools/check_conventions.py --base upstream/main passes, as does git diff --check. The reported Windows and WSL2 runs are useful; only Conventions and PR labels are present and green in this head's hosted checks, with no hosted build/test result shown.

@levgiorg

Copy link
Copy Markdown
Author

The change is already integrated on the agent/integration line as c681bcf, with the same three files (core/libs/prx/libkernel/Socket/src/Udp.cpp, core/libs/tests/GuestUdp.cpp, docs/dev/debt/libkernel.md). Closing this to free the single open pull request the account limit allows, so #9649 can carry the libSceIcu, libSceNpCommon and libSceXml work.

@levgiorg

Copy link
Copy Markdown
Author

Reopening this.

The account has one open pull request until its first merge (pr-intake.yml: allowedOpen = merged >= 1 ? 20 : 1), and the slot was taken by a pull request that carried an entire integration line instead of one topic. That one is closed; this one holds the slot.

@levgiorg levgiorg reopened this Oct 22, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 22, 2026
@adriadam10 adriadam10 self-assigned this Oct 22, 2026
@adriadam10

Copy link
Copy Markdown
Collaborator

Closing: this implements the behaviour that was settled in #8682.

  • The change only masks getsockname: the host socket keeps the address it was bound to, so for an explicitly bound socket the reported state (wildcard address) has no host counterpart; the socket still receives only what the old address delivers. The debt line you add says so itself.
  • In fix(libkernel): report the wildcard local address after a UDP association is dissolved #8682 @oneandonlydean and the collaborator agreed to close it, since no known title needs it and swapping the host handle is too risky without one, and asked for a docs-only update of docs/dev/debt/libkernel.md instead. No title requiring this behaviour has been identified here either.
  • @MarcZX10's review (changes requested) raises the same point.

A docs-only PR that records the FreeBSD udp_disconnect behaviour and the host difference under the connect to AF_UNSPEC entry is welcome. Feel free to reopen with a title that needs the wildcard address reported.

@adriadam10 adriadam10 closed this Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs fix error Closes an issue (Fixes #N) libkernel needs rebase Conflicts with main, rebase needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

libkernel: getsockname still returns the old local address after a UDP association is dissolved

5 participants