D612 - The predicate that was never a predicate

Status: measured Date: 2026-09-08

What the measurement said, and what I nearly did with it

D611 left 031-stackattr/address-is-the-base:sceKernelIsStack:is-stack on the outstanding list rather than asserting it, because the console answered 0 for an address inside its own stack and orbistoun answers 1. The note said the work was reading the probe's check rather than writing an assertion.

Reading it took ten minutes and the answer is not a disagreement about a value.

int on_stack = sceKernelIsStack((void *)&frame, &low, &high);

sceKernelIsStack takes three arguments. The address, then two void ** where the bounds go. It returns a status. Orbistoun declared arity 1, returned 1 for an address in the stack and 0 for one outside, and wrote no bounds at all.

Every part of that is contradicted:

orbistoun the console
arguments 1 3
a local 1 0
a static 0 0
bounds written none low, high

The second and third rows are one measurement, and it is emphatic: 010-kernel/is-stack fails in twenty-three runs out of twenty-three, always with a stack address and a static one were reported alike, value 0x0. That check was written for a predicate, so it reads a successful call as a broken function. It is not broken - the answer is in the out-parameters, and 031-stackattr records them two mebibytes apart, exactly the stack size the same run read off the thread's own attribute.

So a guest asking this where its stack is got an inverted flag and two words of its own uninitialised memory back.

Why it survived

The name. IsStack reads as a question with a yes-or-no answer, the implementation answered one, and every test in this repository asserted the same reading - so the tests agreed with the code about something neither had checked. The measurement that contradicts it has been in the corpus for as long as the corpus has existed and was read as evidence the console's function was broken, which is what the knowledge entry said in as many words.

That is the shape worth naming: a measurement that contradicts an implementation was filed as a fact about the platform's defect rather than about ours. The check's own failure text is what made it plausible - obSCEne said "reported alike", which sounds like a complaint about the console.

The change

Arity 3. The bounds are written through the second and third arguments when they are non-null, and the return is 0.

One thing is deliberately not distinguished. Whether the console writes the bounds when the address is outside any stack is not measured: it answers 0 either way and no capture records the two words for the static case. They are written here regardless, because they describe the calling thread's stack rather than the address - a caller that asked about a static still learns where its own stack is, and refusing to say would invent a distinction nothing has measured.

Two harnesses caught it, which is the part worth keeping

The test helper in tests/posix.rs fills unspecified argument registers with 0xDEAD_BEEF_DEAD_BEEF rather than zero, precisely so a function reading an argument it was not given is caught. It caught this immediately: the new implementation faulted writing through it. A helper that padded with zeroes would have let the change land silently and written nothing.

And declared_arity_and_recorded_arity_never_disagree refused the change until the knowledge entry moved with it, so the arity cannot be right in one file and wrong in another.

It moved the wall, and that was established by taking it out again

The title that has been this project's wall reached 193 distinct imports and faulted at image+0x1389269. With this change it reaches 196 or 197 and faults at image+0x39f7c.

An intervention that moves a wall is not a diagnosis, so it was taken out and put back:

sceKernelIsStack distinct imports fault
as a predicate, one argument 193, 193 image+0x1389269
three arguments, bounds written 196, 197, 197 image+0x39f7c

Reverting the body alone - leaving everything else this session changed in place - puts the guest back on the old number and the old fault site exactly. Restoring it brings the new ones back. Two observations each way, and no other change in the tree accounts for either.

The event-flag mode refusal was tested the same way and is not responsible: with mode 0x00 reading as or again the fault stays at image+0x39f7c. It is on the guest's path - 70 calls to sceKernelWaitEventFlag, 9 to sceKernelPollEventFlag - and it moves nothing here.

Where it now stops is not "further" in the ordinary sense, and the report is careful to say so: 0x39f7c is a lower offset than 0x1389269, so the guest is dying earlier in the image while reaching more of the interface. The two positions are in different code and do not compare. What compares is the interface: three more of it, every run.

image+0x39f7c is also where PPSA02664 stops. Two titles, one offset.