CVE-2026-99178
Publication date:
28/10/2026
In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
x86/kvm: Don&#39;t consume async #PF flags on a kernel #PF in NMI context<br />
<br />
An async #PF "page not present" event arrives through the #PF vector,<br />
and its reason sits in the per-CPU apf_reason.flags until<br />
__kvm_handle_async_pf() reads and clears it. If an NMI arrives after<br />
the event is injected and before the #PF handler reads the flags, and<br />
the NMI handler itself takes a #PF, that fault reads and clears the<br />
flags belonging to the #PF it interrupted. It then finds interrupts<br />
disabled in the NMI context it interrupted, and panics:<br />
<br />
Kernel panic - not syncing: Host injected async #PF in interrupt<br />
disabled region<br />
Call Trace:<br />
<br />
panic+0x52/0x60<br />
__kvm_handle_async_pf+0xb0/0xb0<br />
exc_page_fault+0x71/0xb0<br />
asm_exc_page_fault+0x27/0x30<br />
RIP: 0010:__get_user_nocheck_8+0x6/0x20<br />
perf_callchain_user+0x147/0x1a0<br />
get_perf_callchain+0x12d/0x1c0<br />
...<br />
perf_event_nmi_handler+0x26/0x50<br />
exc_nmi+0xe8/0x110<br />
RIP: 0010:asm_exc_page_fault+0x0/0x30<br />
<br />
<br />
The last RIP line shows the NMI landing on the first instruction of<br />
the #PF entry. perf with user call chains takes such a fault: the<br />
sampling NMI walks the user stack with __get_user(), which faults when<br />
a saved frame pointer is not a valid user address or the walk runs off<br />
the top of the stack. The same window exists when KVM, running as an<br />
L1 hypervisor with DELIVERY_AS_PF_VMEXIT, reads the flags itself after<br />
a #PF VM-exit.<br />
<br />
The NMI entry code saves and restores CR2 for this nesting, but not the<br />
async #PF reason. Andy Lutomirski described the race, with perf as the<br />
example, when async #PF delivery was reworked in 2020 [1]. Saving and<br />
clearing the flags across the NMI, next to CR2, was discussed there but<br />
not merged.<br />
<br />
Since that rework, Linux has stopped setting KVM_ASYNC_PF_SEND_ALWAYS,<br />
so the host delivers "page not present" only for user mode faults and<br />
a kernel-mode fault in NMI context (including kernel-mode #MC and #DB)<br />
cannot be an async page fault. In that case, return without touching<br />
the flags so that the interrupted handler finds them intact.<br />
<br />
Tested on v7.3-rc5 in a 4-vCPU, 2 GiB guest restored from a snapshot<br />
whose memory is served on demand through userfaultfd, so that reading<br />
it back takes async #PFs, while every thread samples hardware cycles<br />
with user call chains and points its frame pointer at an unmapped page.<br />
Without this patch the guest panicked as above in 3 of 3 runs. With it,<br />
the guest survived 3 of 3 runs of 60 seconds, and the host counted<br />
148,441 to 153,334 kvm_async_pf_not_present events in the first 20<br />
seconds of each. On 6.18.50 the results were the same: the guest<br />
panicked without the patch and survived with it.
Severity CVSS v4.0: Pending analysis
Last modification:
28/10/2026