Skip to content

feat(libkernel): implement socketpair - #1711

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
SleglJan:feat/libkernel-socketpair
Oct 9, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
SleglJan:feat/libkernel-socketpair

Conversation

@SleglJan

@SleglJan SleglJan commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

What

Adds socketpair to libkernel, in Socket/src/Udp.cpp beside socket and accept, with the semantics of FreeBSD 11's kern_socketpair and socreate:

  • AF_UNIX with SOCK_STREAM, SOCK_DGRAM or SOCK_SEQPACKET and protocol 0. SOCK_NONBLOCK (0x20000000) puts both ends in non-blocking mode; SOCK_CLOEXEC (0x10000000) is accepted and has nothing to do.
  • Errors as FreeBSD 11's socreate and kern_socketpair give them: EAFNOSUPPORT for an unknown family; EPROTONOSUPPORT for type 0 or a non-zero protocol; EPROTOTYPE for another type; EOPNOTSUPP for AF_INET and AF_INET6 with a type and protocol the inet domain has, whose protocols have no connect2; EFAULT for a null output. Nothing is written to sv on failure.
  • The ends are registered as guest sockets, so send, recv (with MSG_NOSIGNAL and MSG_PEEK), recvfrom, recvmsg, shutdown, fcntl, poll and close work on them as on any guest socket, with the existing errno mapping (EAGAIN 35, EPIPE 32 on Linux). On a pair end getsockname and getpeername report FreeBSD's unnamed AF_UNIX address (16 bytes, family 1, truncated to the caller's length as kern_getsockname does), and recvfrom/recvmsg report length 0 for a stream and the unnamed address for a datagram, instead of converting the host's address as an IP one.
  • On Linux the pair is the host's socketpair(AF_UNIX, ...). On Windows, which has none, a SOCK_STREAM pair is a loopback TCP connection (listener on 127.0.0.1:0 with SO_EXCLUSIVEADDRUSE, connect, accept, peer port checked against the client, TCP_NODELAY), the shape OpenSSL's rio_notifier.c and curl's socketpair.c use on Windows, and a SOCK_DGRAM pair is two UDP sockets bound to 127.0.0.1:0 and connected to each other; SOCK_SEQPACKET throws there. The differences from the console that remain (full-receiver datagrams, getpeername after the peer closed, connect/bind/sendto with an address on a pair end, ECONNRESET on Windows) are recorded in TechnicalDebt.

