D443 - PPSA02664's allocator wall was orbistoun's policy region sitting on the guest's heap
measured - 2026-09-01 (user-directed, /loop; running the resident titles)
Running PPSA02664 walled with a null write at
image+0xafcc08 - its C++ allocator - preceded by the guest
printing
tlsf_add_pool: Memory size must be between 0x28 and 0x100000000 bytes.
Two changes came out of tracing it; the second is the one that moved the
wall.
sceKernelMprotect implemented. It was a
bare stub. The guest reserves a span, then mprotects it
usable before handing it to its allocator; a placeholder answer left the
range looking unusable. The handler re-protects against the same
mappings() space the reservation used
(AddressSpace::protect refuses a range it does not own, so
a typo cannot touch this process's own code), reusing the existing
protection_from_guest decode. Two honest simplifications,
both from the identity-mapped model rather than guessed: the range is
kept readable (the 0xf2 a guest passes
decodes to write-without-read, which would drop a range the allocator
must read back), and the high GPU/cache bits are ignored, not
decoded (no citable layout; inventing one is the D008 error).
Implementing it did not move the wall on its own -
useful to know, and it is needed regardless - which is what pointed at
the reservation itself.
The real cause: POLICY_REGION_BASE collided with
the guest's heap arena. PPSA02664's allocator reserves its
arena at a fixed hint - 0x5000_0000_0000 - through
sceKernelReserveVirtualRange, and does its size arithmetic
relative to that address. orbistoun's stub-policy regions were based at
exactly 0x5000_0000_0000 (chosen as "empty", clear
of loader/stack - but nobody had checked it against where a guest's own
allocator lands). Reserved before the guest ran, the policy region took
the address first; VirtualAlloc at the hint then returned
ERROR_INVALID_ADDRESS (a fresh-process test confirmed
Windows itself allows the address, so the 487 was an orbistoun-owned
conflict, not a VA limit); the reservation fell back to the mapping
arena at 0x72…, a higher address; and the guest
computed pool_size = arena_end - returned_base, which
underflowed to a value past tlsf's 4 GiB ceiling.
tlsf rejected the pool, the next allocation returned null,
and the guest wrote through it.
Moving POLICY_REGION_BASE into orbistoun's own high
cluster (0x6B…, between the TLS block and the mapping
arena, where nothing a guest chooses lands) freed the hint. The
reservation is now honoured, the guest's arithmetic works,
tlsf accepts the pool, and PPSA02664 goes FURTHER:
from 233 calls to 1541 (+1307), 27 to 37 distinct imports. The
lesson generalises past this title - orbistoun places several fixed
regions at guessed addresses (the sentinel already learned this and
picked an odd base); a guest's own allocator is one more claimant to
stay clear of, and "clear of our regions" was not the same as
"clear".
The new wall, for the next unit. The guest now
faults at image+0xb14be3 dereferencing
0x7fff00cf - one of our own placeholder codes used as a
pointer. libc::_Getpctype (the ctype-table accessor, part
of locale/character classification) is unimplemented and answered the
placeholder; malloc_stats_fast is unimplemented too.
_Getpctype returns a pointer to a character-classification
table, so it needs a real implementation (a citable table - FreeBSD's
ctype is the oracle), not a policy region. That is the next wall past
the allocator.
Settles the D442 aside. The full trace shows
PPSA02664 reaching no flexible-memory call of any kind, so the recorded
"imposing the flexible figure took it backwards" cannot have been the
size query - consistent with D442's finding that it imports no flexible
function. It sizes off sceKernelGetDirectMemorySize and the
arena reservation, both now answered.