fix(kernel): ensure_dispatcher_object writes XObj signature + handle (canary mirror)

Mirrors canary's `XObject::StashHandle` (xobject.h:253-256): on first
adoption of a guest dispatcher header, stamp +0x08 with the
kXObjSignature fourcc 'X','E','N','\0' and +0x0C with the stash handle
(here the guest pointer itself, since our shadow table is keyed by ptr).

Audit-023/024A documented divergence at addresses such as 0x828F4838
where canary stores "XEN\0" + handle but we left zeros. Lands as
canary-correctness restoration; cascade impact at -n 500M is nil per
the discipline gate (no sharp prediction tied to the writeback).

Lockstep determinism preserved: instructions=100000003,
imports=987516, swaps=2, draws=0 across 2 reruns.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-05-07 21:06:25 +02:00
parent d9e40d3564
commit c03f2bc9e2

View File

@@ -3094,6 +3094,12 @@ fn ensure_dispatcher_object(state: &mut KernelState, mem: &GuestMemory, ptr: u32
_ => return,
};
state.objects.insert(ptr, obj);
// Mirror canary `XObject::StashHandle` (xobject.h:253-256): on first
// adoption, stamp the X_DISPATCH_HEADER's wait_list with the kXObjSignature
// fourcc 'X','E','N','\0' (flink_ptr) and the stash handle (blink_ptr).
// Game code reads these to recognize already-adopted dispatchers.
mem.write_u32(ptr + 0x08, 0x58454E00);
mem.write_u32(ptr + 0x0C, ptr);
}
/// Set `gpr[3]` on a just-woken HW thread to reflect which handle in its
@@ -4601,6 +4607,35 @@ mod tests {
write_dispatcher_header(&mut mem, ptr, 2, 0); // Mutant — unsupported
ensure_dispatcher_object(&mut state, &mem, ptr);
assert!(!state.objects.contains_key(&ptr), "no shadow for unknown type");
// No StashHandle stamp on an ignored dispatcher.
assert_eq!(mem.read_u32(ptr + 0x08), 0);
assert_eq!(mem.read_u32(ptr + 0x0C), 0);
}
/// Mirror canary `XObject::StashHandle` (xobject.h:253-256): on first
/// adoption of a guest dispatcher, +0x08 must hold the 'X','E','N','\0'
/// fourcc and +0x0C must hold the stash handle.
#[test]
fn ensure_dispatcher_object_stamps_xen_signature_and_handle() {
let (mut ctx, mut mem, mut state) = fresh();
let kevent_ptr = SCRATCH_BASE + 0x700;
write_dispatcher_header(&mut mem, kevent_ptr, 1, 0); // synchronization
// Pre-condition: zeros at +0x08 / +0x0C.
assert_eq!(mem.read_u32(kevent_ptr + 0x08), 0);
assert_eq!(mem.read_u32(kevent_ptr + 0x0C), 0);
ctx.gpr[3] = kevent_ptr as u64;
ke_set_event(&mut ctx, &mut mem, &mut state);
// Post-condition: kXObjSignature ('X','E','N','\0') + stash handle.
assert_eq!(
mem.read_u32(kevent_ptr + 0x08),
0x58454E00,
"wait_list.flink_ptr must hold kXObjSignature 'XEN\\0'"
);
assert_eq!(
mem.read_u32(kevent_ptr + 0x0C),
kevent_ptr,
"wait_list.blink_ptr must hold stash handle (== guest dispatcher ptr)"
);
}
/// `KePulseEvent` on a manual-reset event must wake every parked waiter