Skip to content

fix(libkernel): report SO_ERROR and the other SOL_SOCKET options like FreeBSD - #4796

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
xXPBSXx:feat/socket-options-sol-socket
Oct 14, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
xXPBSXx:feat/socket-options-sol-socket

Conversation

@xXPBSXx

@xXPBSXx xXPBSXx commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

What

getsockopt and setsockopt on a libkernel socket knew four SOL_SOCKET options (SO_REUSEADDR, SO_BROADCAST, SO_SNDBUF, SO_RCVBUF) and failed every other one with ENOPROTOOPT. The options now follow FreeBSD 11.4's sosetopt and sogetopt (the same constants libSceNet already uses, for example NET_SO_RCVTIMEO = 0x1006):

  • SO_ERROR (0x1007) returns the socket's pending error as a FreeBSD errno and reports it once. On Windows the host does not clear it when read, so the library does.

  • SO_KEEPALIVE, SO_DONTROUTE, SO_OOBINLINE and SO_LINGER are read from and set on the host socket; SO_TYPE and SO_ACCEPTCONN are read only (as is SO_ERROR), and setsockopt on any of the three fails with ENOPROTOOPT, as FreeBSD's sosetopt has no case for them (they reach its default; the test does not cover this). A boolean option reads back as its own bit (FreeBSD returns so_options & option), SO_LINGER as {SO_LINGER bit, seconds}.

  • SO_DEBUG, SO_USELOOPBACK, SO_REUSEPORT, SO_TIMESTAMP and SO_NOSIGPIPE are left out and still fail with ENOPROTOOPT, as on main, until a title needs one (review of this PR).

  • Lengths follow sooptcopyin / sooptcopyout: a value is read from its first int and fails with EINVAL when shorter; a getsockopt buffer gets as many bytes as it holds and its own length back. SO_SNDBUF / SO_RCVBUF below 1 fail with EINVAL, SO_LINGER outside 0 to 65535 seconds with EDOM.

  • A connect on a non-blocking socket on Windows fails with EINPROGRESS (36) now; it returned EWOULDBLOCK (35), which a program waiting for the connection does not expect. This is the other half of what SO_ERROR is for, so it is in the same change.

