The Linux KVM subsystem is the hypervisor behind most of the world’s non-hyperscaler public cloud and virtually every private-cloud, VPS, and enterprise-virtualization deployment that is not vSphere or Hyper-V. It runs the OVH, Hetzner, and DigitalOcean legacy droplet fleets; it runs every OpenStack Nova compute node; it runs Proxmox, CloudLinux, and the Linux Foundation’s KubeVirt project. The hyperscalers use their own hypervisors — AWS Nitro, GCP’s Andromeda-based platform, Azure’s Hyper-V variant — but the tier of infrastructure beneath them, from mid-size cloud providers down to on-prem virtualization, is almost entirely KVM.
Under the hood KVM is a Linux kernel module (kvm.ko plus the architecture-specific kvm_intel.ko or kvm_amd.ko) that turns the host kernel into a hypervisor. Every guest VM is a qemu-system-x86_64 process; the kernel module handles the hardware-virtualization extensions (VMX on Intel, SVM on AMD); and the whole edifice depends on one component — the shadow MMU — to give each guest its own view of physical memory while keeping the host kernel’s own page tables authoritative and safe.
CVE-2026-53359, nicknamed Januscape, is a 16-year-old race condition and use-after-free in the KVM shadow MMU (arch/x86/kvm/mmu/mmu.c). Under a specific four-step sequence that a guest with root and nested virtualization enabled can construct, the host associates a shadow page-table entry with the wrong guest frame number.

The mistake is a gfn vs role mismatch that the non-leaf validation check should have caught — except that for sixteen years the check compared the wrong two fields. The corrupt shadow-page metadata collapses into a use-after-free / type confusion in host kernel memory, giving the guest a controllable primitive to either crash the host or reach code execution as root on the hypervisor.
Januscape is named after Janus, the two-faced Roman god of gates and beginnings. The name is the researcher’s, chosen because the same defective code path fires on both Intel VMX and AMD SVM — the first guest-to-host escape confirmed to work identically on both x86 virtualization architectures with the same proof of concept.
The bug was originally used as a zero-day inside Google’s kvmCTF Vulnerability Reward Program before the upstream commits landed in the June 2026 kernel cycle. A working public PoC dropped on GitHub within a week of the upstream commit. As of publication no active mass-exploitation has been observed, but the ingredients — a public PoC, a low bar on prerequisites (guest-root is the default IaaS posture, and nested virt is enabled by default on most KVM builds), and a target class of infrastructure that has historically been slow to patch — put Januscape on the short list of “the next thing that gets weaponized.”
The chain composes two coupled defects at once. The escape fix, upstream commit 81ccda30b4e8, corrects the non-leaf validation field comparison. The companion frame-number fix, upstream commit 0cb2af2ea66a, is tracked separately as CVE-2026-46113 and closes a residual gfn-tracking gap that the escape fix relies on. Distros that ship only one of the two commits are not fully patched. This is one of the more subtle patch-completeness stories to land in the KVM subsystem in years and is where most of the operational risk in this CVE lives.
Vulnerability Overview

Four actions, one race window, one wrong-field comparison. Each stage is legal for a nested-virt guest on its own; composed in order they land the host walker on a shadow page whose metadata has silently drifted from what the guest actually asked for.
The attacker. A guest VM with root inside the guest and nested virtualization enabled on the guest configuration. On public IaaS this is the default posture: the tenant owns their VM and can install what they want, and nested virt is enabled by default in most upstream KVM builds (kvm_intel.nested=1 / kvm_amd.nested=1 are the shipped defaults). No CVE, no bug, no lateral movement is required to reach this state — it is the starting condition of every non-hardened VPS.
Modify a PDE mapping from outside the guest’s normal write path. Nested virtualization gives the guest an out-of-band channel — the nested hypervisor’s VMCS (Intel) or VMCB (AMD) — that lets it modify page-directory entries in a way the shadow-MMU walker does not fully account for. The specific sequence involves setting up a nested guest that touches the outer guest’s PDE via the VMCS shadow indirection. This is the primitive that gives Januscape its “16 years unnoticed” character: standard guest workloads never do this, and even most nested-virt workloads never do it in the specific timing window that matters.
Delete the right memslot. KVM memslots are how a guest requests memory regions be mapped into its physical address space. Deleting a memslot at the right moment during the racing PDE modification lands the host’s shadow-page walker on a page whose gfn (guest frame number) matches its expected value but whose role — the bitfield describing what kind of shadow page this is — does not. The mismatch is exactly what the non-leaf validation check is supposed to catch. It doesn’t.
The wrong-field comparison fires. The non-leaf shadow-page validation function has, for the past sixteen years, compared the wrong two fields when checking whether a candidate shadow page matches what the guest asked for. For every legitimate guest workload the wrong-field comparison happens to give the right answer because the fields are always in sync. Only when the guest deliberately constructs a mismatch — via the three-step sequence above — does the wrong check pass while the correct check would have failed. The host walker then proceeds with a shadow page whose metadata is corrupt, and the resulting gfn / role type-confusion collapses into a use-after-free of the shadow-page structure in host kernel memory.
At a glance. CVSS pending (kernel-security team’s provisional score is High, expected to publish once NVD ingests). Affected: any host running a Linux kernel with the pre-patch arch/x86/kvm/mmu/mmu.c — sixteen years of kernel history. Fixed: both commits 81ccda30b4e8 and 0cb2af2ea66a; per-distro patched kernel versions vary. Required identity: guest root with nested virtualization enabled — no host access, no cross-tenant credentials. User interaction: none. Wire signature: any guest that constructs the four-action sequence.
Root Cause Analysis
The shadow MMU — 60-second primer
KVM does not simply hand the guest its own page tables. On x86 without hardware-assisted paging (Extended Page Tables on Intel, Nested Page Tables on AMD) or under nested-virt scenarios that force software walks, the host maintains a set of shadow page tables that mirror each guest’s page tables into structures the host walker can trust. Each shadow page carries metadata that ties it back to what the guest asked for: the guest frame number (gfn) it corresponds to, a role bitfield (role) describing what kind of mapping this is (level, direct-vs-indirect, quadrant on 2-level guests, and so on), and a link back to the memslot that requested it.
When a guest touches memory that would trigger a page-table walk, the host walker consults its shadow pages, verifies that the shadow-page metadata matches what the guest is asking for, and either satisfies the walk from cached state or builds a new shadow page. The verification step — “does this shadow page match what the guest expects?” — is the safety check. The check compares fields of the shadow-page metadata against the guest’s request. If the fields match, the walker proceeds; if they don’t, the shadow page is torn down and rebuilt.
The wrong-field comparison
For sixteen years, the non-leaf shadow-page validation check compared the wrong two fields.
Specifically, the check that was supposed to compare the shadow page’s role bitfield against the request instead compared a related-but-different field pair that, in every legitimate case, happens to be in sync with role. The bug is subtle in exactly the way that keeps a bug alive in a heavily-audited code path for a decade and a half: the wrong check gives the right answer whenever the guest is behaving normally. Legitimate workloads never construct the state where the wrong field and the right field diverge, so the wrong check appears — to any code reader, static analyzer, or fuzzer that hasn’t specifically targeted the divergence — to be doing its job.
The exact identity of the two fields lives in the kernel patch commit message for 81ccda30b4e8. In shorthand: the check compares a summary of the metadata rather than the authoritative fields the summary is derived from. For any legitimate walk the summary is a valid stand-in for the authoritative fields. Under the four-step race sequence a guest can drive the authoritative fields and the summary out of sync — the summary retains its old value, the wrong check passes on the summary, and the walker proceeds on a shadow page whose authoritative metadata now contradicts what the walker believes it is looking at.

