D471
- The fault reporter checks the page before reading it, because an
execute fault puts rip in the hole
measured - 2026-09-02 (user-directed /loop; the crash D470 could see but not explain)
The assumption that was false
instruction_bytes and bytes_before copy the
machine code around the faulting instruction into the report - this
project reads raw bytes rather than disassembling, because the bytes are
a fact and a disassembly of vendor code is not (principle 1). Both
carried this SAFETY note:
ipis the instruction pointer of the guest that just faulted, so the page holding it is mapped and readable
True of a data fault, false of an execute one. When
a guest jumps to an address that is not code, rip
is the unmapped address. Reading the instruction at it
faults again - and a fault inside a fault handler is not reported, it
ends the process. So the first fault was never recorded, no trace was
written, and the run reported the previous run's file (D470).
The probe caught it exactly:
fault-probe: code=0xc0000005 at=0x7fff0001 rip=0x7fff0001 - before emit <- guest jumped to a placeholder
fault-probe: code=0xc0000005 at=0x7fff0000 rip=0x7ffa932bcc54 <- the reporter, faulting
0x7fff0000 is one byte below 0x7fff0001: it
is bytes_before reading backwards from the placeholder the
guest had jumped to. The second rip is in a system DLL -
the memcpy doing the read. The handler never returned, so
nothing was recorded.
The change
A readable(address, len) helper using
VirtualQuery, which describes a mapping without
dereferencing it and allocates nothing - both properties this path
needs. It requires MEM_COMMIT, rejects
PAGE_NOACCESS and PAGE_GUARD (a guard page is
committed and still faults, which is the point of it), and requires the
whole window to sit inside one region, because the next one may be
unmapped. Both byte windows now ask before reading and answer zero bytes
when the memory is not there.
The reports lose nothing they used to have: a fault whose
rip is real still gets its bytes. What changes is that a
fault whose rip is not real now gets reported at
all.
Why it had never fired
Every fault this project had seen was a data fault -
the guest executing real code that touched a bad address - and for those
the assumption holds. The execute fault only appeared once
_Getpctype was implemented (D468) and the guest ran far
enough to reach a different failure: it asks an unimplemented function
for an address, is handed a placeholder, and calls it.
A new kind of fault found a hole in the reporter that a hundred
runs of the old kind never touched.
That is the general shape worth keeping: the invariant was written
down honestly in a SAFETY note, and it was still wrong,
because it was stated about "the faulting instruction" when it was only
ever checked against one class of fault. A stated invariant is a claim
about every case, not the observed ones.
What it made visible
The first honest measurement of D468's effect on PPSA02664, against the last recorded pre-ctype state:
| imports | calls | fault | |
|---|---|---|---|
| before ctype (01:50) | 39 | 1,544 | image+0xb14be3, dereferencing _Getpctype's
placeholder |
| after ctype | 68 | 10,905 | 0x7fff0001, an instruction fetch |
Seven times the calls and twenty-nine more distinct imports. The
ctype table was a real advance and was reported as
same - nothing moved for a whole morning.
The new wall is legible:
Il2CppUserAssemblies::0x6f8b9da539afc9af, called
222 times, whose arg0 points at
"il2cpp_init" (and "il2cpp_monitor_pulse"
after it in the same string table). It is a name resolver; it answers
the placeholder, and the guest calls what it was given. Naming and
implementing it is the next step, and the capture's 5,400 kernel exports
may name it.