Title: The Mound (PPSA26223, EOS SDK). With a temporary trace in socket and getsockopt (not part of this PR), a Windows run (it ended after a few seconds at an unrelated GPU error, #1409) showed 6 calls socket(2, 1, 0) (AF_INET, SOCK_STREAM) and 2 calls getsockopt(fd, 0xffff, 0x1007), SO_ERROR, on two different sockets; no setsockopt and no other option was requested. Those two calls failed with ENOPROTOOPT before. Stray and Harmony's Odyssey made no libkernel socket call in the same kind of run. Palworld (also EOS SDK) was not measured. A separate Linux run by coltken95 with Nuvio Max'd (PPSA99288), reported in the comments below, shows the same SO_ERROR failure of curl's connect check and its fix by this PR. The options other than SO_ERROR are not requested by any title I ran; they follow FreeBSD for completeness.

Overlap: #4903 also changes Udp.cpp and conflicts with this branch there (reported by the overlap bot), so whichever lands second needs a small rebase. #5403 contains #4903's commit and conflicts here for that reason: both add fields to struct Socket at the same place (options here, receiveShut and sendShut there). Keeping all three fields resolves it, in either order. #4658 (accepting option values longer than an int) and #4391 (descriptor numbering) are merged; the routing in SocketOptions.cpp covers every option (the GuestUdp test still passes). The test registration and the TechnicalDebt entry sit at their own places in CMakeLists.txt and libkernel.md (next to guest_udp and the socket error numbers entry), away from the other socket PRs. Receive and send timeouts, the low-water marks and the IPPROTO_TCP, IPPROTO_IP and IPPROTO_IPV6 levels are not part of this PR (they still fail with ENOPROTOOPT). #5510 (simonbronk, EPIPE for a stream send after the peer closed) conflicts with this PR in docs/dev/debt/libkernel.md (git merge-tree of the two heads, re-checked against main ce5c83953; both heads are clean against main); whichever lands second rebases the debt entry. #5565 (Dripl1x, TCP_NODELAY, SO_NOSIGPIPE and SO_NBIO) conflicts with this PR in core/libs/prx/libkernel/Socket/src/Udp.cpp, in the lines of setsockopt and getsockopt this PR rewrites (git merge-tree of the two heads, re-checked against main ce5c83953; both heads are clean against main); its text already says it goes after this PR, and its three options move into SocketOptions.cpp on the rebase.

Title evidence from screamm's Windows validation on this PR (comment of 13 October, 07:46Z; head e6740bc6): traced on main in ProsperoRadio (PPSA99001, bundled libcurl 8.18.0 and OpenSSL 3.5.2 over libkernel sockets), connect(12, 91.98.4.78:443) returns errno 35, poll then reports POLLOUT, and getsockopt(12, SOL_SOCKET, SO_ERROR) fails with errno 42, which curl's verifyconnect() takes as the connect error (Could not connect to server, curl 7); ProsperoRadio is the title that was traced, PSXS5, SwanStation and ProsperoTV bundle the same libcurl. With this PR the same trace shows errno 36 and SO_ERROR 0, and the TCP connection succeeds; the next failure there is OpenSSL's CA file path (a separate gap, issue #5001). The trace is screamm's, not run by me.

More title evidence, from p3n1cillin's comment on this PR (13 October, 14:48Z): PS5MC (PPSA99420, Kodi 22) on main 3faa9cb40 merged with other open PRs it needs to start. Without this PR Kodi's connectivity check fails about 10 ms after each connect (sceNetResolverStartNtoa for www.msftconnecttest.com and www.w3.org, then libkernel connect to port 80; Kodi logs CCurlFile::Exists ... Failed: Could not connect to server(7)); with this PR merged too, no CCurlFile errors appear in the same 60 s run. This is their measurement, not mine.

Tested

Windows 10 (19045), MinGW-w64 GCC 15.2.0 (WinLibs r7), Release, built and run on main c2565f014 after #4391 (the branch is one commit on it); the branch has since been moved to a newer main (git merge-tree of the head 79e162110 against main bf8dc610e is clean; not rebuilt there) with its test registration and debt entry at new places and merges with the other branches of this series without conflicts:

  • guest_socket_options (new) passes: types, SO_ACCEPTCONN before and after listen, SO_ERROR after a successful and a refused non-blocking connect (the second read is 0), the boolean bits, SO_LINGER values and errors, the length rules, unknown options and a bad descriptor.

  • It fails without the change to Udp.cpp (getsockopt of an integer option failed (errno 42)).

  • guest_udp, guest_tcp, guest_sce_net, guest_poll, guest_dup and the other socket tests: pass.

  • guest_socket_options takes about 2 seconds on Windows (4 runs, 2.07 to 2.16 s; most of it is the refused loopback connect), the other new test about 0.2 s; its wait for the connect is at most 6 s, well below the 30 s limit.

  • python tools/check_conventions.py --base upstream/main passes.

  • Measured on a combined build of the twelve open branches of this series (this PR and the others) merged onto main 0f2683cb7: guest_socket_options takes 2.58 s (ctest, serial, Windows). The full guest_* / libc / libkernel group passes (391 of 391, 0 failed; the build also contains the other branches of this series).

  • Not tested: Linux (the code compiles the Winsock and POSIX forms from one source, only the Windows one was built), a console, a title run with this change.

  • On Windows SO_ERROR is emulated for the connect error only: the library reports it once and clears it on the next connect; a later asynchronous error of the same socket without a new connect is not modelled. A null optval with a length returns EINVAL (FreeBSD: EFAULT).

Checklist

  • Merges onto main bf8dc610e without a conflict (git merge-tree, not rebuilt); no other open PR implements the same functions (overlap with fix(libkernel): report socket status from fstat on a guest socket #4903 described above)

  • 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 (docs/dev/debt/libkernel.md)

  • No notes, investigation .md files or images added to the repository

  • New third-party code is a submodule built from source (none added)

  • Generic behaviour, not specific to one title

  • Depends on: none

  • Unblocks: PPSA99288 Nuvio Max'd, sign-in fails without it (confirmed on Linux by coltken95 in the comments; no Game bring-up discussion exists); also PPSA99420 (PS5MC, p3n1cillin's trace) and PPSA99001 ProsperoRadio with its group (screamm's trace), see above

  • AI-assisted: yes

AI-assisted (Claude Code).

🤖 Generated with Claude Code

This was referenced Oct 12, 2026
@coltken95

Copy link
Copy Markdown

Tested on Linux (CachyOS, RTX 2070, NVIDIA 615.71.09) with Nuvio Max'd (PPSA99288): main at e6ecece with this PR and #3644 merged on top.

Without this PR, every HTTPS request fails: curl's getsockopt(SOL_SOCKET, SO_ERROR) after a nonblocking connect gets ENOPROTOOPT, so the sign-in screen shows "Failed to connect to api.nuvio.tv port 443 after 45 ms". strace shows the host connect returning EINPROGRESS and no host getsockopt following it. With this PR, the connection completes and the sign-in screen receives its link code from api.nuvio.tv. guest_socket_options passes.

AI-assisted: yes

@xXPBSXx

xXPBSXx commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for testing this, that is a useful confirmation.

One question, only if it is easy for you: could you share which setsockopt calls the Nuvio run makes on the host (for example strace -f -e trace=setsockopt during sign-in, levels and option names only)? curl normally sets TCP_NODELAY and the TCP keep-alive options and ignores a failure; a follow-up PR for the IPPROTO_TCP level (not part of this one) only makes sense if a title actually asks for them.

AI-assisted (Claude Code).

@oneandonlydean

Copy link
Copy Markdown
Collaborator

SO_DEBUG, SO_USELOOPBACK, SO_REUSEPORT, SO_TIMESTAMP and SO_NOSIGPIPE are accepted and only kept by the library (SocketOptions.cpp:66-77). The description says no title sets them, and a title that sets SO_REUSEPORT or SO_TIMESTAMP would get success without the behaviour. Please leave those five out (ENOPROTOOPT, as on main) until a title needs one.

The SO_ERROR and EINPROGRESS part, with The Mound and PPSA99288, is fine as it is.

@xXPBSXx
xXPBSXx force-pushed the feat/socket-options-sol-socket branch from 0ca7039 to dabb5a4 Compare October 13, 2026 03:02
@xXPBSXx

xXPBSXx commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

Updated as requested: SO_DEBUG, SO_USELOOPBACK, SO_REUSEPORT, SO_TIMESTAMP and SO_NOSIGPIPE are left out again (ENOPROTOOPT, as on main), the test checks that, and the stored-only state is gone. The branch is one commit on main c13dc7e6b; guest_socket_options (still fails without the Udp.cpp change) and the other socket tests pass on Windows (22 of 22).

AI-assisted (Claude Code).

… FreeBSD

getsockopt and setsockopt on a libkernel socket knew four SOL_SOCKET options
(SO_REUSEADDR, SO_BROADCAST, SO_SNDBUF, SO_RCVBUF) and failed every other
one with ENOPROTOOPT. The Mound's EOS SDK reads SO_ERROR on two of its
sockets after a connect, which therefore always failed.

The SOL_SOCKET options now follow FreeBSD 11.4 sosetopt and sogetopt:
- SO_ERROR returns the pending error of the socket as a FreeBSD errno and
  reports it once (Winsock does not clear it on read, so the library does).
- SO_TYPE, SO_ACCEPTCONN, SO_KEEPALIVE, SO_DONTROUTE, SO_OOBINLINE and
  SO_LINGER are read from and set on the host socket; the boolean options
  read back as their own bit, as FreeBSD returns so_options & option.
- SO_DEBUG, SO_USELOOPBACK, SO_REUSEPORT, SO_TIMESTAMP and SO_NOSIGPIPE stay
  ENOPROTOOPT until a title needs one.
- A value is read from its first int and fails with EINVAL when shorter, a
  getsockopt buffer is filled up to its length (sooptcopyin, sooptcopyout),
  SO_SNDBUF and SO_RCVBUF below 1 fail with EINVAL, and SO_LINGER outside
  0..65535 seconds fails with EDOM.

A connect on a non-blocking socket now fails with EINPROGRESS on Windows too
(it was EWOULDBLOCK, which a title waiting for the connection does not
expect).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@p3n1cillin

Copy link
Copy Markdown

Title evidence for this PR: PS5MC (PPSA99420, Kodi 22, catalog release) on Windows 11, MinGW GCC 15.2.0, Release, on main 3faa9cb40 merged with the open PRs it needs to start (#4379, #4374, #4383, #5183, #5184, #5185, #5186).

  • Without this PR, Kodi's connectivity check fails about 10 ms after each connect: gdb shows sceNetResolverStartNtoa resolving www.msftconnecttest.com and www.w3.org (2.21.64.155 and 104.18.23.19), then libkernel connect to port 80, and Kodi logs CCurlFile::Exists - <http://www.w3.org/> Failed: Could not connect to server(7) (the same for msftconnecttest.com). That is curl reading the non-blocking connect's result through getsockopt(SO_ERROR).
  • With this PR merged too, the checks no longer fail: no CCurlFile errors in the same 60 s run.

AI-assisted (Claude Code).

@coltken95

Copy link
Copy Markdown

Still needed on main 842766d: without this PR Nuvio Max'd (PPSA99288) shows "Failed to connect to v3-cinemeta.strem.io port 443" (tested on main 0f2683c, CachyOS, RTX 2070). With d748c659e (this PR cherry-picked onto 842766d) the addons and all 8 home catalogs load. The cherry-pick applies cleanly.

AI-assisted: yes (Claude Code)

MarcZX10 pushed a commit to MarcZX10/AnyPS5 that referenced this pull request Oct 13, 2026
Titles that bundle OpenSSL load the console's CA file from the sandbox
directory and fail every HTTPS request without it. ProsperoRadio
(PPSA99001, libcurl 8.18.0 + OpenSSL 3.5.2) on main + boykopovar#4796 + boykopovar#4525:

  [ProsperoRadio][http] https://all.api.radio-browser.info/json/servers: Problem with the SSL CA cert (path? access rights?) (error adding trust anchors from file: /gkbHtXc3Wq/common/cert/CA_LIST.cer)

SwanStation (PPSA98510) stops on the same file (boykopovar#5001).

The guest path resolver now accepts exact-path providers, and libkernel
registers one for /<sceKernelGetFsSandboxRandomWord()>/common/cert/CA_LIST.cer.
On first use it writes a PEM bundle once per process to
cert_cache/CA_LIST.cer in the run directory, read-only: on Windows the
current user's ROOT and CA system stores, elsewhere SSL_CERT_FILE or the
first distro bundle that exists. Without a certificate the path keeps
resolving under the guest root and does not exist, as before. Every other
path under the sandbox word is unchanged. The console file's format and
the rest of its common/cert directory are unverified and recorded in
docs/dev/debt/libkernel.md.

With this change, boykopovar#4796, boykopovar#4525 and boykopovar#5025 on Windows 11, 60 s each:

  [ProsperoRadio][http] 11846160 bytes in 1458 ms: 1161 passes, 803 waits (679 woken) for 1428 ms
  [ProsperoRadio][catalog] page offset=0 bytes=11846160 objects=10000 accepted=9549 total=9549
  ...
  [ProsperoRadio][catalog] page offset=60000 bytes=1712035 objects=1494 accepted=1436 total=59371

  [SwanStationPS5] update: latest 1.2.5, this 1.1.3, available
  [SwanStationPS5] gamedb: index written

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

xXPBSXx commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for the validations and title traces, @screamm, @p3n1cillin and @coltken95. They are quoted in the description (ProsperoRadio and its group, PS5MC, Nuvio Max'd).

On the mismatches mentioned in the comments: fcntl(F_SETFD) on a socket is #5020. TCP_NODELAY, SO_NOSIGPIPE and option 0x1200 are not part of this PR; the maintainer's comment above asks to leave options out until a title needs them, and screamm's ProsperoRadio trace is the evidence for those three, so they would be a separate change if wanted.

@coltken95, my earlier question about which setsockopt calls the Nuvio run makes still stands; screamm's list is from ProsperoRadio, not from Nuvio. It is not needed for this PR.

AI-assisted (Claude Code).

@oneandonlydean
oneandonlydean merged commit 9b6555b into boykopovar:main Oct 14, 2026
14 of 15 checks passed
santhoshkumar-dev pushed a commit to santhoshkumar-dev/AnyPS5 that referenced this pull request Oct 14, 2026
Titles that bundle OpenSSL load the console's CA file from the sandbox
directory and fail every HTTPS request without it. ProsperoRadio
(PPSA99001, libcurl 8.18.0 + OpenSSL 3.5.2) on main + boykopovar#4796 + boykopovar#4525:

  [ProsperoRadio][http] https://all.api.radio-browser.info/json/servers: Problem with the SSL CA cert (path? access rights?) (error adding trust anchors from file: /gkbHtXc3Wq/common/cert/CA_LIST.cer)

SwanStation (PPSA98510) stops on the same file (boykopovar#5001).

The guest path resolver now accepts exact-path providers, and libkernel
registers one for /<sceKernelGetFsSandboxRandomWord()>/common/cert/CA_LIST.cer.
On first use it writes a PEM bundle once per process to
cert_cache/CA_LIST.cer in the run directory, read-only: on Windows the
current user's ROOT and CA system stores, elsewhere SSL_CERT_FILE or the
first distro bundle that exists. Without a certificate the path keeps
resolving under the guest root and does not exist, as before. Every other
path under the sandbox word is unchanged. The console file's format and
the rest of its common/cert directory are unverified and recorded in
docs/dev/debt/libkernel.md.

With this change, boykopovar#4796, boykopovar#4525 and boykopovar#5025 on Windows 11, 60 s each:

  [ProsperoRadio][http] 11846160 bytes in 1458 ms: 1161 passes, 803 waits (679 woken) for 1428 ms
  [ProsperoRadio][catalog] page offset=0 bytes=11846160 objects=10000 accepted=9549 total=9549
  ...
  [ProsperoRadio][catalog] page offset=60000 bytes=1712035 objects=1494 accepted=1436 total=59371

  [SwanStationPS5] update: latest 1.2.5, this 1.1.3, available
  [SwanStationPS5] gamedb: index written

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
adriadam10 pushed a commit to xXPBSXx/AnyPS5 that referenced this pull request Oct 14, 2026
fcntl on a guest socket handled F_GETFL and F_SETFL only and failed every other command with EINVAL, so libcurl's fcntl(fd, F_SETFD, FD_CLOEXEC) on a new socket failed (ProsperoRadio PPSA99001, traced by screamm on boykopovar#4796). Record FD_CLOEXEC per socket descriptor: F_SETFD sets or clears it, F_GETFD reads it, SOCK_CLOEXEC sets it at creation, and dup, dup2 and close start without it, as FreeBSD's kern_fcntl and kern_dup do.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
santhoshkumar-dev pushed a commit to santhoshkumar-dev/AnyPS5 that referenced this pull request Oct 14, 2026
Titles that bundle OpenSSL load the console's CA file from the sandbox
directory and fail every HTTPS request without it. ProsperoRadio
(PPSA99001, libcurl 8.18.0 + OpenSSL 3.5.2) on main + boykopovar#4796 + boykopovar#4525:

  [ProsperoRadio][http] https://all.api.radio-browser.info/json/servers: Problem with the SSL CA cert (path? access rights?) (error adding trust anchors from file: /gkbHtXc3Wq/common/cert/CA_LIST.cer)

SwanStation (PPSA98510) stops on the same file (boykopovar#5001).

The guest path resolver now accepts exact-path providers, and libkernel
registers one for /<sceKernelGetFsSandboxRandomWord()>/common/cert/CA_LIST.cer.
On first use it writes a PEM bundle once per process to
cert_cache/CA_LIST.cer in the run directory, read-only: on Windows the
current user's ROOT and CA system stores, elsewhere SSL_CERT_FILE or the
first distro bundle that exists. Without a certificate the path keeps
resolving under the guest root and does not exist, as before. Every other
path under the sandbox word is unchanged. The console file's format and
the rest of its common/cert directory are unverified and recorded in
docs/dev/debt/libkernel.md.

With this change, boykopovar#4796, boykopovar#4525 and boykopovar#5025 on Windows 11, 60 s each:

  [ProsperoRadio][http] 11846160 bytes in 1458 ms: 1161 passes, 803 waits (679 woken) for 1428 ms
  [ProsperoRadio][catalog] page offset=0 bytes=11846160 objects=10000 accepted=9549 total=9549
  ...
  [ProsperoRadio][catalog] page offset=60000 bytes=1712035 objects=1494 accepted=1436 total=59371

  [SwanStationPS5] update: latest 1.2.5, this 1.1.3, available
  [SwanStationPS5] gamedb: index written

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
RINNECODER added a commit to RINNECODER/AnyPS5 that referenced this pull request Oct 15, 2026
* feat(libkernel): implement sigsuspend on the pending-signal set

(cherry picked from commit b3d850d)

* docs(libkernel): record sigsuspend 32-signal mask limit

(cherry picked from commit 38e2db3)

* feat(libkernel): share pause wait loop and _sigprocmask restore in sigsuspend

(cherry picked from commit 1ce5c2c)

* feat(libkernel): EVFILT_VNODE on macOS through the host kqueue

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 84bb450)

* feat(libkernel): implement mincore

Implement mincore (+hFjyohmOU0) in libkernel following FreeBSD 11's sys_mincore (sys/vm/vm_mmap.c). Validate page alignment, return EFAULT for null vector, check range bounds and residency via sceKernelVirtualQuery, and report MINCORE_INCORE (1) for resident pages. Required by a Java 25 homebrew title.

(cherry picked from commit 7781eda)

* fix(libkernel): round mincore address down as FreeBSD does

Follow FreeBSD 11's sys_mincore by rounding unaligned address down to page boundary rather than returning EINVAL. Update guest memory test and document unmodeled residency/referenced/modified bits in libkernel technical debt.

(cherry picked from commit 3699eff)

* fix(libkernel): follow kern_mincore order and report reserved pages as not resident

Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 2854d51)

* feat(libc): add optional log timestamps

(cherry picked from commit 2cc4999)

* fix(libkernel): accept MSG_DONTWAIT in the socket send and receive calls

send, sendto, sendmsg, recv, recvfrom and recvmsg failed with EOPNOTSUPP
for MSG_DONTWAIT (0x80), so code that polls a blocking socket with it
got an error on every call instead of its data or EWOULDBLOCK.

FreeBSD 11.4 defines the flag in sys/sys/socket.h:

	#define	MSG_DONTWAIT	0x80		/* this message should be nonblocking */

and soreceive_generic() (sys/kern/uipc_socket.c) treats it as a
non-blocking socket for that one call:

		if ((so->so_state & SS_NBIO) ||
		    (flags & (MSG_DONTWAIT|MSG_NBIO))) {
			SOCKBUF_UNLOCK(&so->so_rcv);
			error = EWOULDBLOCK;
			goto release;

On Linux and macOS the flag now maps to the host's MSG_DONTWAIT, next to
MSG_PEEK on a receive and MSG_NOSIGNAL on a send. Winsock has no
per-call non-blocking flag, so Windows keeps failing with EOPNOTSUPP
for it; TechnicalDebt records that. Other flags still fail with
EOPNOTSUPP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 7afd889)

* fix(libkernel): answer fcntl status flags on libSceNet sockets

With libSceNet sockets on ordinary descriptor numbers (boykopovar#4391), a title
that makes a libSceNet socket non-blocking with plain fcntl(F_GETFL) and
fcntl(F_SETFL, O_NONBLOCK) reached File::Fcntl, whose classification
throws for a socket. libSceNet now registers a status control with
libkernel, like its option setter: F_GETFL returns O_RDWR with the
socket's non-blocking flag, F_SETFL sets it through SO_NBIO, as fcntl
already does for libkernel sockets. A homebrew title stopped
here on Linux.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017WeVmjnnPPKdxVTpr2Vjtw
(cherry picked from commit 5dcf28d)

* fix(libc): preserve path services through process exit

(cherry picked from commit 99ae2e8)

* fix(memory): preserve process-owned services during exit

(cherry picked from commit d50eed2)

* fix(libkernel): keep host standard streams open on guest close

Guest descriptors are host C runtime descriptors. Closing descriptor 0, 1
or 2 closed the host process's standard stream, and the runtime then gave
that number to the next file opened anywhere in the process, including
files libkernel opens itself, such as the APR reader's. The title's next
close of the same number closed that file mid-read.

close and sceKernelClose now fail with EBADF for descriptors 0 to 2 that
were open when libkernel loaded, and leave them open. A standard
descriptor that was not open at load can still be opened and closed by
the guest.

A title calls sceKernelClose(0) repeatedly while loading asset
bundles, which made APR reads return 0 or a part of the requested bytes
and raised UCRT invalid parameter reports from close.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 8029428)

* fix(libkernel): poll libSceNet sockets with the host wait

With libSceNet sockets on ordinary descriptor numbers (boykopovar#4391), poll on a
libSceNet socket threw "neither a socket nor a pipe": poll only knew
libkernel sockets and pipes, while select already asks libSceNet through
its poller. libSceNet now lends libkernel the host socket behind a
descriptor, together with an owner that keeps it open, so poll puts it in
the same WSAPoll or poll call as libkernel sockets. A homebrew title
stopped here on Linux and now reaches its PC list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EvDbpwUPTa78oCAJqe7m9K
(cherry picked from commit 62d8eea)

* fix(libkernel): answer F_GETFD and F_SETFD on a guest socket

fcntl on a guest socket handled F_GETFL and F_SETFL only and failed every other command with EINVAL, so libcurl's fcntl(fd, F_SETFD, FD_CLOEXEC) on a new socket failed (seen in a homebrew title, boykopovar#4796). Record FD_CLOEXEC per socket descriptor: F_SETFD sets or clears it, F_GETFD reads it, SOCK_CLOEXEC sets it at creation, and dup, dup2 and close start without it, as FreeBSD's kern_fcntl and kern_dup do.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 84aa234)

* fix(libkernel): fail sceKernelMprotect over an unmapped range with EINVAL instead of aborting

The allocation tracker threw a runtime_error (a hole in the protected range) from inside
sceKernelMprotect, which terminated the title. mprotect already turned the exception into
EINVAL. sceKernelMprotect and sceKernelMtypeprotect now check that the range is registered
first and return EINVAL; the retype of direct mappings is skipped on failure.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 05ffb45)

* fix(libkernel): fall back below a mapping hint with no room above it

A mapping without MAP_FIXED treated its address as the lowest place it
could go, and a hint at or above the end of the guest arena threw on the
host. FreeBSD 11.4's vm_mmap_object places such mappings with
vm_map_find_min, which takes the address as a hint only: it tries at or
above it, then again from its minimum address.

GuestArenaAllocateFromHint does the same in the arena: at or above the
hint when it lies below the arena's end and a range fits there, otherwise
from the arena's start. MapPlaced and the Windows mmap_aligned use it for
hinted mappings; GuestArenaAllocateAtOrAbove keeps its meaning for the
callers that need a range above an address.

A homebrew title reserves its JIT range with
sceKernelReserveVirtualRange and a hint near its own code, a host address
above the arena here, and aborted with "mapping address hint is above the
guest address space arena" when launching a 3DS title.

(cherry picked from commit a016b4f)

* docs(debt): note the fallback of mapping hints with no room above them

(cherry picked from commit aca7a3a)

* fix(libkernel): check every part of an mprotect range for pins before protecting the first

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 238fe1e)

* fix(libkernel): give an inheriting thread its creator's priority

Measured on a PS5 (firmware 13.60): a thread created with
PTHREAD_INHERIT_SCHED, a null attribute or a default attribute runs at
its creator's priority, whatever priority the attribute holds; with
explicit scheduling it takes the attribute's priority.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 143eb6a)

* fix(libkernel): convert local time with the host time zone

sceKernelConvertUtcToLocaltime and sceKernelConvertLocaltimeToUtc returned
the time unchanged while libc localtime follows the host zone, so a title
that mixes them shows UTC as local time (boykopovar#5067).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017iLxaSz4qjmirfqKZdhVQk
(cherry picked from commit 56c8f34)

* fix(libkernel): report west_sec as seconds east of UTC

A homebrew emulator documents that the console fills west_sec with the
offset east of UTC despite the field's name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017iLxaSz4qjmirfqKZdhVQk
(cherry picked from commit ae7f5e3)

* fix(libkernel): call tzset before reading the host zone offsets

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017iLxaSz4qjmirfqKZdhVQk
(cherry picked from commit 1614d82)

* fix(libkernel): report the zone's daylight saving amount from sceKernelConvertLocaltimeToUtc

libc mktime subtracts it when tm_isdst is set outside summer time, so it
must not be zero there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017iLxaSz4qjmirfqKZdhVQk
(cherry picked from commit 15969f2)

* fix(libSceSystemService): end LoadExec exit without running guest exit handlers

(cherry picked from commit 2dd7f2b)

* feat(libc): lazily initialize application heap on first access

(cherry picked from commit cee2006)

* fix(libkernel): name the failing vnode watch in the kqueue notification error

The Windows error for a refused FindFirstChangeNotification was one string shared
by WatchParent and WatchContents, so the log could not tell which handle or path
was denied. Build it from the watcher, the path the API was given and
GetLastError instead.

Still throws: the EVFILT_VNODE attach path is vfs_kqfilter, which returns only
EINVAL, ENOMEM or 0, with EBADF from fget before it, and consults no directory
permission, so no FreeBSD errno stands for a host that refuses to notify.

(cherry picked from commit e9d3677)

* docs(debt): keep title IDs out of the ported libkernel debt lines

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj

* fix(libkernel): keep the reused-page table on Darwin after the exit-lifetime change

The picked lifetime change moved the physical-used table into the process
state behind a Linux-only guard. This fork's direct memory zeroes reused
pages on Darwin as well, so the table and its accessor need the same guard
as ZeroReusedPhysical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj

* test(libc): do not register the lazy application-heap test on macOS

The lazy mode needs process parameters that ApplicationHeap.cpp installs
only on Windows and Linux, so on macOS the test fails deterministically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj

---------

Co-authored-by: Kurenaiiiii <Kurenaiiiii@users.noreply.github.com>
Co-authored-by: Oleksandr Sierov <50950970+OleksandrS23@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Amirtheshwaran Navaneethakrishnan <anavane1@charlotte.edu>
Co-authored-by: Adrià Franch <adriadam10@gmail.com>
Co-authored-by: MarcZX100 <marczxrr@gmail.com>
Co-authored-by: milescoviello <kitslayer29@gmail.com>
Co-authored-by: Nicolas Falesy <112726756+nicolasfalesy@users.noreply.github.com>
Co-authored-by: krishalivaibhav <krishalivaibhav@gmail.com>
Co-authored-by: SynnZer0 <243459655+SynnZer0@users.noreply.github.com>
Co-authored-by: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com>
Co-authored-by: Yossi Yadgar <yadgar360@gmail.com>
Co-authored-by: hugodearaujo <hugoinjustice@gmail.com>
Co-authored-by: David-cmrg <202826622+David-cmrg@users.noreply.github.com>
Co-authored-by: hamb3r <hamb3r>
Co-authored-by: Rujul Talekar <113801876+Roojool@users.noreply.github.com>
santhoshkumar-dev pushed a commit to santhoshkumar-dev/AnyPS5 that referenced this pull request Oct 15, 2026
Titles that bundle OpenSSL load the console's CA file from the sandbox
directory and fail every HTTPS request without it. ProsperoRadio
(PPSA99001, libcurl 8.18.0 + OpenSSL 3.5.2) on main + boykopovar#4796 + boykopovar#4525:

  [ProsperoRadio][http] https://all.api.radio-browser.info/json/servers: Problem with the SSL CA cert (path? access rights?) (error adding trust anchors from file: /gkbHtXc3Wq/common/cert/CA_LIST.cer)

SwanStation (PPSA98510) stops on the same file (boykopovar#5001).

The guest path resolver now accepts exact-path providers, and libkernel
registers one for /<sceKernelGetFsSandboxRandomWord()>/common/cert/CA_LIST.cer.
On first use it writes a PEM bundle once per process to
cert_cache/CA_LIST.cer in the run directory, read-only: on Windows the
current user's ROOT and CA system stores, elsewhere SSL_CERT_FILE or the
first distro bundle that exists. Without a certificate the path keeps
resolving under the guest root and does not exist, as before. Every other
path under the sandbox word is unchanged. The console file's format and
the rest of its common/cert directory are unverified and recorded in
docs/dev/debt/libkernel.md.

With this change, boykopovar#4796, boykopovar#4525 and boykopovar#5025 on Windows 11, 60 s each:

  [ProsperoRadio][http] 11846160 bytes in 1458 ms: 1161 passes, 803 waits (679 woken) for 1428 ms
  [ProsperoRadio][catalog] page offset=0 bytes=11846160 objects=10000 accepted=9549 total=9549
  ...
  [ProsperoRadio][catalog] page offset=60000 bytes=1712035 objects=1494 accepted=1436 total=59371

  [SwanStationPS5] update: latest 1.2.5, this 1.1.3, available
  [SwanStationPS5] gamedb: index written

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
santhoshkumar-dev pushed a commit to santhoshkumar-dev/AnyPS5 that referenced this pull request Oct 17, 2026
Titles that bundle OpenSSL load the console's CA file from the sandbox
directory and fail every HTTPS request without it. ProsperoRadio
(PPSA99001, libcurl 8.18.0 + OpenSSL 3.5.2) on main + boykopovar#4796 + boykopovar#4525:

  [ProsperoRadio][http] https://all.api.radio-browser.info/json/servers: Problem with the SSL CA cert (path? access rights?) (error adding trust anchors from file: /gkbHtXc3Wq/common/cert/CA_LIST.cer)

SwanStation (PPSA98510) stops on the same file (boykopovar#5001).

The guest path resolver now accepts exact-path providers, and libkernel
registers one for /<sceKernelGetFsSandboxRandomWord()>/common/cert/CA_LIST.cer.
On first use it writes a PEM bundle once per process to
cert_cache/CA_LIST.cer in the run directory, read-only: on Windows the
current user's ROOT and CA system stores, elsewhere SSL_CERT_FILE or the
first distro bundle that exists. Without a certificate the path keeps
resolving under the guest root and does not exist, as before. Every other
path under the sandbox word is unchanged. The console file's format and
the rest of its common/cert directory are unverified and recorded in
docs/dev/debt/libkernel.md.

With this change, boykopovar#4796, boykopovar#4525 and boykopovar#5025 on Windows 11, 60 s each:

  [ProsperoRadio][http] 11846160 bytes in 1458 ms: 1161 passes, 803 waits (679 woken) for 1428 ms
  [ProsperoRadio][catalog] page offset=0 bytes=11846160 objects=10000 accepted=9549 total=9549
  ...
  [ProsperoRadio][catalog] page offset=60000 bytes=1712035 objects=1494 accepted=1436 total=59371

  [SwanStationPS5] update: latest 1.2.5, this 1.1.3, available
  [SwanStationPS5] gamedb: index written

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
RINNECODER added a commit to RINNECODER/AnyPS5 that referenced this pull request Oct 17, 2026
…hroughput (#530)

* fix(libSceNet): read SO_RCVTIMEO and SO_SNDTIMEO as a timeval in sceNetSetsockopt

sceNetSetsockopt took the BSD timeout options, SO_SNDTIMEO (0x1005) and
SO_RCVTIMEO (0x1006), like Sony's 0x1105 and 0x1106: as an int of
microseconds read from the first four bytes. A caller passing a struct
timeval got tv_sec taken as microseconds, so {0, 100000} meant to wait
100 ms became 0 and disabled the timeout, and the receive blocked until
data came.

On the console 0x1006 takes a timeval, as FreeBSD 11.4's sosetopt does.
ProsperoLight (PPSA99002) gives it {0, 100000} through sceNetSetsockopt,
and its log from a console (firmware 12.20) shows Moonlight's receive
timing out every 100 ms before the first packet ("Received first video
packet after 400 ms"). Sony moved only these two timeval options to
0x1105 and 0x1106, as ints. On AnyPS5 the receive never times out, so
ProsperoLight's receive threads never wake when the PC stops sending and
LiStopConnection never returns: the stream screen freezes when a session
ends from the PC side. Steam Remote Play (PPSA99350) sets {0, 10000} the
same way and stays on "THE PC DID NOT ANSWER" when the PC doesn't reply.

0x1005 and 0x1006 now go through the timeval path that setsockopt() on a
libSceNet socket already uses (EINVAL for a value shorter than a timeval,
EDOM for a negative or out-of-range one). sceNetGetsockopt gives them
back as a timeval cut to the caller's length, as FreeBSD's sooptcopyout
does. 0x1105 and 0x1106 keep taking an int of microseconds.

* docs(debt): sync debt documentation structure with upstream

* fix(libSceNet): let a datagram receive on an unbound socket wait on Windows

sceNetRecv, sceNetRecvfrom and sceNetRecvmsg called Winsock's receive
straight away. Winsock fails a receive on a socket with no local address
with WSAEINVAL, so on Windows the call returned EINVAL at once. On the
console the receive waits: FreeBSD 11.4's soreceive_dgram sleeps on the
receive buffer whether or not the socket has an address, returns
EWOULDBLOCK for a non-blocking socket, and a send from another thread
binds the socket so the reply can arrive.

Steam Remote Play (PPSA99350) relies on that. Its stream library opens
its discovery socket, starts receiving on a worker thread, and binds the
socket only when another thread sends the discovery broadcast. On
Windows the worker got EINVAL, took it as fatal and closed the socket
before the broadcast went out, so the title never found a PC running
Steam.

On Windows a receive on a datagram socket with no local address now
waits until a bind, connect or send gives it one, checking every 10 ms.
A non-blocking socket fails with EAGAIN at once, and the receive timeout
ends the wait with EAGAIN. An abort or close ends the wait, and the host
receive then fails as before. Once the socket has an address it is noted
as bound, so later receives skip the check, and a later sceNetBind fails
with EINVAL as it does on the console.

* fix(shader): flush v_cvt_pknorm_*_f16 denormal inputs by FLOAT_MODE

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA

* refactor(libSceNet): wait for a local address after sceNetRecvmsg's socket type check

* feat(libSceAppContent): implement sceAppContentGetAddcontDownloadProgress

The signature (uint32_t service label, const NpUnifiedEntitlementLabel*,
progress*) follows SharpProspero Interop/AppContent/AppContent.cs.
dlc_emu dlc_content.cpp (fcf69b16) answers PARAMETER for a null label or
progress and NOT_FOUND for a label that is not installed additional
content. No additional content is ever installed here, as
sceAppContentAddcontMount, sceAppContentAddcontEnqueueDownload and
sceAppContentAddcontDelete already model, so every other call answers
NOT_FOUND and the progress, whose 16-byte layout SharpProspero gives,
is never written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(shader): intersect dominator sets as bitsets

Structurizing recomputes dominators after every CFG rewrite, and each block started from a vector of every block id. The same fixed point now runs on bitsets and still writes the sorted id lists.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(relinker): run the file writer's replacement test on a copy of itself

The running-executable replacement test copied /bin/sleep under the
names "running" and "leftover" and started the copy. On Ubuntu 26.04
/bin/sleep is the uutils multi-call coreutils binary, which picks the
utility from the file name of the path it was started through, not
from argv[0], so the copy printed "coreutils: unknown program
'running'" and exited, and file_writer_binary and file_writer_text
failed without ever replacing a running executable. The test now
copies its own executable and starts it with a "wait" argument that
sleeps, so it no longer depends on how a system binary reads its name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ajm): honor Opus gapless resets

* fix(ajm): preserve live decoder handles across ID wrap

* feat(libSceNpPartner001): answer sceNpHasEAAccessSubscription without an EA Access server

Co-authored-by: Cursor <cursoragent@cursor.com>

* perf(libSceSysmodule): cache the unwind region of loaded images on Windows

sceSysmoduleGetModuleInfoForUnwind called VirtualQuery and GetMappedFileNameA on every call. VirtualQuery on a large image section costs time proportional to the section (2 us for 16 KiB, 51 us for 8 MiB, 167 us for 32 MiB), and a title that unwinds exceptions while loading calls it for every frame of every throw.

The region and file name of an address inside an image of the Windows loader's module list are kept per region and used while the loader still reports the same image (base, time stamp and size). Other addresses are queried every time.

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

* feat(opus): decode at 8, 12, 16 and 24 kHz

Accept every creation rate listed by the public decoder research in
libSceOpusDec and libSceOpusCeltDec, resampling FFmpeg's 48 kHz output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(libc): raise logic_error::_Doraise and _Xruntime_error

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(relinker): preserve weak imports in Linux guest modules

* fix(libSceNetCtl): answer the DHCP host name, PPPoE name and proxy server as empty strings

Since boykopovar#5056, sceNetCtlGetInfo throws for every info code it does not
model. Ghost of Yotei (PPSA26344) asks for code 12, the DHCP host name,
during boot and now aborts one second in.

The configuration boykopovar#5056 reports (wired, DHCP, HTTP proxy off) sets no
DHCP host name, no PPPoE user name and no proxy server, so codes 12, 13
and 20 now return 0 with an empty string. The Wi-Fi codes (5-10) and
the proxy port (21) still throw. GuestNetCtl checks the three empty
answers and keeps the throw check for the rest; the libSceNetCtl debt
line says the same.

* feat(libSceAmpr): implement __cxa_call_unexpected

__cxa_call_unexpected takes the in-flight exception's unwind header and
never returns, as in libcxxabi. No std::set_unexpected is exported and
the exception header's unexpected handler is never set, so the handler
is always the default one, which calls std::terminate. It therefore
forwards to libc's __cxa_call_terminate, which begins the catch and runs
the current terminate handler; a null header terminates in libc's
__cxa_begin_catch. libcxxabi's std::terminate would instead run the
terminate handler saved in the exception header at throw time; the two
differ only if std::set_terminate was called in between, which the debt
entry records.

The test shares the forked-child helper of the std::_Pad test, which now
takes the terminate handler to install.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(relinker): guard TLS instruction byte access at EOF

* feat(libSceLoginService): let PPSA34015 load without a missing module

FC 27 stops on Windows with Failed to load module / error 126 because this PRX is absent. Export Initialize and Terminate as no-ops and keep RequestDevices throwing until the buffer layout is known.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(libSceSysmodule): report the region of the queried page from a cached unwind region

VirtualQuery describes the pages from the queried address to the end of the run of equal pages, so the reported seg0 begins at the queried page. The cached entry returned the first lookup's page for every later address in the run. It now returns the queried page and the bytes up to the end of the cached run, which is what VirtualQuery returns.

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

* test(libSceSysmodule): look up an earlier page of a cached unwind region after a later one

The backward lookup added with the region fix compared the address with a page it could not reach and never ran. The test now queries an ntdll address three pages in, then the first page, then the pages between, and compares each with VirtualQuery. The lookups after the first fail on the cache before the region fix.

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

* docs(input): list the default keyboard and mouse bindings

INPUT_MAPPING.md names the actions and says that omitted actions keep their built-in bindings, but not what those bindings are. The table is read from InputMapping in core/libs/prx/libScePad/include/InputMapping.hpp.

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

* test(libc): keep intentional aborts away from the system crash handler

guest_host_standard_error and exception_runtime fork children that end in
SIGABRT on purpose. When kernel.core_pattern pipes cores to a handler, as
Ubuntu's apport and systemd-coredump do, the kernel starts it for every
dumpable crash even with RLIMIT_CORE at 0. apport takes about 1.1 s per
crash here, so guest_host_standard_error spent 2.3 s waiting and timed out
at its 10 s limit under parallel ctest load. Marking those children
non-dumpable on Linux skips the handler: 2.29 s to 0.01 s and
1.17 s to 0.01 s, with the same SIGABRT and message checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(libkernel): support EVFILT_READ on the read end of a pipe

kevent threw for any descriptor that is not a guest socket. The read end
of a pipe now gets the descriptor knote, following FreeBSD 11's
filt_piperead: data is the number of buffered bytes and EV_EOF is set
once the write end is closed. GuestSockets::Ready falls back to the new
PipeReadState for a descriptor that is not a socket, and WaitAny waits on
the pipe with ppoll on Linux or in 1 ms WSAPoll slices on Windows, only
when the queue holds a pipe. On Windows the equeue waker now starts
Winsock itself, since a queue can wait on a pipe before any socket exists.

* feat(libkernel): add sceCoredumpConfigDumpMode as a throwing stub

PPSA16833 imports it from libkernel, and its executable does not load
while no library exports the NID. The signature is unknown, so it throws
if called, like sceCoredumpWriteUserString.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(libkernel): share the Windows pipe query between poll and kevent

PipeReadiness and PipeReadState both opened the pipe handle and asked
NtQueryInformationFile for its access mask and local information. Both
now go through QueryPipe, which keeps each caller's error prefix.

* docs(build): describe the [gputime] line and its command classes

* fix(libkernel): fail connect on a listening socket with EOPNOTSUPP

FreeBSD's soconnectat refuses a socket with SO_ACCEPTCONN set with EOPNOTSUPP (45). The host answers EINVAL on Windows and EISCONN on Linux, so connect() on a listening stream socket returned the host's error. Answer EOPNOTSUPP for a listening stream socket before the address is read.

* test(agc): wait for preparation workers before linking

* fix(libkernel): reject invalid ftruncate descriptors safely

* fix(ngs2): stop stale rendering after waveform callbacks

* fix(libkernel): use precise waits for select and poll on pipes on Windows

On Windows, select over pipes polls readiness in HostPoll and sleeps
std::this_thread::sleep_for(1 ms) between rounds, and poll with pipes
but no native sockets sleeps sleep_for(slice). winpthreads ends these
sleeps on 15.6 ms timer ticks (boykopovar#1471), so a guest select or poll with a
short timeout on an idle pipe waits about 15 ms per call instead of its
timeout, and a longer wait checks the pipes only every 15.6 ms.

Both paths now sleep with PreciseSleepUntil. Each sleep requests at
most 1 ms and ends no later than the deadline; the old HostPoll sleep
could pass its deadline by a whole tick. poll on pipes also stops
spinning through the last partial millisecond before its deadline,
where the remaining time truncated to 0 ms and no sleep was made.
Infinite timeouts still sleep 1 ms per round.

The pipes are now checked about every 1.5 ms instead of every 15.6 ms:
a pipe that becomes ready is noticed sooner, and a thread blocked on an
idle pipe wakes about ten times as often. A 1 ms poll on an idle pipe
returns after about 1.5 ms instead of spinning for 1 ms.

guest_poll times 20 polls with a 2 ms timeout on an empty pipe read end
and guest_select times 20 selects with a 1 ms timeout on it; each set
must take less than 200 ms. A 1 ms poll does not reach the old sleep,
because the remaining time truncates to 0 ms and the old loop spins
until the deadline, so the poll check uses 2 ms. On Windows Server 2025
both sets took 312 ms before this change and about 50 ms and 30 ms
after it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(shader): allow 32 sampler pairs per shader

A pixel shader of MXGP 26 (PPSA30839) uses 17 samplers, over the limit
of 16 sampler pairs. The sampler heap now holds 64 descriptors, ShaderData
grows by 32 sampler metadata records and the runtime ABI version moves
to 13.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(agc): honor the GPU selector in recorder fixtures

* perf(agc): draw an indirect rect list from its records on the GPU

ClassifyIndirectDraw sent every indirect rect-list draw to the CPU path
(non-vertex path), which reads the records on the CPU. When the GPU writes
those records earlier in the frame, the read waits for every batch up to
the producer: in Kena (PPSA01802) each such DRAW_INDIRECT waited about
15 ms, about once per frame.

A rect list on the vertex path is drawn by its generated tessellation
stages from three-vertex patches, with no draw-dependent constants, so the
records can be consumed on the GPU like any vertex-path draw. The
classifier now keeps a rect list without mesh or tessellation stages on
the GPU path, and Graphics::Draw accepts it. APS5_NO_RECT_INDIRECT=1
restores the CPU path.

agc_driver_rect_list_indirect submits a direct rect-list draw and the
same rectangle from a DRAW_INDIRECT record and requires the same bytes,
and a record without vertices to draw nothing; it passes on either path.
agc_driver_draw_offset_indices requires an auto indirect rect-list record
whose packet names no offset registers to be classified to the GPU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(libSceNet): return 0 from an unbound datagram receive after shutdown or for zero bytes

On Windows a receive on a datagram socket with no local address waits
for one. A sceNetShutdown of the receive side now ends that wait, and
later receives return 0 at once; a zero-length receive returns 0 without
waiting. FreeBSD's soreceive_dgram returns 0 in both cases. Before, a
thread blocked in such a receive kept waiting after the socket was shut
down.

* test(libSceNet): receive on an unbound datagram socket that another thread binds or connects

The test covered a send that names the waiting socket. A bind and a
datagram connect from another thread name it too: each case now waits in
sceNetRecv on the unbound socket, binds or connects it, and checks that a
peer's datagram to the new address reaches the waiting receive.

* perf(libSceNet): end the wait for a local address when a bind, connect or send gives one

A receive on an unbound datagram socket checked the host socket every
10 ms, so it went on waiting up to 10 ms after another thread's bind,
connect or send had named the socket. sceNetBind now wakes the waiting
receives. On Windows a datagram connect or a send marks the socket bound
and wakes them once the host socket has its local address; a raw socket
that a send leaves without one stays unmarked and keeps waiting.

* fix(libSceNet): keep one receive timeout across the wait for a local address

When the address came during the wait, the host receive that followed
waited its whole SO_RCVTIMEO again, so a receive could take up to twice
the timeout. FreeBSD's soreceive_dgram sleeps once per timeout. Now the
receive waits for a datagram with WSAPoll only for what is left of the
timeout, and fails with EAGAIN when it runs out, as before. Nothing
changes when the socket already had an address or has no timeout.

* test(tools): add tool convention tests from upstream

* fix(tools): recognize markdown fences with CRLF endings

* fix(tools): keep duplicate export paths unquoted

* fix(tools): hide labels that do not fit treemap slivers

* docs(usage): state that the shader cache directory has no size limit

The Shader cache section says only that subdirectories of other versions are removed after 14 days. housekeeping() in ShaderDiskCache.cpp removes nothing from the current version, and no size or entry limit applies there.

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

* fix(tools): read workflow job keys at the indentation the file uses

The ci-gate check read a job's needs and if only two spaces deeper
than the job name. In a workflow indented by four spaces those keys
were never found, so a job gated with needs: eligibility and an if on
its outputs was reported as ungated. Read the keys at the indentation
of the job's first child instead.

* test(agc): wait for the second-store image itself to be pending

* test(agc): wait for pending result on the tested surface

* test(agc): cover pending surface identity in the shared wait

* docs(usage): describe the texture cache budgets and APS5_TEXTURE_CACHE_MIB

* test(agc): shut libc down before agc_shader_helpers returns

On Windows, agc_shader_helpers returned from main with the driver's
threads still running. ExitProcess ends them first, then the DLLs'
static destructors run and wait on what those threads left locked:
the async log's queue mutex in Driver::stop, DeviceUseGate's condition
variable in ~Driver, and condition variables in libkernel's statics.
The test hung after printing its result in about one ctest run of
three on a Radeon 860M (boykopovar#6791).

Like the other driver tests, it now runs LibcRunShutdown_nid_postfix
before returning, which stops the driver while its threads are alive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(libkernel): validate the address before refusing connect on a listening socket

FreeBSD reads and checks the address (getsockaddr) before soconnect refuses a listening socket with EOPNOTSUPP, so a null or short address on a listener fails with the address error. Move the listener check after the address is read and keep it ahead of the family check.

* test(agc): require the GPU route for the indirect rect-list draws

agc_driver_rect_list_indirect compared the indirect draws' output with
the direct draws only, so it also passed when the records were read on
the CPU. Its records lived in a static array that is not guest memory,
so every indirect draw fell back to the CPU path as not imported.

The records now sit in a registered 64 KiB guest allocation, the test
turns on APS5_PROFILE_DRAW and reads the indirect-draw counters through
the new Graphics::IndirectDrawCounts, and it requires all four indirect
draws on the GPU path. It fails with APS5_NO_RECT_INDIRECT=1 and with
the IndirectDraw.cpp and Draw.cpp changes reverted (4 non-vertex path).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(libc): print the text that registers point at in Windows crash reports

Unreal titles end in their own libc fatal path with the UTF-16
LowLevelFatalError message in a register, and the crash report shows
only the first 64 bytes of it as hex words, so the message is lost
(boykopovar#4238: Demon Slayer PPSA03490, Stray PPSA02100).

For every general register that points at readable memory starting
with at least four printable ASCII characters, as UTF-16LE or as single
bytes, the report now prints the text up to a NUL, the first other
character, 512 characters or the end of readable memory (each page is
checked with VirtualQuery before it is read).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(libSceVdecsw): block on SyncDecodeInput/Output like TrySync

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(libSceNpManager): export two unknown NIDs so PPSA34015 can load

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(libkernel): serve the console CA list from the host trust store

Titles that bundle OpenSSL load the console's CA file from the sandbox
directory and fail every HTTPS request without it. ProsperoRadio
(PPSA99001, libcurl 8.18.0 + OpenSSL 3.5.2) on main + boykopovar#4796 + boykopovar#4525:

  [ProsperoRadio][http] https://all.api.radio-browser.info/json/servers: Problem with the SSL CA cert (path? access rights?) (error adding trust anchors from file: /gkbHtXc3Wq/common/cert/CA_LIST.cer)

SwanStation (PPSA98510) stops on the same file (boykopovar#5001).

The guest path resolver now accepts exact-path providers, and libkernel
registers one for /<sceKernelGetFsSandboxRandomWord()>/common/cert/CA_LIST.cer.
On first use it writes a PEM bundle once per process to
cert_cache/CA_LIST.cer in the run directory, read-only: on Windows the
current user's ROOT and CA system stores, elsewhere SSL_CERT_FILE or the
first distro bundle that exists. Without a certificate the path keeps
resolving under the guest root and does not exist, as before. Every other
path under the sandbox word is unchanged. The console file's format and
the rest of its common/cert directory are unverified and recorded in
docs/dev/debt/libkernel.md.

With this change, boykopovar#4796, boykopovar#4525 and boykopovar#5025 on Windows 11, 60 s each:

  [ProsperoRadio][http] 11846160 bytes in 1458 ms: 1161 passes, 803 waits (679 woken) for 1428 ms
  [ProsperoRadio][catalog] page offset=0 bytes=11846160 objects=10000 accepted=9549 total=9549
  ...
  [ProsperoRadio][catalog] page offset=60000 bytes=1712035 objects=1494 accepted=1436 total=59371

  [SwanStationPS5] update: latest 1.2.5, this 1.1.3, available
  [SwanStationPS5] gamedb: index written

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

* fix(libkernel): filter sandbox TLS roots and preserve path overrides

* test(agc): map GPU memory on fibers with and without a host stack

Since boykopovar#6003 a fiber with a context of 64 KiB or more runs on the
context itself, so the 64 KiB fiber of agc_driver_gpu_map_on_fiber
imports its mapping on a host thread on Windows too, and the test's
Windows-only expectation of no thread failed. The test now also maps
on a 16 KiB fiber, which keeps a host stack and imports on it on
Windows, and expects one import thread for the 64 KiB fiber on every
platform.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(recompiler): hold 32 samplers in the runtime sampler heap

A pixel shader of PPSA16833 binds more than 16 samplers outside the
sampler tables. Each takes two heap entries, one per descriptor variant,
so the 32-entry heap refused it with "shader sampler pairs exceed runtime
heap capacity" and the title aborted when a new game started.

The heap now holds 64 entries (32 samplers). Its metadata array is part
of ShaderData, which grows from 14528 to 16064 bytes, still within the
16 KiB uniform buffer every Vulkan device supports; the runtime ABI
version moves to 13 so shaders cached with the old layout are rebuilt.
The overflow message names the sampler count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(metal): optimize Civ 7 128-bit detile vectors and elide non-indexed draw snapshots

* fix(tools): sync nid_names and exempt missing debt doc-links for macOS fork

---------

Co-authored-by: Yunus Shaikh <75296019+EagleBearerYS@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: KernelPanicNT <76663133+KernelPanicNT@users.noreply.github.com>
Co-authored-by: wh1trope <noxerkrul@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Niko <nikobilger2000@gmail.com>
Co-authored-by: Ondra97 <47064087+Ondra97@users.noreply.github.com>
Co-authored-by: notaxeltv <notaxeltv@gmail.com>
Co-authored-by: JAB-dev <68183649+JAB-dev@users.noreply.github.com>
Co-authored-by: snipeTR <31975916+snipeTR@users.noreply.github.com>
Co-authored-by: Zinebyte <197974009+Zinebyte@users.noreply.github.com>
Co-authored-by: Celegans12 <53881228+Celegans12@users.noreply.github.com>
Co-authored-by: MarcZX100 <marczxrr@gmail.com>
Co-authored-by: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com>
Co-authored-by: Hashaam Zafar <hashaam1217@gmail.com>
Co-authored-by: Felipe Arce <felipearce.2004@gmail.com>
Co-authored-by: Tamalero <11452991+Tamalero@users.noreply.github.com>
Co-authored-by: Josip Parlov <josipparlov@protonmail.com>
Co-authored-by: Hussein Kadhim <husseinkad.dev@gmail.com>
Co-authored-by: MarcZX100 <MarcZX100@users.noreply.github.com>
Co-authored-by: fdfawef324 <omarelfernani07@gmail.com>
Co-authored-by: Nahid Delic <nahe.delic84@gmail.com>
Co-authored-by: Romans Pokrovskis <motivated.it@gmail.com>
Co-authored-by: alfalfa-star <kosher-chive-take@duck.com>
Co-authored-by: Ishmam Ahmed <89687266+IshmamDC217@users.noreply.github.com>
Co-authored-by: LeandroLP23 <leandroplacidi@gmail.com>
Co-authored-by: screamm <davidrydgren@gmail.com>
Co-authored-by: Santhosh Kumar <santhosh-pc@outlook.com>
Co-authored-by: thuanquang <60096168+thuanquang@users.noreply.github.com>
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.

5 participants