Skip to content

CVE-2026-99178 x86/kvm: Don't consume async #PF flags on a kernel #PF in NMI context

MediumCVSS 6.5 EPSS 0.17%
Published
Updated
Exploit status
No public exploit known

Description

In the Linux kernel, the following vulnerability has been resolved:

x86/kvm: Don't consume async #PF flags on a kernel #PF in NMI context

An async #PF "page not present" event arrives through the #PF vector,
and its reason sits in the per-CPU apf_reason.flags until
__kvm_handle_async_pf() reads and clears it. If an NMI arrives after
the event is injected and before the #PF handler reads the flags, and
the NMI handler itself takes a #PF, that fault reads and clears the
flags belonging to the #PF it interrupted. It then finds interrupts
disabled in the NMI context it interrupted, and panics:

Kernel panic - not syncing: Host injected async #PF in interrupt
disabled region
Call Trace:
<NMI>
panic+0x52/0x60
__kvm_handle_async_pf+0xb0/0xb0
exc_page_fault+0x71/0xb0
asm_exc_page_fault+0x27/0x30
RIP: 0010:__get_user_nocheck_8+0x6/0x20
perf_callchain_user+0x147/0x1a0
get_perf_callchain+0x12d/0x1c0
...
perf_event_nmi_handler+0x26/0x50
exc_nmi+0xe8/0x110
RIP: 0010:asm_exc_page_fault+0x0/0x30
</NMI>

The last RIP line shows the NMI landing on the first instruction of
the #PF entry. perf with user call chains takes such a fault: the
sampling NMI walks the user stack with __get_user(), which faults when
a saved frame pointer is not a valid user address or the walk runs off
the top of the stack. The same window exists when KVM, running as an
L1 hypervisor with DELIVERY_AS_PF_VMEXIT, reads the flags itself after
a #PF VM-exit.

The NMI entry code saves and restores CR2 for this nesting, but not the
async #PF reason. Andy Lutomirski described the race, with perf as the
example, when async #PF delivery was reworked in 2020 [1]. Saving and
clearing the flags across the NMI, next to CR2, was discussed there but
not merged.

