PPSA02664 / PPSA03416 - image+0xafc959, write to 0xfffe0

Highest value. Reached only once sceKernelCreateSema was implemented (D193). Two titles, identical. 0xfffe0 is around a megabyte - not a null-plus-offset, so more likely a computed pointer that came out wrong, and the run now says so in as many words: an address in no region this run mapped, with rcx holding it and rdx holding 0x100000.

One suspect eliminated. memalign was the leading hypothesis. Forcing a dump for it with ORBISTOUN_DUMP=memalign (D198) showed arg0=0x8, arg1=0x1988 - a request that succeeds - so it is not the source.

It is not a naming problem, and the argument was readable all along. This used to say the next move was to name libkernel::0x6abac2f3dc6f8cee. Every naming mechanism has since been run to exhaustion and none reaches it (D213) - and it turned out not to matter, because the argument answers the question and had never been read. The readable window declared for dumps was a guard page too low, so 0x600000800d38 fell in the one page it excluded and rendered as a count (D217).

What it is, now that it dumps:

arg0 = 0x600000800d38 -> stack+0x800d38 = 00*8, 49 ba a2 01 00 00 00 00, 01, 00*8
arg1 = 0x100000        <- and the fault has rdx = 0x100000
arg2 = 0x0
arg3 = 0x40000
arg4 = 0x0
arg5 = 0x600000800db8 -> stack+0x800db8 = <image base>, 0x20, 0x10, 0x600000800ee0

0x100000 - 0x20 = 0xfffe0, which is the faulting address exactly. The guest asks for a megabyte aligned to 256 KB, then indexes base + size - 0x20 with base zero.

Two sources for that zero are eliminated, not argued about: it is not the stub return (an unimplemented stub returns a non-zero placeholder), and it is not unwritten memory (ORBISTOUN_STACK_FILL=5a leaves arg0's first eight bytes zero and the fault unmoved). The guest wrote that zero itself, as an out-parameter it expected filled.

Characterised, and the first positive fact about it after seven eliminations. Seven things were eliminated - the stub return, unwritten stack, unwritten heap, both stack-pointing argument targets, memalign, and the unfilled slot in the object the fault carries. Every candidate for a lost base was gone, which left the reading nobody had tested: that no base was lost and the address was right.

Reserving 0xf0000+0x10000 for one run moved the fault 213 bytes on - and watching that region said why, and contradicted the first reading of it (D224, corrected by D226).

The guest writes {next, prev, .., count=1} there: a circular list head with one node, an arena descriptor, whose lock is the mutex at image+0x1a2ddb8 taken immediately before. It asks for arg1 = 0x100000 bytes aligned to arg3 = 0x40000 and lays that header at region_end - 0x20 - so with the base lost, region_end is 0 + 0x100000 and the header lands at 0xfffe0.

So 0xfffe0 is not an address the guest wanted; it is size - 0x20 from a base of zero, and the mapping only gave a wrong pointer somewhere to land. The FURTHER was bought by answering wrongly, which is the progress principle 3 refuses to count.

What to do: libkernel::0x6abac2f3dc6f8cee is a region allocator. arg1 is a size, arg3 an alignment, and the caller wants a base it can put an arena header at the top of. Implementing that is the fix; the mapping must stay a diagnostic. Where the base is handed back is still open - the return value, arg0 and arg5 are all eliminated.