Re: [BUG] KVM: x86: Hyper-V VP-assist page reuses the KVM PV-EOI gfn_to_hva_cache, so enabling both WARNs and fails KVM_RUN

From: Sean Christopherson

Date: Sat Oct 17 2026 - 10:06:18 EST


On Sat, Oct 17, 2026, Amirmohammad Eftekhar wrote:
> Hi,
>
> Reporting a guest self-DoS (correctness/robustness; not memory-safety, not
> guest->host).

And a KVM WARN, that's the only reason I care about "fixing" this.

> The Hyper-V VP-assist page and KVM's PV-EOI feature share a single
> gfn_to_hva_cache, vcpu->arch.pv_eoi.data:

I'm still flabbergasted that this is the case (I recently learned about this thanks
to another AI-found bug).

> - Writing HV_X64_MSR_VP_ASSIST_PAGE arms the cache for the VP-assist page via
> kvm_lapic_set_pv_eoi(vcpu, gfn_to_gpa(gfn) | KVM_MSR_ENABLED,
> sizeof(struct hv_vp_assist_page)) /* hyperv.c ~1600 */
> - kvm_hv_get_assist_page() then reads it back with that same cache and the
> VP-assist length:
> kvm_read_guest_cached(kvm, &vcpu->arch.pv_eoi.data,
> &hv_vcpu->vp_assist_page,
> sizeof(struct hv_vp_assist_page)) /* hyperv.c ~952 */
>
> pv_eoi.data is the one gfn_to_hva_cache shared between the two features. A guest
> that has VP-assist enabled and then writes MSR_KVM_PV_EOI_EN (a different GPA,
> len 1) re-initializes pv_eoi.data for the 1-byte PV-EOI object while VP-assist
> stays enabled. The next kvm_hv_get_assist_page() then does a cached read of
> sizeof(struct hv_vp_assist_page) against a cache set up for len 1 -> the
> length/GPA mismatch trips the cache and KVM fails the read, surfacing as a WARN
> and a failed KVM_RUN for a guest that did nothing architecturally illegal (the
> two features are independent and both legitimately enableable).

Eh, given that no one has ever complained, and that enabling both at different
locations would probably break the guest anyways, I don't see any reason to go
out of our way to make this work. If someone wants to clean up the Hyper-V mess,
they're more than welcome to, but for an immediate fix, I just want to prevent
KVM from tripping the WARN.

So as a stopgap (which will probably become the long-term behavior...), this?

diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index c18f45f66c24..88cc5a6ce170 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -947,6 +947,15 @@ int kvm_hv_get_assist_page(struct kvm_vcpu *vcpu)
if (!hv_vcpu || !kvm_hv_assist_page_enabled(vcpu))
return -EFAULT;

+ /*
+ * KVM reuses its own PV EOI feature to emulate Hyper-V's EOI Assist,
+ * and rather stupidly reuses the same MSR and cache storage. Treat
+ * the assist page as inaccessible if the shared state is configured
+ * only for PV EOI's single byte of data.
+ */
+ if (vcpu->arch.pv_eoi.data.len < sizeof(struct hv_vp_assist_page))
+ return -EFAULT;
+
return kvm_read_guest_cached(vcpu->kvm, &vcpu->arch.pv_eoi.data,
&hv_vcpu->vp_assist_page, sizeof(struct hv_vp_assist_page));
}