D160 - The flag sweep: measurable without a console, and the dump answered the wrong call
The flag sweep: the second argument to
sceKernelDirectMemoryQuery is measurable without a console,
and the existing dump was answering the wrong call.
sceKernelDirectMemoryQuery is the most-called function
in the corpus mined from real titles - 87.6 million calls in one of
them, about 99.9% of every call it makes. The guest walks the memory
map, refuses what it is shown, and walks it again. So the contents of
its destination buffer are the single most valuable thing a hardware run
could bring back.
130-layout/direct-memory-query has dumped that buffer
for some time. It queries with flags = 0. Every
observed call from a real title passes 1. The dump
this project has been collecting therefore answers a different call from
the one that matters, and nobody had noticed because the argument was
never the subject.
Why a sweep answers it, and needs no oracle
Query one offset repeatedly with flags 0, 1, 2 and 4,
dump all four buffers, and report the offset of the first byte that
differs from the baseline. The difference is the
answer. Nothing is compared against an expectation and no field
is named, so this stays inside 130-layout's design - bytes,
not verdicts - and inside D008, because adding a value to an
int argument invents no structure and no arity.
It is a measurement of whichever implementation runs it. An emulator today says what that emulator does, which is worth having on its own terms; the same check on a console later says what the platform does. Neither is a guess.
Four values because a flag argument that means anything is usually a bit set, and two extra single bits separate "bit field" from "enumerated selector" without guessing what any bit means. Widening is a decision to make after these four have said something.
Zero differences is a result
A sweep where no flag changes the buffer says the flag does not
affect what this offset reports, on this implementation. That is exactly
as much of an answer as a difference, so it passes with a count rather
than failing. Every value refused is partial - a value the
platform rejects is a value that means something.
The guard check stops the sweep on the first overrun rather than continuing. Calling a function three more times after it has written past its buffer is not a measurement; it is repeating the damage.
The superfluous guard, found on the way
Both query checks carried
OBS_REQUIRE(&sceKernelGetDirectMemorySize) and
neither calls it - the symbol appeared in nothing but
the guard.
D058 is about not jumping to a null weak symbol. A guard on a symbol
the check never touches is the inverse mistake and it is not harmless:
it makes the check skip on a platform that has
sceKernelDirectMemoryQuery and happens not to export the
other, and the skip says "the symbol is not present" about a symbol that
is present. Removed from both.
Validated, which neither query check had been
Both skipped on the host build for want of a stub, so
130-layout/direct-memory-query had never run against a
known-good implementation either. The host now writes a start and a
length in the first sixteen bytes, a byte at offset 16 that varies with
the flag, and refuses flags = 4.
That shape is not a model of what a console returns - this program does not know that, and finding it out is the whole purpose of the section. It is chosen to drive every branch: baseline capture, difference detection, the first-differing-byte report, and the refusal count. The varying byte sits past both fields deliberately, so a differencing pass that stopped after sixteen bytes would report "the flag changes nothing", which is a conclusion rather than a silence.