Aps5GuestTlsAllocateBlock and Aps5GuestTlsGetAddr read the descriptor's Key
outside the registration mutex while another thread's first access could be
writing it, a data race that could also expose a key before
pthread_key_create had finished. Every access now goes through
RegisteredKey: an acquire load through std::atomic_ref on the unchanged
descriptor field, and on a zero key a double-checked creation under the mutex
that publishes with a release store.
macos_guest_tls_first_access races the first access from 16 threads, 100
rounds through GetAddr and 100 through AllocateBlock, and checks for one
non-zero key, a distinct block per thread and a stable second access.
Winsock does not answer SO_ACCEPTCONN on a datagram socket, so the
listening check returned the host error (ENOPROTOOPT). A datagram socket
can never listen, so it fails with EINVAL without asking the host, as
FreeBSD 11.4 does. Reported by @wachi35676.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On a PS5 (firmware 13.40) the builder writes SET_UCONFIG_REG 0x342 = 0xc6000005 before its DRAW_INDEX_INDIRECT_MULTI packet and 0xc6000000 after it, 64 bytes in all, and sceAgcDcbDrawIndexIndirectMultiGetSize returns 64.
A chain of table bytes that decodes to a return after a padding run looked
like trailing gap code and stopped the mask inside a real table, which hands
the rest of it back to the decoder. Truncating now needs the block to contain
a syscall, or to end where the gap turns into padding, so a 2 KiB table with
00/cc/90 runs and return-like bytes stays masked.
The only conflict is in Time.cpp with the guest timezone store of the
settimeofday change: gettimeofday reads the timezone under the mutex into
a local copy, releases it, then copies it out with the checked write.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A relinked macOS game stops in dyld before any guest code runs: libc does not
export the guest entry and TLS functions the macOS relinker's executable
imports, and AppKit only allows windows on the process main thread.
- libc: add Aps5StartGuest, which the relinker's entry stub calls with the
guest entry and the FreeBSD initial block. It runs the guest on a second
thread and keeps the main thread serving the main dispatch queue.
- libc: add the guest TLS functions the relinked code imports
(Aps5GuestTlsRegister, Aps5GuestTlsAllocateBlock, Aps5GuestTlsGetAddr,
Aps5GuestTlsUseXsave).
- libc: MainThread.hpp's RunOnMainThread runs a call on the main thread,
waits for it and rethrows its exception. It runs on the calling thread
when the main thread does not serve the main queue, as in the tests.
- libSceVideoOut: create, resize, retitle and destroy the window, initialise
SDL video, create the Vulkan surface and pump events through
RunOnMainThread on macOS.
- libc: find the process parameters through dl_iterate_phdr on macOS too,
and link CoreFoundation.
- relinker: leave 0x400 bytes after the Mach-O load commands, like ld's
-headerpad, so codesign's LC_CODE_SIGNATURE no longer overwrites the
entry stub.
Linux and Windows compile the same libc and libSceVideoOut code as before.
A cancel with no waiter is kept once for that poll and makes its next
sceRudpPollWait return 0x80770014, also with a zero timeout. A cancel
with waiters wakes all of them with 0x80770014 and returns once none is
left. Behaviour from @BunnyFl's execution of the 13.42 libSceRudp.sprx
in #10954.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Validate the string and count its UTF-8 size first, then make the one
guest allocation and write into it, so no std::vector can throw
std::bad_alloc past the guest. Approach from @thepoppfish's review.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A gap that holds a table, padding and then a syscall stub followed by
non-padding data classified the stub as invalid, so the mask ran to the end
of the gap and hid the syscall from the scan. Mark any instruction that ends
control flow as a terminator, which stops the mask before that block.
Covered by the helper test and by the SCE, SysV and guest-module conversion
tests, which convert before the change and refuse syscall 9 after it.
Every pending gap target walked the whole unwind function vector, so a
large executable cost targets x functions. Sort and merge the ranges once,
then use upper_bound for each target.
guest_system_load_exec_standard_error compared the host standard error of
the re-executed child with "started <mode>\n" exactly. On a Linux kernel
without asynchronous userfaultfd write protection (before 6.7, such as
WSL2's 6.6) libc prints "[memory] write watch unavailable: ..." to the
host standard error at start-up, so the comparison failed and the test
aborted on every run. That one diagnostic line is now skipped when it
comes first; the rest of the host stream must still be the child's line
exactly, and the guest pipe must still be empty.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01En5cnt6RwCiNXDJwm9t8Tf