[2.BF] Silph WorkerCtx: install canary's real sub-vtable at [+0x2C][0]
Round-21 pivot of the audit-059 synth-spawn module. Round 20 made the
silph::WorkerCtx workers run by attaching a 32-slot stub sub-vtable
where every entry was a `li r3, 0; blr` stub — workers spawned but
spun forever because slots 15/17 short-circuited to NULL ("no work").
Round 21 reads canary's real sub-vtable VA out of the XEX `.rdata` —
`0x8200A168` — and points `[sub_object + 0]` at it directly. The
vtable bytes live in the static image both engines map, so no guest
memory is consumed and slot 15 (= `sub_824FCCC8`) and slot 17
(= `sub_824FCE38`) — the only slots `sub_82506B08` ever calls —
become working game methods.
Discovery method (canary probes in
`audit-runs/audit-059-handle-disambiguation/round21-subvtable-canary/`):
1. `--audit_jit_prolog_pc=0x82506B08` to catch the first WorkerCtx
virtual-dispatch entry; `[r3+0x2C]` revealed the sub-object VA.
2. Re-run with `--audit_jit_prolog_mem_dump=<sub-obj VA>` to deref
`[sub-object + 0]` = sub-vtable VA = 0x8200A168.
3. PE inspection (`xex-text/xex-rdata` is the static image) reads
all 31 slots; slot 15 -> sub_824FCCC8, slot 17 -> sub_824FCE38.
Smoke metrics (50M instructions, `XENIA_CACHE_PERSIST=1
XENIA_SILPH_SYNTH=1`, audit-runs/audit-059-handle-disambiguation/
round21-real-vtable/):
* 4/4 workers spawned, no crash, no new fault
* KeSetEvent 633885 -> 431860 (-32%)
* KeWaitForSingleObject 258441 -> 185762 (-28%)
* Per-handle state unchanged on the focused stalled set
(0x1020/0x1090 still `<NO_SIGNALS_DESPITE_WAITS>`,
0x12a4/0x12ac/0x1218/0x1224 still `<UNCREATED>`).
* No VdSwap/draws progression observed in this window.
Verdict: B (partial). The workers no longer spin in a stub-loop —
internal call density shifted — but the focused wedge handles still
don't get signalled. Likely root cause: workers may now be waiting
on the WorkerCtx's own KEVENTs (which we synthesised at
+0x54/+0x94) for upstream work that no producer is enqueuing.
Net LOC: 29 ins / 31 del. Tests: workspace passes (lockstep app
tests, kernel 127/127, hir 288/288, scheduler 38/38).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -56,9 +56,10 @@ const SILPH_WORKER_ENTRIES: [u32; 4] = [
|
||||
/// Round 0x440 up to a page-ish so the ctx alloc never straddles a page
|
||||
/// boundary in heap_alloc's bookkeeping. Round 20 grew the alloc from 0x500
|
||||
/// to 0x800 to make room for a synthesised sub-object at +0x300 and its
|
||||
/// 32-slot vtable at +0x500 (= ctx + 0x500..0x580). Both are required because
|
||||
/// the worker bodies dereference `[ctx+0x2C]` to obtain a sub-object pointer
|
||||
/// and then virtual-dispatch through its slot 15.
|
||||
/// 32-slot vtable at +0x500 (= ctx + 0x500..0x580). Round 21 retains the
|
||||
/// embedded sub-object but drops the synthesized vtable (we now point at
|
||||
/// canary's real XEX-resident sub-vtable directly), so the 0x500..0x580
|
||||
/// region is unused but harmless.
|
||||
const SILPH_CTX_SIZE: u32 = 0x800;
|
||||
|
||||
/// Offset within the ctx allocation of the synthetic sub-object referenced
|
||||
@@ -67,21 +68,20 @@ const SILPH_CTX_SIZE: u32 = 0x800;
|
||||
/// `heap_alloc` covers everything.
|
||||
const SILPH_SUBOBJ_OFFSET: u32 = 0x300;
|
||||
|
||||
/// Offset within the ctx allocation of the synthetic sub-object vtable.
|
||||
const SILPH_SUBVTBL_OFFSET: u32 = 0x500;
|
||||
|
||||
/// Number of slots in the synthetic sub-vtable. The worker bodies access
|
||||
/// slots 15 and 17 (per audit-059 round 19); 32 slots is generous and gives
|
||||
/// headroom for any downstream vtable index we haven't surveyed yet.
|
||||
const SILPH_SUBVTBL_SLOTS: u32 = 32;
|
||||
|
||||
/// Trivial `li r3, 0; blr` stub at VA `0x8216CAA4` in the XEX `.text`
|
||||
/// section — the first 4-byte-aligned standalone instance located by the
|
||||
/// round-20 PE scan (preceded by a `blr` ending the previous function).
|
||||
/// We populate every slot of the synthetic sub-vtable with this address so
|
||||
/// any virtual call through the sub-object idles, returning NULL work and
|
||||
/// keeping the worker live.
|
||||
const SILPH_STUB_RETURN_NULL_VA: u32 = 0x8216_CAA4;
|
||||
/// XEX `.rdata` VA of canary's real sub-object vtable (audit-059 round 21).
|
||||
/// Discovered by:
|
||||
/// 1. Probing canary at `pc=0x82506B08` (= `sub_82506B08`, method 35 of
|
||||
/// the WorkerCtx vtable, the first sub-object method called by every
|
||||
/// `sub_82506528/58/88/B8` worker entry).
|
||||
/// 2. Capturing `[ctx+0x2C]` from the JIT-prolog dump (= sub-object VA
|
||||
/// in canary's heap).
|
||||
/// 3. Re-running with `--audit_jit_prolog_mem_dump=<sub-obj VA>` to read
|
||||
/// `[sub-object + 0]` = sub-vtable VA = **`0x8200A168`**.
|
||||
/// PE inspection confirms slot 15 (called via `[r11+0x3C]` at
|
||||
/// `sub_82506B08+0x44`) = `sub_824FCCC8` and slot 17 (`[r11+0x44]` at
|
||||
/// `sub_82506B08+0x70`) = `sub_824FCE38`. Both are real game methods in
|
||||
/// the same `.text` region as the rest of the worker dispatch surface.
|
||||
const SILPH_SUB_VTABLE_SOURCE_VA: u32 = 0x8200_A168;
|
||||
|
||||
/// Round-19 XEX-resident wrapper constant observed at `[ctx+0x30]` in every
|
||||
/// canary ctx (audit-059 round 7). Same value for all four ctxes — opaque
|
||||
@@ -153,22 +153,20 @@ pub fn spawn_silph_workers(
|
||||
// +0x30 — XEX-resident wrapper constant 0xBE568F00 (round 7). Opaque
|
||||
// but identical across all four canary ctxes.
|
||||
let subobj_ptr = ctx + SILPH_SUBOBJ_OFFSET;
|
||||
let subvtbl_ptr = ctx + SILPH_SUBVTBL_OFFSET;
|
||||
mem.write_u32(ctx + 0x2C, subobj_ptr);
|
||||
mem.write_u32(ctx + 0x30, SILPH_CTX_FIELD_30_CONST);
|
||||
|
||||
// ---- Synthetic sub-object at +0x300 ----
|
||||
// Round-19 disassembly shows worker bodies only touch the sub-object's
|
||||
// vtable (slots 15/17) and treat the rest as opaque. Zero-fill is
|
||||
// sufficient. The only required field is `[+0]` = sub-vtable pointer.
|
||||
mem.write_u32(subobj_ptr, subvtbl_ptr);
|
||||
|
||||
// ---- Synthetic sub-vtable at +0x500 ----
|
||||
// Every slot points at SILPH_STUB_RETURN_NULL_VA so any virtual call
|
||||
// through the sub-object idles, returning NULL work without crashing.
|
||||
for slot in 0..SILPH_SUBVTBL_SLOTS {
|
||||
mem.write_u32(subvtbl_ptr + slot * 4, SILPH_STUB_RETURN_NULL_VA);
|
||||
}
|
||||
// ---- Embedded sub-object at +0x300 ----
|
||||
// Round-21 pivot: instead of synthesising a stub vtable that returns
|
||||
// NULL from every slot, point `[sub_object + 0]` directly at canary's
|
||||
// real XEX-resident sub-vtable VA. The vtable bytes are part of the
|
||||
// same static image both engines map, so referring to it costs zero
|
||||
// guest memory and gives the workers a working virtual-method surface
|
||||
// (slot 15 = sub_824FCCC8, slot 17 = sub_824FCE38, plus 29 other real
|
||||
// methods). Round-19 disassembly shows worker bodies only touch the
|
||||
// sub-object's vtable; the rest of the sub-object is opaque so we
|
||||
// leave it zero-filled.
|
||||
mem.write_u32(subobj_ptr, SILPH_SUB_VTABLE_SOURCE_VA);
|
||||
|
||||
// ---- 4× X_KEVENT auto-reset at +0x54/+0x64/+0x74/+0x84, state = 0 ----
|
||||
// X_DISPATCH_HEADER layout (canary xobject.h:35):
|
||||
|
||||
Reference in New Issue
Block a user