Of the 34 relinked titles in the homebrew catalog (#1578), 19 import socketpair and 2 stop at it on main b858fdc, PPSA99810 PSBox Cloud Gaming and PPSA99995 Prospero Win: each dies in the host loader with app.elf: symbol lookup error: app.elf: undefined symbol: MZb0GKT3mo8 when run for 8 s in a bubblewrap sandbox on Linux. A third, PPSA74126 Stremio Plus, reaches it once sceKernelGetFsSandboxRandomWord is in.

Overlap: #1091 (socket events) inserts its test block at the same place in libkernel/CMakeLists.txt and touches Udp.cpp above the new code; #1459 and #1708 insert a TechnicalDebt line at the same place; #1128 (clang-format) reformats the whole file. Whichever merges second rebases. #1795 (opened after this PR) adds a Linux-only socketpair together with lstat in the same files; this PR covers both platforms and registers its test in ctest.

Tested

  • Linux (CachyOS, kernel 7.2.9, GCC 16.2.1), full build with BUILD_TESTING=ON and ANYPS5_ENABLE_SPIRV_TOOLS=ON on this branch (based on main 4b005b2): ctest --repeat until-fail:3 467/467; tools/check_test_registration.py and tools/check_conventions.py --base upstream/main pass.
  • New guest_socket_pair test: a stream pair carrying data both ways, poll idle and readable, MSG_PEEK, recvfrom with length 0, the unnamed address from getsockname and getpeername with full and truncated buffers, fcntl F_GETFL/F_SETFL with EAGAIN on an empty non-blocking end, shutdown(SHUT_WR) giving the peer EOF while the other direction stays open, close giving EOF and (Linux) EPIPE on send (the readiness wait before each EOF accepts POLLHUP as well as POLLIN, since WSAPoll reports the peer's FIN as POLLHUP alone, recorded in TechnicalDebt), EBADF on a second close; a SOCK_NONBLOCK | SOCK_CLOEXEC pair non-blocking on both ends; a datagram pair keeping message boundaries with the unnamed sender from recvfrom; a SOCK_SEQPACKET pair (Linux); and the errno for AF_INET, AF_INET6, family 3, type 0, type 3 with and without a protocol, a stray type bit, protocol 6 and a null output, with sv left untouched. Every receive after a send, shutdown or close waits with poll first, since loopback TCP on Windows does not deliver synchronously. Written before the implementation; it failed on the unresolved export and passes with it.
  • The patched libkernel.prx exports the NID (MZb0GKT3mo8).
  • tools/import_audit.py on five of the 19 catalog titles, absent imports before → after (before: the Four open-source homebrew titles stop at start-up on unresolved imports (sysctl, pthread_set_name_np, isatty, mkstemp, signal sets, Opus decoders) #1578 audit on main 4b005b2 + fix(relinker): keep Linux imports declared from host modules off guest exports #1529; after: this branch): PPSA99039 EVO Player 24 → 23, PPSA99082 OpenNOW 36 → 35, PPSA99810 PSBox 64 → 63, PPSA99420 PS5MC 121 → 120, PPSA99003 ProsperoTV 49 → 48. None of them loads yet; the remaining imports are listed in Four open-source homebrew titles stop at start-up on unresolved imports (sysctl, pthread_set_name_np, isatty, mkstemp, signal sets, Opus decoders) #1578.
  • Windows: the same Build workflow run on this commit in the fork's Actions (merged onto main 6b56175), run 37828080042: guest_socket_pair passes its three repeats with the SOCK_SEQPACKET and EPIPE cases excluded, full suite 474/474. The run of the previous commit (210ea0c) aborted at the readiness wait after the peer's shutdown, which accepted POLLIN only. hikazey ran 29a11db merged onto main b858fdc on Windows 11 with MinGW-w64 GCC 15.2.0: guest_socket_pair 5/5, the full suite 478/478, and 5/5 failures with the connect(first → second) removed from the Windows datagram path (comment); their note that a stream send after the peer closed reports ECONNABORTED on Windows is added to the TechnicalDebt line.

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 in TechnicalDebt
  • 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
  • AI-assisted: yes (Claude Code)

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 8, 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
#1832 docs/dev/TechnicalDebt.md L347-348

@hikazey

hikazey commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Windows run of the loopback emulation, since the description lists Linux only: Windows 11 Pro (26200), MinGW-w64 GCC 15.2.0 (WinLibs r7, the CI build), Release, configured like CI, Ryzen 9 5900X. This commit merged onto main b858fdc4; only docs/dev/TechnicalDebt.md needed an auto-merge.

build guest_socket_pair
this PR 5/5 passed
connect(first → second) removed from the Windows SOCK_DGRAM path, test kept 5/5 failed
full suite with this PR 478/478 passed

So NativePair works on Windows for both shapes: the stream pair (listener on 127.0.0.1:0 with SO_EXCLUSIVEADDRUSE, connect, accept, peer-port check, TCP_NODELAY) and the connected UDP pair. That covers data both ways, SOCK_NONBLOCK via FIONBIO (EAGAIN 35 on an empty end), getsockname/getpeername reporting the unnamed AF_UNIX address, and the error cases. The test's two #ifndef _WIN32 blocks are skipped here, as intended.

Related, from #1727: on Windows a write after the peer closed a TCP socket gives errno 53 (ECONNABORTED) rather than EPIPE. That applies to the stream pair too, next to the ECONNRESET line this adds to TechnicalDebt.

AI-assisted: yes (Claude Code).

AF_UNIX pairs as FreeBSD 11 makes them: SOCK_STREAM, SOCK_DGRAM and
SOCK_SEQPACKET with protocol 0, SOCK_NONBLOCK applied to both ends and
SOCK_CLOEXEC accepted, EAFNOSUPPORT for an unknown family, EOPNOTSUPP
for AF_INET and AF_INET6, EPROTONOSUPPORT for type 0 or a protocol,
EPROTOTYPE for another type, EFAULT for a null output, and nothing
written on failure. The ends are guest sockets, so send, recv,
shutdown, fcntl, poll and close work on them; getsockname and
getpeername report the unnamed AF_UNIX address, recvfrom and recvmsg
no address for a stream and the unnamed one for a datagram. On Linux
the pair is the host's socketpair; on Windows a loopback TCP connection
or two connected UDP sockets, and SOCK_SEQPACKET throws. The remaining
differences from the console are recorded in TechnicalDebt.

OpenSSL's QUIC notifier in 15 catalog titles creates such a pair.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@SleglJan

SleglJan commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the Windows run and the mutation check on the datagram pair. Added to the Tested section, and the ECONNABORTED after the peer's close is now in the TechnicalDebt line (8f31696, docs only).

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

Progress report (4b6ab6b → 8f31696)

➖ System libraries: 84.76% (+0.0%, +1 implemented functions)

✅ 1 implemented
Library Name
libkernel socketpair_nid_postfix

@oneandonlydean
oneandonlydean merged commit ede2a9c into boykopovar:main Oct 9, 2026
6 of 8 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.

3 participants