Skip to content

WHP: faithfully handle all guest CPU exceptions (no guest IDT) + fix MXCSR mask corruption #1069

Description

@momo5502

Summary

The WHP backend runs the guest without a populated IDT, so any CPU exception the emulator does not explicitly intercept gets vectored by the hardware through the null IDT — reading IDT_base(0) + vector*16 and faulting. Today only #DB/#BP/#UD/#PF are intercepted (WHvPartitionPropertyCodeExceptionExitBitmap). Any other exception → spurious #PF at a tiny address → crash that looks like a guest null-deref but isn't.

Concrete bug this surfaced from

Intermittent in-game crash in iw4x/MW2 (WHP) reported as Null-pointer call to 0x130:

  • 0x130 = vector 19 (#XM, SIMD FP exception) × 16 = IDT[19]. That is the whole tell.
  • Trigger: guest MXCSR is corrupted to 0x20 (all exception masks cleared; Windows user-mode default is 0x1F80 = masked). An SSE op (CVTTSD2SI [ESP] at iw4x.exe:0x6b5255, DIVSS at iw4x.dll:0x7ee9b) on a NaN/out-of-range value then raises #XM, which isn't intercepted → null-IDT fault at 0x130.
  • What made it confusing: the "faulting instruction" is a benign FP op that touches no memory; the error code is supervisor (descriptor/IDT reads are supervisor); RIP and all segment bases are correct. It survives with preemption disabled (so it's not a scheduler race), just rarer.

Current workaround (applied)

  • Add WHvX64ExceptionTypeSimdFloatingPointFault (#XM) and WHvX64ExceptionTypeFloatingPointErrorFault (#MF) to the exception-exit bitmap.
  • In handle_exception, re-mask MXCSR (| 0x1F80 & ~0x3F) + x87 CW (| 0x3F) and re-run the instruction so it completes (NaN/inf), matching Windows masked-FP behavior.

Validated: [FP-EXC] #1 type=19 mxcsr 0x20->0x1f80 readback=0x1f80, no crash, in-game survival went from ~60-90s to 325s+ before hitting an unrelated failure.

This stops the crash but is a workaround, because:

  1. It treats the symptom (re-masks) instead of the cause — whatever clears the MXCSR mask bits (most likely the WoW64 32-bit FP-state transition; a sibling of the fresh-thread x87 TagWord seeding bug).
  2. It forcibly overrides guest MXCSR (not transparent if a program legitimately unmasks FP exceptions).
  3. It only covers the FP vectors. #DE (vector 0 → IDT[0]=0x0), #GP (13 → 0xD0), #AC, #BR, #OF, … are still un-intercepted and would crash identically.

Proper fix

  1. Find and fix the MXCSR mask corruption — trace MXCSR across the WoW64 32↔64 boundary / FP-state save-restore so #XM never fires (the Windows-correct state is 0x1F80).
  2. Generalize exception handling — intercept all user-reachable exception vectors and faithfully deliver each to the guest as the correct STATUS_* SEH exception (decoding MXCSR / error code), exactly as the Windows kernel does, instead of depending on the (absent) guest IDT.

Repro

EMULATOR_WHP=1 analyzer -vc -ss "<...>/iw4x.exe" -console -stdout

Load a map and play; previously crashed within ~1-5 min with Null-pointer call to 0x130.

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions