On macOS under Rosetta, Aps5StartGuest moves the main thread off the host
stack and releases the old range to the guest arena. dyld keeps its own
pointers into the process start block at the top of that range and offers
no call that redirects them, where argv and environ have one:
_NSGetExecutablePath copies the path string out of the block on every
call, so the first call after the move read unmapped memory and the
process died with KERN_PROTECTION_FAILURE. A title does not have to call
it: libxpc reaches it from xpc_connection_resume, and ShaderCacheDirectory
calls it for the shader disk cache.
Release the old stack only up to the pages that hold the block, and only
while argv still points into the stack. They are the top two pages, 8 KiB
of the 8372224-byte stack here, so a title that fixes a mapping over that
range now fails with a host region overlap; the rest is free as before.
guest_main_stack_move asked for a 64 KiB guest mapping aligned down from
a local of main, which falls inside the kept pages. It now probes the
64 KiB below them, requires the kept pages to be refused, and calls
_NSGetExecutablePath after the move, which segfaults without this change.
Under Rosetta the main thread stack lies at about 12 GiB, inside the guest
arena, where a title can map fixed addresses; a fixed mapping over it
aborts with a host region overlap.
- Aps5StartGuest moves the main thread onto a 16 MiB stack at
0x600000000000 when the old stack overlaps a host region of the arena.
It copies argv, environ, the program name and the initial block off the
old stack, then frees the old stack and releases its range to the arena.
- The guest thread keeps the stack size of the original main thread.
- The arena gets GuestArenaOverlapsHostRegion and
GuestArenaReleaseHostRegion for POSIX hosts.
- guest_main_stack_move starts the guest, then requires the main thread
outside the arena, a fixed mapping where the old stack was, and argv and
the environment intact.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>