---
id: "CVE-2026-99178"
kind: "vulnerability"
aliases: ["CVE-2026-99178"]
title: "Linux KVM guest kernel can panic from async page fault during NMI"
severity: "unknown"
cvss: null
epss: null
exploited: false
status: "open"
category: "infrastructure"
products: []
ecosystems: []
tags: ["hypervisor", "infrastructure", "kvm", "linux-kernel", "self-hosted", "x86"]
not_affected_if: ["no-kvm", "non-x86"]
published_at: "2026-10-28T11:17:38+00:00"
first_seen_at: "2026-10-28T11:32:36+00:00"
updated_at: "2026-10-28T11:35:28+00:00"
sources: [{"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-99178", "first_seen_at": "2026-10-28T11:32:36+00:00"}]
---

# Linux KVM guest kernel can panic from async page fault during NMI

A race in the x86 KVM async page fault handling lets an NMI that takes a kernel page fault clear the flags of the interrupted fault, causing a kernel panic. The sources describe a denial of service by crash, not code execution.

_Summary, check and fix written by a model from the sources; labelled as such._

## How to check

- `uname -r` on KVM guests and hypervisors, compare against the fixed kernel version in the upstream commit, which the sources do not name
- Check whether the guest runs with async page fault enabled (`KVM_FEATURE_ASYNC_PF` on the host) and whether perf samples with user call chains are in use (`perf record -g`)

## Fix

- Update the Linux kernel to a release that includes the upstream fix for CVE-2026-99178 (the sources do not name the fixed version)
- Reboot affected guests and hypervisors after the kernel update

## Timeline

- 2026-10-28T11:32:36+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-99178

## From the sources

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.

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
