The entire exploit hinges on a specific memory geometry: a single physical page that is simultaneously the leaf destination of a 2 MB large-page PDE entry and the page table page that a second configuration of the same PDE points into. Because both configurations resolve through the same PDE index to the same guest frame number, L0’s shadow MMU sees the sameDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/V4bel/Januscape/llms.txt
Use this file to discover all available pages before exploring further.
gfn in both cases — yet the two shadow pages it must create have opposite role.direct values. This is the precondition that makes the role-mismatch reuse in kvm_mmu_get_child_sp reachable. The build_world() function inside poc.c constructs this geometry entirely from L1 physical memory with no host cooperation.
Memory Allocations
Three regions are central to the exploit:low_va/low_pa— An order-9 (2 MB) allocation that provides the identity-mapped guest physical space for the L2 guest image, GDT, TSS, page tables, and stack. The L2 page tables insidelow_vamap the first 2 MB of L2 guest virtual space (npd[0]) and theHVwindow (npd[PDE_IDX]) using standard 2 MB PTEs withPF_P|PF_RW|PF_US|PF_PS.greg_va/greg_pa— A second order-9 allocation. This page serves the double role: whennest_pd[PDE_IDX]holds a huge-page entry,greg_pais the physical frame being mapped as 2 MB; whennest_pd[PDE_IDX]holds a table entry, the first page ofgreg_va(ptg) acts as the 4 KB page table that L0 must shadow indirectly.q_va/q_pa— A single 4 KB probe page filled entirely with0x4141414141414141. Its only role is to appear atptg[PRIME_IDX]so that when the reused direct-split shadow page installs a leaf for it, the real gfn (Q) diverges from the direct assumption (sp->gfn + PRIME_IDX), causing theWARN_ONCEat[6]and subsequently the fatalpte_list_removermap mismatch.
The Key Geometry
[10], ptg (the nested PT page) is taken as the first page of greg — so ptg_pa == greg_pa. The consequences are:
- When
nest_pd[PDE_IDX]mapsgregas a 2 MB large page (as at[11]), thegfnof the mapping isgreg_pa >> 12. L0’s shadow MMU also uses this value as thetable_gfnit will pass tokvm_mmu_get_child_spwhen processing the PDE. - When
nest_pd[PDE_IDX]instead points toptgas a page table,table_gfn = ptg_pa >> 12 = greg_pa >> 12. Identical.
direct=1), while the table-path demands an indirect shadow page (direct=0). This is the mismatch that the buggy reuse check at [1] in kvm_mmu_get_child_sp fails to catch.
At [12], ptg[PRIME_IDX] is pointed at q_pa. When the reused direct-split page later installs a leaf SPTE for gfn Q through slot PRIME_IDX, the shadow page believes the leaf gfn should be sp->gfn + PRIME_IDX — not the actual gfn of Q. This divergence is what breaks the rmap invariant and triggers the host panic.
gfn Match / Role Mismatch Table
| PDE configuration | Physical target | table_gfn passed to shadow MMU | Required role.direct |
|---|---|---|---|
huge_pte(greg_pa) | greg_pa mapped as 2 MB leaf | greg_pa >> 12 | 1 (direct split) |
tbl_pte(ptg_pa) | ptg_pa as page table | ptg_pa >> 12 = greg_pa >> 12 | 0 (indirect shadow) |
[1] return -EEXIST when it should not.
L2 Guest Code
The L2 guest image (ncode) placed inside low_va at offset N_CODE is a minimal stub:
movabs rax, GVA; mov rax,[rax]; vmcall; hlt. The mov rax,[rax] dereferences GVA (set to HV for the normal fault path, or GVA_PRIME for the race path), inducing a nested EPT/NPT violation that forces L0 into ept_page_fault → FNAME(fetch) → kvm_mmu_get_child_sp. The vmcall (Intel) or vmmcall (AMD) signals normal exit so the faulter kthread can loop again immediately.
The race path uses
N_CODE_RACE which loads GVA_PRIME = HV + (0x100UL << 12), i.e. the virtual address that maps through ptg[PRIME_IDX] — the slot that points at the q probe page. This is what drives the leaf installation for gfn Q through the reused direct-split shadow page.Nested Page Table Structure
The full nested page table tree built bybuild_world() for L2 is:
the_root) is constructed by ops->mk_root(nest_pml4_pa) — for Intel this ORs in the EPT memory-type and walk-length fields (0x1e); for AMD the PML4 physical address is used as-is as nested_cr3.