D272 - Two shapes a guest hands over, and both were got wrong

decided · 2026-08-25 · one hang and ten million wasted calls

A pointer to a pointer is not the object

scePthreadMutexattrInit takes &attr where the guest declared ScePthreadMutexattr attr = NULL. It must allocate and write the address back - the same model the mutexes already used. Treating the double pointer as the object meant Settype overwrote the guest's own pointer variable with a type value.

An int out-parameter is four bytes

Fixing that produced a worse failure. scePthreadMutexattrGettype writes through int *, and writing a whole word took the caller's neighbouring stack variable with it - which was the loop counter, so every iteration reset it and the check ran until the call budget stopped it at twenty million calls.

This crate already carried a type to prevent exactly that: SemaphoreHandle is an i32 because "a mutex is a void * and a semaphore is an int written through an out-pointer: four bytes, not eight" (D210). The lesson had been learned and written down and the code still did it again, in a different function, because the knowledge lived in a type that this call did not use.

There is now a write_int beside write_word, so the next one is a function call rather than a decision.

Both bugs were caught by the run stopping at a deterministic twenty million calls rather than a wall clock (D238). A time limit would have made the same hang look like a slow machine.