⬢ DragonFlyBSD Kernel Audit
DF-3829 / nmi_ddb_evidence.txt
← back to finding ↓ download raw
NMI delivery evidence (captured live from the serial console before the
guest was rebooted; boot.log.vmx was truncated by the restart).

Trial: `nvmm_drive spin 24 6` (6 vCPUs spinning in VMX non-root) with
three QEMU-monitor `nmi` injections at ~12s, ~15s, ~18s.

Result: the guest dropped into DDB:

    CPU0 stopping CPUs: 0x0000003e
     stopped
    Stopped at      vmx_vcpu_run+0x522:     movl    %dr0,%edx
    db> 3

Interpretation
--------------
The NMI that froze the CPU landed while the CPU was executing nvmm's
*host-side* run loop (`vmx_vcpu_run+0x522` is in the DR-register
save/restore region, `x86_get_dr0()` inside `vmx_vcpu_guest_dbregs_leave`,
nvmm_x86_vmx.c:2131). With `machdep.ddb_on_nmi=1` (default), that NMI was
delivered to DragonFly's trap() handler -> DDB.

=> NMIs arriving while the CPU is in the host-side part of the run loop
   ARE delivered normally. The swallow window of DF-3829 is only the
   non-root residency: an NMI that arrives while the vCPU is executing
   the guest causes VMCS exit reason 0 (EXC_NMI) with EXIT_INTR_INFO
   type=NMI, which `vmx_exit_exc_nmi()` (nvmm_x86_vmx.c:1252-1271)
   classifies as NVMM_VCPU_EXIT_NONE and re-enters the guest -- the NMI
   is never dispatched to the host.

Why the differential cannot be isolated on this guest: QEMU's monitor
`nmi` injects an NMI on *all* 6 vCPUs simultaneously; with 6 CPUs the
probability that at least one NMI lands in a host-side window (and thus
DDB-freezes the machine, masking the swallow) is overwhelming. On real
hardware, or with a per-CPU NMI source, the differential would be:
control (no vCPU resident) -> "NMI ... going to debugger"; vCPU resident
-> nothing, run loop continues.

verdict for DF-3829: status=inconclusive (live), root-cause established
by code reading (NetBSD added nmi_dispatch() upstream for exactly this).