[PATCH v2 01/20] KVM: x86/xen: Fill the hypercall page with a single guest write
From: David Woodhouse
Date: Wed Nov 04 2026 - 22:10:17 EST
From: David Woodhouse <dwmw@xxxxxxxxxxxx>
Writing the hypercall page as 128 separate 32-byte guest writes pushes
128 entries into the dirty ring for the same gfn. The ring's headroom
above the soft limit is KVM_DIRTY_RING_RSVD_ENTRIES (64) plus
kvm_cpu_dirty_log_size(), so when the CPU assists with dirty logging
(PML adds 512) a single WRMSR fits; without that assist (e.g. VMX with
PML disabled) one guest WRMSR can overflow it: a guest-reachable
WARN_ON_ONCE() in kvm_dirty_ring_push(), and an unharvested entry
overwritten, losing a page from dirty tracking during live migration.
Build the page in a buffer and write it with one call.
Fixes: fb04a1eddb1a ("KVM: X86: Implement ring-based dirty memory tracking")
Signed-off-by: David Woodhouse <dwmw@xxxxxxxxxxxx>
---
arch/x86/kvm/xen.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 6aa00f746cba..5875d10ae3a9 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -1345,7 +1345,8 @@ int kvm_xen_write_hypercall_page(struct kvm_vcpu *vcpu, u64 data)
*/
if (kvm_xen_hypercall_enabled(kvm)) {
u8 instructions[32];
- int i;
+ u8 *page;
+ int ret, i;
if (page_num)
return 1;
@@ -1362,13 +1363,20 @@ int kvm_xen_write_hypercall_page(struct kvm_vcpu *vcpu, u64 data)
/* int3 to pad */
memset(instructions + 9, 0xcc, sizeof(instructions) - 9);
+ page = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
for (i = 0; i < PAGE_SIZE / sizeof(instructions); i++) {
*(u32 *)&instructions[1] = i;
- if (kvm_vcpu_write_guest(vcpu,
- page_addr + (i * sizeof(instructions)),
- instructions, sizeof(instructions)))
- return 1;
+ memcpy(page + (i * sizeof(instructions)), instructions,
+ sizeof(instructions));
}
+
+ ret = kvm_vcpu_write_guest(vcpu, page_addr, page, PAGE_SIZE);
+ kfree(page);
+ if (ret)
+ return 1;
} else {
/*
* Note, truncation is a non-issue as 'lm' is guaranteed to be
--
2.43.0