Why the leaf path is fine and only non-leaf breaks
The shadow-page validation logic exists in two variants: one for leaf pages (the level of the walk that maps actual physical memory) and one for non-leaf pages (page directories, page-directory pointers, and higher-level structures that hold references to other shadow pages).
The leaf variant compares the correct authoritative fields. It has always compared the correct fields. The bug lives only in the non-leaf variant, which is why the primitive is reachable only through a sequence that lands the host walker on a non-leaf shadow page during the memslot-deletion window. The four-action race is precisely the sequence that gets the walker there.
The nested-virt requirement
Nested virtualization is not a nice-to-have precondition — it is architecturally load-bearing. The specific out-of-band write channel used in Step 2 is only available to a guest running its own hypervisor. Nested virt’s VMCS-shadowing (Intel) and VMCB-cloning (AMD) mechanisms give the guest a way to touch page-directory entries that the shadow-MMU walker does not fully synchronize.

This is what makes the “disable nested virt on guests that don’t need it” recommendation an effective mitigation and not just defense-in-depth. Without nested virt, the guest has no way to drive the authoritative fields and the summary out of sync in the way the escape requires.
Why nobody noticed for 16 years
Three reasons, and understanding all three is important because the same shape of bug lives elsewhere in the kernel.
The wrong-field comparison passes for every legitimate case. The wrong field is a valid derivative of the right field under all normal operating conditions. Every fuzzer that has ever run against the KVM MMU has hit only cases where the wrong check gives the right answer. Every developer who has read the code has read a check that appears to be checking the right thing.
The trigger requires a specific sequence that no legitimate workload constructs. PDE modification from outside the normal write path plus memslot deletion in a specific timing window plus a non-leaf walker fault is not a state any real workload wants to be in. Even syzkaller — the primary fuzzer for the KVM subsystem — did not organically construct the sequence in fifteen years of continuous operation. It took a human researcher, deliberately hunting the divergence, to build the four-action chain.
Nested virtualization at scale is a recent operational posture. The code path has been in the kernel since roughly 2010; nested virt on x86 was experimental for years, gradually stabilized, and only became a broadly-enabled default in production KVM deployments around 2020. Guest-root plus nested-virt at a meaningful scale is a phenomenon of the past five or six years. The bug has been present for the entire life of the code; the operational exposure to it is a phenomenon of the last third of that period.

The cross-architecture surprise
Intel VMX and AMD SVM share the same non-leaf shadow-page validation code path in arch/x86/kvm/mmu/mmu.c. This is what makes Januscape structurally different from every prior KVM guest-to-host escape: earlier bugs were arch-specific (Intel-only VMX bugs, AMD-only SVM bugs, or Intel-EPT/AMD-NPT differences that meant the same class of bug required two separate PoCs). Januscape’s PoC is a single binary that runs identically on both architectures.
The reason the two architectures share this code path is that both hardware-virtualization implementations converged on the same shadow-page abstractions in the Linux kernel a decade ago. What was originally an Intel-first implementation absorbed the AMD path, and the two-faced quality of the resulting code — one function serving both architectures — is what the researcher named the bug after.
Patch Diffing
The two-commit fix
Januscape is fixed by two upstream kernel commits, both required:
Commit 81ccda30b4e8 — the escape fix, tracked as CVE-2026-53359. This commit corrects the field comparison in the non-leaf validation path so the check actually validates what it was supposed to. The change is small in isolation: a handful of lines in the field comparison expression, plus a comment explaining why the previously-used field is not a valid stand-in for the authoritative one.
Commit 0cb2af2ea66a — the frame-number fix, tracked as CVE-2026-46113. This commit closes a residual gfn-tracking gap that the escape fix relies on. Without 0cb2af2ea66a, the escape fix from 81ccda30b4e8 leaves a smaller but still-reachable primitive: the corrected check still trusts the walker’s cached gfn value in a state where the memslot-deletion race can invalidate it.