Since that rework, Linux has stopped setting KVM_ASYNC_PF_SEND_ALWAYS,
so the host delivers "page not present" only for user mode faults and
a kernel-mode fault in NMI context (including kernel-mode #MC and #DB)
cannot be an async page fault. In that case, return without touching
the flags so that the interrupted handler finds them intact.

Tested on v7.3-rc5 in a 4-vCPU, 2 GiB guest restored from a snapshot
whose memory is served on demand through userfaultfd, so that reading
it back takes async #PFs, while every thread samples hardware cycles
with user call chains and points its frame pointer at an unmapped page.
Without this patch the guest panicked as above in 3 of 3 runs. With it,
the guest survived 3 of 3 runs of 60 seconds, and the host counted
148,441 to 153,334 kvm_async_pf_not_present events in the first 20
seconds of each. On 6.18.50 the results were the same: the guest
panicked without the patch and survived with it.

Affected products

ProductAffected versionsFixed in
Linux kernel >= 631bc4878220932fe67fc46fc7cf7cccdb1ec597, < fd60b32a99627fda479c9fa1d4a82fa9f7994670 fd60b32a99627fda479c9fa1d4a82fa9f7994670
Linux kernel >= 631bc4878220932fe67fc46fc7cf7cccdb1ec597, < b77e9622bbade9812e2090c66d25c372f515e921 b77e9622bbade9812e2090c66d25c372f515e921
Linux kernel >= 631bc4878220932fe67fc46fc7cf7cccdb1ec597, < f45ee1ff6358a22d7e22a4c53cb3148ea97b10d9 f45ee1ff6358a22d7e22a4c53cb3148ea97b10d9
Linux kernel >= 631bc4878220932fe67fc46fc7cf7cccdb1ec597, < 81d99abe2bd81379abfb595e403bbbee6fbfc624 81d99abe2bd81379abfb595e403bbbee6fbfc624
Linux kernel = 2.6.38 No fix listed
Linux kernel < 6.12.115 6.12.115
Linux kernel < 6.18.59 6.18.59
Linux kernel < 7.2.13 7.2.13
Linux kernel < 7.3 7.3
ubuntu/linux(Ubuntu:22.04) All versions No fix listed
ubuntu/linux-kvm(Ubuntu:22.04) All versions No fix listed
ubuntu/linux-raspi(Ubuntu:22.04) All versions No fix listed
ubuntu/linux(Ubuntu:24.04) All versions No fix listed
ubuntu/linux-raspi(Ubuntu:24.04) All versions No fix listed
debian/linux(Debian:12) All versions No fix listed
debian/linux(Debian:12) = 6.1.106-1 No fix listed
debian/linux(Debian:12) = 6.1.106-2 No fix listed
debian/linux(Debian:12) = 6.1.106-3 No fix listed
debian/linux(Debian:12) = 6.1.112-1 No fix listed
debian/linux(Debian:12) = 6.1.115-1 No fix listed
debian/linux(Debian:12) = 6.1.119-1 No fix listed
debian/linux(Debian:12) = 6.1.123-1 No fix listed
debian/linux(Debian:12) = 6.1.124-1 No fix listed
debian/linux(Debian:12) = 6.1.128-1 No fix listed
debian/linux(Debian:12) = 6.1.129-1 No fix listed
Show 175 more
ProductAffected versionsFixed in
debian/linux(Debian:12) = 6.1.133-1 No fix listed
debian/linux(Debian:12) = 6.1.135-1 No fix listed
debian/linux(Debian:12) = 6.1.137-1 No fix listed
debian/linux(Debian:12) = 6.1.139-1 No fix listed
debian/linux(Debian:12) = 6.1.140-1 No fix listed
debian/linux(Debian:12) = 6.1.147-1 No fix listed
debian/linux(Debian:12) = 6.1.148-1 No fix listed
debian/linux(Debian:12) = 6.1.153-1 No fix listed
debian/linux(Debian:12) = 6.1.158-1 No fix listed
debian/linux(Debian:12) = 6.1.159-1 No fix listed
debian/linux(Debian:12) = 6.1.162-1 No fix listed
debian/linux(Debian:12) = 6.1.164-1 No fix listed
debian/linux(Debian:12) = 6.1.170-1 No fix listed
debian/linux(Debian:12) = 6.1.170-2 No fix listed
debian/linux(Debian:12) = 6.1.170-3 No fix listed
debian/linux(Debian:12) = 6.1.172-1 No fix listed
debian/linux(Debian:12) = 6.1.174-1 No fix listed
debian/linux(Debian:12) = 6.1.176-1 No fix listed
debian/linux(Debian:12) = 6.1.177-1 No fix listed
debian/linux(Debian:12) = 6.1.180-1 No fix listed
debian/linux(Debian:12) = 6.1.187-1 No fix listed
debian/linux(Debian:12) = 6.1.190-1 No fix listed
debian/linux(Debian:12) = 6.1.27-1 No fix listed
debian/linux(Debian:12) = 6.1.37-1 No fix listed
debian/linux(Debian:12) = 6.1.38-1 No fix listed
debian/linux(Debian:12) = 6.1.38-2 No fix listed
debian/linux(Debian:12) = 6.1.38-2~bpo11+1 No fix listed
debian/linux(Debian:12) = 6.1.38-3 No fix listed
debian/linux(Debian:12) = 6.1.38-4 No fix listed
debian/linux(Debian:12) = 6.1.38-4~bpo11+1 No fix listed
debian/linux(Debian:12) = 6.1.52-1 No fix listed
debian/linux(Debian:12) = 6.1.55-1 No fix listed
debian/linux(Debian:12) = 6.1.55-1~bpo11+1 No fix listed
debian/linux(Debian:12) = 6.1.64-1 No fix listed
debian/linux(Debian:12) = 6.1.66-1 No fix listed
debian/linux(Debian:12) = 6.1.67-1 No fix listed
debian/linux(Debian:12) = 6.1.69-1 No fix listed
debian/linux(Debian:12) = 6.1.69-1~bpo11+1 No fix listed
debian/linux(Debian:12) = 6.1.76-1 No fix listed
debian/linux(Debian:12) = 6.1.76-1~bpo11+1 No fix listed
debian/linux(Debian:12) = 6.1.82-1 No fix listed
debian/linux(Debian:12) = 6.1.85-1 No fix listed
debian/linux(Debian:12) = 6.1.90-1 No fix listed
debian/linux(Debian:12) = 6.1.90-1~bpo11+1 No fix listed
debian/linux(Debian:12) = 6.1.94-1 No fix listed
debian/linux(Debian:12) = 6.1.94-1~bpo11+1 No fix listed
debian/linux(Debian:12) = 6.1.98-1 No fix listed
debian/linux(Debian:12) = 6.1.99-1 No fix listed
debian/linux(Debian:12) = 6.10-1~exp1 No fix listed
debian/linux(Debian:12) = 6.10.1-1~exp1 No fix listed
debian/linux(Debian:12) = 6.10.11-1 No fix listed
debian/linux(Debian:12) = 6.10.11-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.10.12-1 No fix listed
debian/linux(Debian:12) = 6.10.3-1 No fix listed
debian/linux(Debian:12) = 6.10.4-1 No fix listed
debian/linux(Debian:12) = 6.10.6-1 No fix listed
debian/linux(Debian:12) = 6.10.6-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.10.7-1 No fix listed
debian/linux(Debian:12) = 6.10.9-1 No fix listed
debian/linux(Debian:12) = 6.11-1~exp1 No fix listed
debian/linux(Debian:12) = 6.11.10-1 No fix listed
debian/linux(Debian:12) = 6.11.10-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.11.2-1 No fix listed
debian/linux(Debian:12) = 6.11.4-1 No fix listed
debian/linux(Debian:12) = 6.11.5-1 No fix listed
debian/linux(Debian:12) = 6.11.5-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.11.6-1 No fix listed
debian/linux(Debian:12) = 6.11.7-1 No fix listed
debian/linux(Debian:12) = 6.11.9-1 No fix listed
debian/linux(Debian:12) = 6.11~rc4-1~exp1 No fix listed
debian/linux(Debian:12) = 6.11~rc5-1~exp1 No fix listed
debian/linux(Debian:12) = 6.12.10-1 No fix listed
debian/linux(Debian:12) = 6.12.100-1 No fix listed
debian/linux(Debian:12) = 6.12.101-1 No fix listed
debian/linux(Debian:12) = 6.12.105-1 No fix listed
debian/linux(Debian:12) = 6.12.107-1 No fix listed
debian/linux(Debian:12) = 6.12.11-1 No fix listed
debian/linux(Debian:12) = 6.12.11-1+alpha No fix listed
debian/linux(Debian:12) = 6.12.11-1+alpha.1 No fix listed
debian/linux(Debian:12) = 6.12.111-1 No fix listed
debian/linux(Debian:12) = 6.12.113-1 No fix listed
debian/linux(Debian:12) = 6.12.115-1 No fix listed
debian/linux(Debian:12) = 6.12.115-2 No fix listed
debian/linux(Debian:12) = 6.12.116-1 No fix listed
debian/linux(Debian:12) = 6.12.12-1 No fix listed
debian/linux(Debian:12) = 6.12.12-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.13-1 No fix listed
debian/linux(Debian:12) = 6.12.15-1 No fix listed
debian/linux(Debian:12) = 6.12.16-1 No fix listed
debian/linux(Debian:12) = 6.12.17-1 No fix listed
debian/linux(Debian:12) = 6.12.19-1 No fix listed
debian/linux(Debian:12) = 6.12.20-1 No fix listed
debian/linux(Debian:12) = 6.12.21-1 No fix listed
debian/linux(Debian:12) = 6.12.22-1 No fix listed
debian/linux(Debian:12) = 6.12.22-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.25-1 No fix listed
debian/linux(Debian:12) = 6.12.27-1 No fix listed
debian/linux(Debian:12) = 6.12.27-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.29-1 No fix listed
debian/linux(Debian:12) = 6.12.3-1 No fix listed
debian/linux(Debian:12) = 6.12.30-1 No fix listed
debian/linux(Debian:12) = 6.12.30-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.31-1 No fix listed
debian/linux(Debian:12) = 6.12.32-1 No fix listed
debian/linux(Debian:12) = 6.12.32-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.33-1 No fix listed
debian/linux(Debian:12) = 6.12.33-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.35-1 No fix listed
debian/linux(Debian:12) = 6.12.35-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.37-1 No fix listed
debian/linux(Debian:12) = 6.12.38-1 No fix listed
debian/linux(Debian:12) = 6.12.38-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.41-1 No fix listed
debian/linux(Debian:12) = 6.12.43-1 No fix listed
debian/linux(Debian:12) = 6.12.43-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.48-1 No fix listed
debian/linux(Debian:12) = 6.12.5-1 No fix listed
debian/linux(Debian:12) = 6.12.57-1 No fix listed
debian/linux(Debian:12) = 6.12.57-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.6-1 No fix listed
debian/linux(Debian:12) = 6.12.63-1 No fix listed
debian/linux(Debian:12) = 6.12.63-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.69-1 No fix listed
debian/linux(Debian:12) = 6.12.69-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.73-1 No fix listed
debian/linux(Debian:12) = 6.12.73-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.74-1 No fix listed
debian/linux(Debian:12) = 6.12.74-2 No fix listed
debian/linux(Debian:12) = 6.12.74-2~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.8-1 No fix listed
debian/linux(Debian:12) = 6.12.85-1 No fix listed
debian/linux(Debian:12) = 6.12.85-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.86-1 No fix listed
debian/linux(Debian:12) = 6.12.86-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.88-1 No fix listed
debian/linux(Debian:12) = 6.12.88-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.9-1 No fix listed
debian/linux(Debian:12) = 6.12.9-1+alpha No fix listed
debian/linux(Debian:12) = 6.12.9-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.90-1 No fix listed
debian/linux(Debian:12) = 6.12.90-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.90-2 No fix listed
debian/linux(Debian:12) = 6.12.90-2~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.94-1 No fix listed
debian/linux(Debian:12) = 6.12.94-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.95-1 No fix listed
debian/linux(Debian:12) = 6.12.95-1~bpo12+1 No fix listed
debian/linux(Debian:12) = 6.12.96-1 No fix listed
debian/linux(Debian:12) = 6.12~rc6-1~exp1 No fix listed
debian/linux(Debian:12) = 6.13.10-1~exp1 No fix listed
debian/linux(Debian:12) = 6.13.11-1~exp1 No fix listed
debian/linux(Debian:12) = 6.13.2-1~exp1 No fix listed
debian/linux(Debian:12) = 6.13.3-1~exp1 No fix listed
debian/linux(Debian:12) = 6.13.4-1~exp1 No fix listed
debian/linux(Debian:12) = 6.13.5-1~exp1 No fix listed
debian/linux(Debian:12) = 6.13.6-1~exp1 No fix listed
debian/linux(Debian:12) = 6.13.7-1~exp1 No fix listed
debian/linux(Debian:12) = 6.13.8-1~exp1 No fix listed
debian/linux(Debian:12) = 6.13.9-1~exp1 No fix listed
debian/linux(Debian:12) = 6.13~rc6-1~exp1 No fix listed
debian/linux(Debian:12) = 6.13~rc7-1~exp1 No fix listed
debian/linux(Debian:12) = 6.14.3-1~exp1 No fix listed
debian/linux(Debian:12) = 6.14.5-1~exp1 No fix listed
debian/linux(Debian:12) = 6.14.6-1~exp1 No fix listed
debian/linux(Debian:12) = 6.15-1~exp1 No fix listed
debian/linux(Debian:12) = 6.15.1-1~exp1 No fix listed
debian/linux(Debian:12) = 6.15.2-1~exp1 No fix listed
debian/linux(Debian:12) = 6.15.3-1~exp1 No fix listed
debian/linux(Debian:12) = 6.15.4-1~exp1 No fix listed
debian/linux(Debian:12) = 6.15.5-1~exp1 No fix listed
debian/linux(Debian:12) = 6.15.6-1~exp1 No fix listed
debian/linux(Debian:12) = 6.15~rc7-1~exp1 No fix listed
debian/linux(Debian:12) = 6.16-1~exp1 No fix listed
debian/linux(Debian:12) = 6.16.1-1~exp1 No fix listed
debian/linux(Debian:12) = 6.16.10-1 No fix listed

References