From: Ethan Yang Date: Mon, 6 Apr 2026 22:53:56 +0000 (-0700) Subject: KVM: x86: Don't leave APF half-enabled on bad APF data GPA X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=923ca078f08cbdf526d1e8df9bbb824b14c8ed9d;p=thirdparty%2Flinux.git KVM: x86: Don't leave APF half-enabled on bad APF data GPA kvm_pv_enable_async_pf() updates vcpu->arch.apf.msr_en_val before initializing the APF data gfn_to_hva cache. If userspace provides an invalid GPA, kvm_gfn_to_hva_cache_init() fails, but msr_en_val stays enabled and leaves APF state half-initialized. Later APF paths can then try to use the empty cache and trigger WARN_ON() in kvm_read_guest_offset_cached(). Determine the new APF enabled state from the incoming MSR value, do cache initialization first on the enable path, and commit msr_en_val only after successful initialization. Keep the disable path behavior unchanged. Reported-by: syzbot+bc0e18379a290e5edfe4@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=bc0e18379a290e5edfe4 Fixes: 344d9588a9df ("KVM: Add PV MSR to enable asynchronous page faults delivery.") Link: https://lore.kernel.org/r/aHfD3MczrDpzDX9O@google.com Suggested-by: Sean Christopherson Reviewed-by: Xiaoyao Li Signed-off-by: Ethan Yang [sean: don't bother with a local "enable" variable] Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260406225359.1245490-2-seanjc@google.com Signed-off-by: Sean Christopherson --- diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 810ff08780d1..82dce54ac505 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1043,11 +1043,16 @@ bool kvm_require_dr(struct kvm_vcpu *vcpu, int dr) } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_require_dr); -static bool kvm_pv_async_pf_enabled(struct kvm_vcpu *vcpu) +static bool __kvm_pv_async_pf_enabled(u64 data) { u64 mask = KVM_ASYNC_PF_ENABLED | KVM_ASYNC_PF_DELIVERY_AS_INT; - return (vcpu->arch.apf.msr_en_val & mask) == mask; + return (data & mask) == mask; +} + +static bool kvm_pv_async_pf_enabled(struct kvm_vcpu *vcpu) +{ + return __kvm_pv_async_pf_enabled(vcpu->arch.apf.msr_en_val); } static inline u64 pdptr_rsvd_bits(struct kvm_vcpu *vcpu) @@ -3648,18 +3653,19 @@ static int kvm_pv_enable_async_pf(struct kvm_vcpu *vcpu, u64 data) if (!lapic_in_kernel(vcpu)) return data ? 1 : 0; + if (__kvm_pv_async_pf_enabled(data) && + kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.apf.data, gpa, + sizeof(u64))) + return 1; + vcpu->arch.apf.msr_en_val = data; - if (!kvm_pv_async_pf_enabled(vcpu)) { + if (!__kvm_pv_async_pf_enabled(data)) { kvm_clear_async_pf_completion_queue(vcpu); kvm_async_pf_hash_reset(vcpu); return 0; } - if (kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.apf.data, gpa, - sizeof(u64))) - return 1; - vcpu->arch.apf.send_always = (data & KVM_ASYNC_PF_SEND_ALWAYS); vcpu->arch.apf.delivery_as_pf_vmexit = data & KVM_ASYNC_PF_DELIVERY_AS_PF_VMEXIT;