Why one alone is insufficient
The subtle character of Januscape is that its two commits fix two different but coupled defects. The escape fix (81ccda30b4e8) corrects the comparison; the frame-number fix (0cb2af2ea66a) corrects the value being compared. If you only fix the comparison, the value is still wrong and the check now compares two wrong values that still happen to be in sync in most cases. If you only fix the value, the comparison logic still compares the wrong fields and misses the mismatch.
Any distribution that has shipped only one commit is not fully patched. This is where the operational risk lives: kernel security teams under time pressure will occasionally backport what they see labelled with the primary CVE, miss the companion, and end up shipping a partial fix. As of publication, all major distro advisories (Red Hat, Ubuntu, SUSE, Debian) have shipped both commits — but if you consume a kernel from a smaller vendor or roll your own, verify both commit hashes are present in your source tree.
The diff at a glance
Both commits touch arch/x86/kvm/mmu/mmu.c. 81ccda30b4e8 modifies the field-comparison expression in the non-leaf validation function; 0cb2af2ea66a modifies the guest-frame-number caching path so the value the escape-fix check reads is authoritative rather than stale. Neither commit is more than a few dozen lines. The smallness of the fix relative to the sixteen-year lifetime of the bug is one of the more striking data points in the disclosure.
Static Analysis
The arch/x86/kvm/mmu/mmu.c code path
The vulnerable code lives in the non-leaf shadow-page validation function inside arch/x86/kvm/mmu/mmu.c. Its purpose is to answer a single question for every non-leaf level of a page walk: is this cached shadow page still a valid stand-in for what the guest is asking for, or does it need to be torn down and rebuilt?
The function receives a shadow-page pointer, a gfn, and a role descriptor derived from the current walker state. It compares fields of the shadow page against the request and returns a match/no-match verdict. In the pre-patch code the comparison expression referenced a summary field of the shadow page — a derived value that, under normal operation, is always a valid summary of the two authoritative fields (gfn and role) that the check should have compared directly.
Under the Januscape race sequence, the summary field can lag the authoritative fields by exactly one walker cycle. The check reads the stale summary, decides the shadow page is valid, and proceeds. The walker then dereferences into a shadow page whose authoritative gfn and role no longer match what the walker believes it is walking.

The memslot-deletion trigger
KVM memslots are the mechanism by which a guest asks the hypervisor to map memory regions into its physical address space. When a guest asks for a memslot to be deleted, the hypervisor’s job is to tear down every shadow-page reference that pointed at the memslot’s pages. In the racing scenario Januscape constructs, the memslot-deletion path and the walker’s shadow-page cache lookup interleave in a way that leaves stale summary-field values behind for exactly the walker cycle that immediately follows the deletion.
The specific timing is short but reproducible. The public PoC hits it within seconds of the guest constructing the initial nested-virt state.
The nested-virt write channel
Nested virtualization’s role is to provide the out-of-band write channel that Step 2 of the attack chain requires. On Intel VMX, the mechanism is VMCS shadowing — the nested guest’s VMCS state is mirrored into a host-visible structure that the shadow MMU touches. On AMD SVM, the equivalent mechanism is VMCB cloning. Both give the guest a way to modify page-directory entries that the shadow-MMU walker synchronizes only partially.
The correct fix — arguably the fix that should have been shipped alongside 81ccda30b4e8 — would be to fully synchronize the walker’s cached state across the nested-virt write channel. The kernel team took the more surgical path of correcting the validation check instead, on the grounds that the correct-fields comparison is a defensible check independent of any nested-virt behavior. The trade-off is that the correct-fields check depends on the frame-number tracking being authoritative, which is why the companion CVE-2026-46113 patch is required.

