Merge audit-2026-05-fix/kernel-p0-spinlock-xam: KRNBUG-017 Kf-spinlock

This commit is contained in:
MechaCat02
2026-05-03 14:25:00 +02:00

View File

@@ -49,7 +49,7 @@ pub fn register_exports(state: &mut KernelState) {
state.register_export(Xboxkrnl, 0x84, "KeQuerySystemTime", ke_query_system_time);
state.register_export(Xboxkrnl, 0x85, "KeRaiseIrqlToDpcLevel", stub_return_zero);
state.register_export(Xboxkrnl, 0x88, "KeReleaseSemaphore", ke_release_semaphore);
state.register_export(Xboxkrnl, 0x89, "KeReleaseSpinLockFromRaisedIrql", stub_success);
state.register_export(Xboxkrnl, 0x89, "KeReleaseSpinLockFromRaisedIrql", ke_release_spinlock_from_raised_irql);
state.register_export(Xboxkrnl, 0x8F, "KeResetEvent", ke_reset_event);
state.register_export(Xboxkrnl, 0x92, "KeResumeThread", ke_resume_thread);
state.register_export(Xboxkrnl, 0x97, "KeSetAffinityThread", ke_set_affinity_thread);
@@ -60,9 +60,9 @@ pub fn register_exports(state: &mut KernelState) {
state.register_export(Xboxkrnl, 0xAE, "KeTryToAcquireSpinLockAtRaisedIrql", ke_try_acquire_spinlock);
state.register_export(Xboxkrnl, 0xAF, "KeWaitForMultipleObjects", ke_wait_for_multiple_objects);
state.register_export(Xboxkrnl, 0xB0, "KeWaitForSingleObject", ke_wait_for_single_object);
state.register_export(Xboxkrnl, 0xB1, "KfAcquireSpinLock", stub_return_zero);
state.register_export(Xboxkrnl, 0xB1, "KfAcquireSpinLock", kf_acquire_spin_lock);
state.register_export(Xboxkrnl, 0xB3, "KfLowerIrql", stub_success);
state.register_export(Xboxkrnl, 0xB4, "KfReleaseSpinLock", stub_success);
state.register_export(Xboxkrnl, 0xB4, "KfReleaseSpinLock", kf_release_spin_lock);
state.register_export(Xboxkrnl, 0x0152, "KeTlsAlloc", ke_tls_alloc);
state.register_export(Xboxkrnl, 0x0153, "KeTlsFree", stub_success);
state.register_export(Xboxkrnl, 0x0154, "KeTlsGetValue", ke_tls_get_value);
@@ -522,8 +522,58 @@ fn ke_initialize_semaphore(ctx: &mut PpcContext, mem: &GuestMemory, _state: &mut
mem.write_u32(sem_ptr + 0x10, limit);
}
fn ke_try_acquire_spinlock(ctx: &mut PpcContext, _mem: &GuestMemory, _state: &mut KernelState) {
ctx.gpr[3] = 1; // TRUE (acquired successfully in single-threaded mode)
fn ke_try_acquire_spinlock(ctx: &mut PpcContext, mem: &GuestMemory, _state: &mut KernelState) {
// r3 = KSPIN_LOCK*. Returns 1 (TRUE) on success. Single-threaded HLE
// mirrors canary's `KeTryToAcquireSpinLockAtRaisedIrql`: write 1 to
// the lock value (mark held) and return success. Under `--parallel`
// the coarse Arc<Mutex<KernelState>> already serializes us.
let lock_ptr = ctx.gpr[3] as u32;
if lock_ptr != 0 {
mem.write_u32(lock_ptr, 1);
}
ctx.gpr[3] = 1;
}
/// `KfAcquireSpinLock(KSPIN_LOCK *SpinLock)` — returns previous IRQL.
/// Per canary `xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc`
/// the function raises IRQL to DISPATCH_LEVEL (2), spins on the lock,
/// then sets it to 1. Pre-fix this was `stub_return_zero` — guest code
/// could enter critical regions without ever taking the lock, leading
/// to subtle races even in lockstep when the same code path was
/// re-entered before the matching release fired. KRNBUG-017.
fn kf_acquire_spin_lock(ctx: &mut PpcContext, mem: &GuestMemory, _state: &mut KernelState) {
let lock_ptr = ctx.gpr[3] as u32;
if lock_ptr != 0 {
mem.write_u32(lock_ptr, 1);
}
// Old IRQL = PASSIVE_LEVEL (0). The new IRQL is DISPATCH_LEVEL (2),
// tracked implicitly by the kf_release path. Returning 0 matches the
// common-case "called from a passive-level routine" entry path.
ctx.gpr[3] = 0;
}
/// `KfReleaseSpinLock(KSPIN_LOCK *SpinLock, KIRQL OldIrql)`.
/// Releases the spinlock and lowers IRQL to OldIrql. KRNBUG-017.
fn kf_release_spin_lock(ctx: &mut PpcContext, mem: &GuestMemory, _state: &mut KernelState) {
let lock_ptr = ctx.gpr[3] as u32;
if lock_ptr != 0 {
mem.write_u32(lock_ptr, 0);
}
ctx.gpr[3] = 0;
}
/// `KeReleaseSpinLockFromRaisedIrql(KSPIN_LOCK *SpinLock)`.
/// Releases the spinlock without changing IRQL. KRNBUG-017.
fn ke_release_spinlock_from_raised_irql(
ctx: &mut PpcContext,
mem: &GuestMemory,
_state: &mut KernelState,
) {
let lock_ptr = ctx.gpr[3] as u32;
if lock_ptr != 0 {
mem.write_u32(lock_ptr, 0);
}
ctx.gpr[3] = 0;
}
fn ke_tls_alloc(ctx: &mut PpcContext, _mem: &GuestMemory, state: &mut KernelState) {