Skip to content

fix(libkernel): fail sendto to port 0 from an unbound IPv4 datagram socket with EADDRNOTAVAIL - #8499

Merged
adriadam10 merged 2 commits into
boykopovar:mainfrom
xXPBSXx:fix/sendto-port-zero
Oct 22, 2026
Merged

adriadam10 merged 2 commits into
boykopovar:mainfrom
xXPBSXx:fix/sendto-port-zero

Conversation

@xXPBSXx

@xXPBSXx xXPBSXx commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

What

udp_output hands the destination to in_pcbconnect_setup unless the socket is bound to a specific address and port and the destination is neither INADDR_ANY nor INADDR_BROADCAST (in_pcbconnect_setup fails port 0 with EADDRNOTAVAIL). sendto, and sendmsg through it, sent such a datagram to the host. For an AF_INET datagram socket that is not connected they now fail with 49 in those cases; a socket bound to a specific address and port can still send to port 0, as FreeBSD allows. This closes the sendto/sendmsg part of the known gap in the connect Technical debt entry (#6884). AF_INET6 is unchanged.
Fixes #8498
Overlap: git merge-tree against #7475 (sendto on a connected socket, open) has one conflict in Udp.cpp; against #7090, #8392, #8448, #8463, #8474, #8483, #8492, #7511, #7850, #7490 and #8312 it is clean. Whichever lands second rebases. pr_overlap was not run.

Tested

Windows 10 (19045), MinGW-w64 GCC 15.2.0, Release, on upstream/main d0eb3b6 + this commit:

  • guest_udp: the new case aborts without the change to Udp.cpp and passes with it. It checks port 0 from an unbound socket (49, nothing queued), a datagram to the real port, port 0 from a socket bound to 127.0.0.1 (sent) and destination 0.0.0.0 port 0 from that socket (49).
  • guest_datagram_recv, guest_sce_net, guest_socket_error, guest_socket_address_length, guest_poll and guest_tcp pass.
  • python tools/check_conventions.py --base upstream/main: passes.
  • Not tested: Linux/macOS, a console, a title run, sendmsg directly, AF_INET6.

Checklist

AI-assisted (Claude Code).

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 20, 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 7475 core/libs/prx/libkernel/Socket/src/Udp.cpp L1387
PR 8728 docs/dev/debt/libkernel.md L209-210
PR 9105 core/libs/prx/libkernel/Socket/src/Udp.cpp L305-314
docs/dev/debt/libkernel.md L209
PR 9392 core/libs/prx/libkernel/Socket/src/Udp.cpp L314-315
core/libs/tests/GuestUdp.cpp L138
docs/dev/debt/libkernel.md L208-209

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 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 6740 docs/dev/debt/libkernel.md
PR 6830 docs/dev/debt/libkernel.md
PR 6879 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 7149 docs/dev/debt/libkernel.md
PR 7201 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 7447 docs/dev/debt/libkernel.md
PR 7509 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 8344 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 8520 docs/dev/debt/libkernel.md
PR 8553 docs/dev/debt/libkernel.md
PR 9492 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
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

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Thanks, the rule matches udp_output and in_pcbconnect_setup. One problem on Linux: the new test expects a socket bound to 127.0.0.1 to send to port 0 successfully, and that send goes to the host. Linux's udp_sendmsg rejects destination port 0 with EINVAL (bound or not; checked on a 7.2 kernel and in an Ubuntu 24.04 container), so guest_udp will fail on the Linux CI job. Could you either answer that case without the host on Linux (FreeBSD sends the datagram, and nothing receives on port 0, so returning the length matches what the guest sees), or keep EINVAL on Linux, note it in the technical debt entry, and make that check platform-specific?

@github-actions github-actions Bot added the fix error Closes an issue (Fixes #N) label Oct 20, 2026
xXPBSXx and others added 2 commits October 20, 2026 16:54
…ocket with EADDRNOTAVAIL

udp_output hands the destination to in_pcbconnect_setup unless the socket is
bound to a specific address and port and the destination is neither
INADDR_ANY nor INADDR_BROADCAST, and in_pcbconnect_setup fails port 0 with
EADDRNOTAVAIL. The send went to the host, which accepted it. sendto, and
sendmsg through it, now fails the same way for an AF_INET datagram socket
that is not connected. connect to port 0 already did.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…he host

Linux's udp_sendmsg refuses port 0 with EINVAL, so a send that FreeBSD
accepts (the socket is bound to a specific address and port and the
destination is not INADDR_ANY or INADDR_BROADCAST) failed there. Nothing can
receive on port 0, so the call now returns the length itself on every host.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@xXPBSXx
xXPBSXx force-pushed the fix/sendto-port-zero branch from f531a91 to 0bb7b02 Compare October 20, 2026 14:57
@xXPBSXx

xXPBSXx commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, you're right: Linux's udp_sendmsg refuses port 0 with EINVAL. The send that FreeBSD accepts (socket bound to a specific address and port, destination not INADDR_ANY/INADDR_BROADCAST) is now answered in Udp.cpp without the host in 0bb7b02: nothing can receive on port 0, so it returns the length on every platform. The Technical debt entry says so; guest_udp now also checks that nothing is queued afterwards. Re-tested on Windows 10 (guest_udp, guest_sce_net, guest_socket_address_length pass); I could not run Linux here, so the Linux CI is the check.

AI-assisted (Claude Code).

@adriadam10

Copy link
Copy Markdown
Collaborator

Re-reviewed against current main (cf2c667): the bound-socket answer in 0bb7b02 matches the udp_output / in_pcbconnect_setup condition, so a socket bound to a specific address and port gets the length without the host, and Linux's EINVAL doesn't reach the guest. Checked the unbound and wildcard cases (49), sendmsg through sendto, the guest_udp case (fails without the change) and the debt entry; merged.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

libkernel: sendto to port 0 from an unbound IPv4 datagram socket is sent where FreeBSD 11.4 fails with EADDRNOTAVAIL

3 participants