D656 - The context has to be on a real stack
Status: measured Date: 2026-09-09
The measurement
REQ-20260909T1720Z-4e17 asked what the console hands a
signal handler in rsi. Sweep 20260909-175052:
rsi and rdx |
the identical pointer |
| the pointer | 0x7eeffaeb0, on the interrupted thread's own 2 MiB
stack |
| its mapping | [0x7eedfc000, 0x7eeffc000), confirmed by
sceKernelVirtualQuery |
+0x48 |
1e00000000000000 - the signal number |
+0xf8 |
0x7eeffb698 - another address on that same stack,
mapped |
+0x100..+0x170 |
register and thread frame state |
+0x00 |
zero |
Three attempts, and the third is the finding
A reserved page of orbistoun's own.
0x5E2E_0000_0000, per the address map's own rule for
regions this project invents. The handler ran past
mov rax, [rsi+0xf8], made ten more calls, reached two more
imports - and faulted reading +0x13c8, past the end of a 4
KiB page.
The same region, 2 MiB - the measured extent of the console's
own mapping. It faulted reading 0x368 past the end
of that.
That is the finding. The guest is not reading a structure, it is scanning - forward, in eight-byte steps, until it leaves the allocation. A root scan, which is what a collector does to a suspended thread's stack. A scan is bounded by the allocation it is in, so it does not matter how large an invented region is; it matters that it is the right allocation.
So the context is built on the stack the handler runs on
call_guest already reserves a fresh guarded stack for a
reentrant call and releases it after. That stack is the closest thing
this process has to the console's arrangement, where the frame is built
on the interrupted thread's stack and the handler runs on it.
call_guest_placing hands the caller that stack's span and
takes the three arguments back, so only the function that owns the
reservation ever knows the address - it cannot be handed out early or
kept late.
An earlier attempt used the target thread's recorded stack,
which is None for main - it is adopted rather
than spawned - so the handler got a null and the run went straight back
to where it started. The failure was silent in exactly the way an
Option fallback usually is.
What it moved
before: 141 imports 310,987 calls ran to the time limit, silent for 19.7s of 20
after: 151 imports 321,973 calls image+0x17554a3
FURTHER - the project's one measure of progress, and the first time this title has produced one since the binding fix. The fault has left the title's own modules entirely.
Two runs agree on the fault site and on the import count; the call count now varies by a few dozen between runs, which it did not before. That is expected and worth stating: a collector is now actually running across threads, so the number of calls in a wall-clock second is no longer fixed. The fault site is what D080 reads progress from, and that is stable. The frontier records one call count and may need the call budget for this title if the jitter grows.
Nothing else in the corpus moved.
What is still zero, and why it stays zero
+0x100..+0x170 is register and thread state on the
console. Orbistoun has none to put there: the handler runs because a
different thread raised the signal, and the interrupted
thread's registers are not this process's to read - it is parked inside
a Rust condition variable, not stopped at a guest instruction. Plausible
values there would let a guest resume onto them. Zero makes it read a
null and check, the same trade the zeroed data blocks make (D323).
The inner pointer at +0xf8 points 0x7e8
further along the same stack, which is the measured relationship. What
it points at is zero, because on the console it is whatever
that thread happened to have there.