The public PoC — primitives, not exact code
The public PoC (published on GitHub within a week of the upstream commit) implements the four-action sequence as a self-contained C program that runs inside a guest with root and nested-virt. The primitives it exercises:
- Enable and enter a nested-guest state via KVM_SET_VCPU2 (or the platform equivalent).
- Construct a nested-guest VMCS that touches an outer-guest PDE via VMCS shadowing.
- Ask the outer guest’s KVM instance to delete the memslot underneath the PDE.
- Enter the outer guest and trigger a walk that lands on the racing shadow page.
The demonstrated payload in the public PoC is a controllable host-kernel panic — a demonstration that the walker is dereferencing a shadow page whose metadata is corrupt, in a way that lands in a fault path the host cannot recover from. The path to full code execution as root on the host is described in the researcher’s disclosure but the public PoC does not weaponize it. Reproducing the panic is trivial; developing a stable code-execution primitive on top of it is the standard kernel-exploit work of turning a use-after-free into an arbitrary-write, which several public write-ups have already sketched at the primitive level.

Cross-architecture generality
The same PoC binary runs on both Intel and AMD hosts. The researcher confirmed this on public-cloud hosts from three separate providers, and the SecurityOnline PoC writeup reproduces the panic on both an Intel Xeon and an AMD EPYC host in the same demo video. This is Januscape’s structural novelty relative to prior KVM escapes — every earlier guest-to-host bug required two separate PoCs, one per vendor’s virtualization extensions.
Conclusion
Impact
An attacker with guest root on any VM running under an unpatched shared-KVM host, with nested virtualization enabled on the guest configuration, can:
- Crash the host kernel — knocking every co-tenant VM offline on the same physical host, with high reliability and short time-to-effect.
- Corrupt host kernel memory in a controllable way — the documented path to root-level code execution on the hypervisor. A hypervisor-root attacker controls every guest VM on the host, has full read access to their memory, their disk-backing state, and any credentials they have loaded into memory.
The at-risk fleet is significant. The three hyperscalers use their own hypervisors and are not affected. Below them, essentially every KVM-based public cloud, VPS provider, private OpenStack deployment, Proxmox cluster, CloudLinux host, and self-managed KubeVirt install is at risk until both patches are applied. Guest-root is the customer’s default posture in every public-cloud offering, and nested virt is enabled by default in most upstream KVM builds. The pre-conditions for Januscape are, in operational terms, “guest exists on unpatched host” plus “guest configuration hasn’t been hardened away from defaults.”
The cross-tenant risk is what makes this a Critical bug rather than just an interesting one. A single compromised customer VM on a shared-KVM public-cloud host is now a potential path to the compromise of every other tenant on that host.
Remediation
In priority order:
- Apply both kernel patches. Verify 81ccda30b4e8 and 0cb2af2ea66a are both present in your running kernel source tree. On distro kernels, check your vendor’s CVE tracker for both CVE-2026-53359 and CVE-2026-46113; a fixed kernel must reference both.
# On a distro kernel, verify both commits are backported:
git log --oneline arch/x86/kvm/mmu/mmu.c | grep -E "(81ccda30b4e8|0cb2af2ea66a)"
- Disable nested virtualization on guests that don’t need it. Nested virt is the load-bearing precondition for Januscape’s write channel; disabling it neutralizes the primitive independently of any patch state.
# Host-wide, at module load:
options kvm_intel nested=0
options kvm_amd nested=0
Per-guest, in libvirt XML:
<cpu mode='host-passthrough'>
<feature policy='disable' name='vmx'/>
<feature policy='disable' name='svm'/>
</cpu>
- On multi-tenant KVM hosts that cannot be patched immediately, evacuate untrusted tenants first. Live-migrate tenant VMs off unpatched hosts before patching or restarting the host kernel. On multi-tenant public-cloud infrastructure this is often an operational nightmare, but the alternative — leaving a known-vulnerable host running with untrusted tenants — is worse.
- Audit kernel oops and panic logs on your KVM hosts for the past 30 days. The public PoC exists; opportunistic scanning by researchers, red-teamers, or unauthorized parties will produce host-kernel oopses in the arch/x86/kvm/mmu/mmu.c frame. Retroactive detection is the only defense you have against attackers who reached the primitive before you patched.
Detection
Pre-compromise detection options are limited by construction — the bug lives inside the hypervisor kernel, and the trigger is a legitimate KVM/QEMU API sequence from a guest that has the standing to make those calls. Guest-side detection is essentially impossible short of full memory-integrity monitoring that no real deployment runs. Host-side detection is where the pragmatic signals live.
Kernel oops / panic logs. Any host-kernel panic whose crashing frame references arch/x86/kvm/mmu/mmu.c addresses is a high-confidence indicator. Legitimate KVM operation does not produce panics originating from mmu.c under any normal condition. On a syslog collector:
grep -E "kvm_mmu|arch/x86/kvm/mmu" /var/log/kern.log*
Any hit within the past 30 days is a candidate. Correlate against the guest whose VM was running at the time of the panic.
Repeated guest-triggered oopses on the same host within a short window. Attackers probing the race timing will not hit the primitive on the first try; expect three to twenty attempts within a five-minute window before the panic fires. A pattern of KVM-related oopses clustered in time, associated with a single guest UUID, is stronger evidence than a single panic.
Nested-virt enablement on guests that don’t need it. Not an IOC — a policy indicator. A tenant VM with nested virt enabled but no legitimate hypervisor workload inside it is either running a hobbyist nested-hypervisor experiment (fine) or preparing to run the Januscape PoC (not fine). Correlate against tenant workload profile.
KVM userspace ioctl telemetry. If you have eBPF instrumentation on the host, the specific ioctl sequence — KVM_SET_MEMORY_REGION deletion racing against KVM_RUN on a nested-vcpu — is a strong wire signature. Building this into a Falco or Tetragon rule is the right level of investment for a KVM-based public-cloud operator.
Takeaway
The structural lesson: nested virtualization is its own security surface, and a wrong-field comparison in a hypervisor’s shadow MMU is exactly the shape of bug that lives dormant until the surrounding software’s operational posture changes. When nested virt went from “niche experiment” to “default-enabled on cloud tenants,” Januscape went from theoretically-reachable to one Python script away from every guest on OVH.
The pattern of a 16-year-old kernel bug going from “unreachable in practice” to “one-shot cross-tenant primitive” is not unique to KVM. The same shape has surfaced in the network stack, in the filesystem layer, and in the io_uring subsystem in the past three years. The unifying observation is that a code path that was “safe enough for typical workloads” ten years ago is not necessarily safe against attackers who now have the freedom to construct arbitrary system-call sequences from inside their own root-owned VMs, containers, or process namespaces. The operational scope of what an attacker can build has expanded faster than the kernel’s audit posture for the code paths that expansion has newly reached.
The defender’s playbook: treat shared-KVM hosts as latent cross-tenant risk. Disable nested virt on every VM that doesn’t demonstrably need it. Apply both commits — not just the one your CVE tracker highlights. And when the next 16-year-old wrong-field-comparison bug lands — and one will — read the entire patch series, not just the one commit that has the CVE label.
References
- Linux kernel commit 81ccda30b4e8 (CVE-2026-53359, the escape fix) — https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=81ccda30b4e8
- Linux kernel commit 0cb2af2ea66a (CVE-2026-46113, the frame-number fix) — https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0cb2af2ea66a
- NVD entry for CVE-2026-53359 — https://nvd.nist.gov/vuln/detail/CVE-2026-53359
- NVD entry for CVE-2026-46113 (companion) — https://nvd.nist.gov/vuln/detail/CVE-2026-46113
- The Hacker News — 16-Year-Old Linux KVM Flaw Lets Guest VMs Escape — https://thehackernews.com/2026/07/16-year-old-linux-kvm-flaw-lets-guest.html