D128 - The heap, and a fault address that identified itself

decided · 2026-08-20

malloc was not implemented, so it answered the placeholder error code. A title took that as its buffer, handed it to memset, and our memset faithfully wrote there.

The fault address was the proof, not a hint. write to 0x7fff0001 is GuestError::Unimplemented.as_raw(). Principle 3 put placeholder codes in a range no real value occupies specifically so a stub leaking into guest-visible behaviour would be obvious, and it paid for itself here: the faulting address named its own cause. The earlier write to 0x7fff0119 was the same value plus a field offset.

The second half of the evidence was the instruction pointer - 0x7ffc…, host address space, not the guest's. A guest that faults inside our code is a guest we handed something bad.

Implemented malloc, calloc, realloc and free over the host allocator, which is legitimate rather than lazy: the address space is identity-mapped, so a host allocation is a guest allocation at the same address, and a private arena would buy nothing but a second allocator to get wrong. A sixteen-byte header carries the size, because free is given only a pointer and sixteen is the alignment malloc owes any type on x86-64.

Result: 29 distinct imports reached became 37, and the title now gets to sceVideoOutOpen, sceUltMutexLock and sceUltConditionVariableSignal - display and synchronisation - before failing on a null it does not check.

The progress metric misled here, and that is worth recording

D080 measures progress by the faulting instruction pointer. By that measure this run went backwards: image+0x13514 to image+0xf2f6.

It did not. The previous fault was inside our memset with the guest's own position unrecorded, and the new one is a different code path entirely - reached only because the allocation succeeded. An instruction pointer compares two positions in one path; it says nothing useful across two different ones.

Distinct imports reached is the better signal when the path changes. Neither is right alone: a guest can call more imports while getting less far, and can get further while calling fewer. Both belong in the progress block, and the one that moved should be named rather than reduced to a single verdict.