From: Ethan Yang 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] Signed-off-by: Sean Christopherson --- arch/x86/kvm/x86.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 0a1b63c63d1a..c35d359b56dd 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) @@ -3647,18 +3652,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; -- 2.53.0.1213.gd9a14994de-goog Drop kvm_vcpu_arch.delivery_as_pf_vmexit and instead use msr_en_val as the source of truth to reduce the probability of operating on stale data. This fixes flaws where KVM fails to update delivery_as_pf_vmexit when APF is explicitly disabled by the guest or implicitly disabled by KVM on INIT. Absent other bugs, the flaws are benign as KVM *shouldn't* consume delivery_as_pf_vmexit when PV APF support is disabled. Simply delete the field, as there's zero benefit to maintaining a separate "cache" of the state. Fixes: 52a5c155cf79 ("KVM: async_pf: Let guest support delivery of async_pf from guest mode") Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm_host.h | 1 - arch/x86/kvm/x86.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index c470e40a00aa..fae1f4aeca5a 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1039,7 +1039,6 @@ struct kvm_vcpu_arch { u32 id; u32 host_apf_flags; bool send_always; - bool delivery_as_pf_vmexit; bool pageready_pending; } apf; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c35d359b56dd..4632222a5d1c 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3666,7 +3666,6 @@ static int kvm_pv_enable_async_pf(struct kvm_vcpu *vcpu, u64 data) } 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; kvm_async_pf_wakeup_all(vcpu); @@ -14035,7 +14034,7 @@ static bool kvm_can_deliver_async_pf(struct kvm_vcpu *vcpu) * L1 needs to opt into the special #PF vmexits that are * used to deliver async page faults. */ - return vcpu->arch.apf.delivery_as_pf_vmexit; + return vcpu->arch.apf.msr_en_val & KVM_ASYNC_PF_DELIVERY_AS_PF_VMEXIT; } else { /* * Play it safe in case the guest temporarily disables paging. -- 2.53.0.1213.gd9a14994de-goog Drop kvm_vcpu_arch.send_always and instead use msr_en_val as the source of truth to reduce the probability of operating on stale data. This fixes flaws where KVM fails to update send_always when APF is explicitly disabled by the guest or implicitly disabled by KVM on INIT. Absent other bugs, the flaws are benign as KVM *shouldn't* consume send_always when PV APF support is disabled. Simply delete the field, as there's zero benefit to maintaining a separate "cache" of the state. Opportunistically turn the enabled vs. disabled logic at the end of kvm_pv_enable_async_pf() into an if-else instead of using an early return, e.g. so that it's more obvious that both paths are "success" paths. Fixes: 6adba5274206 ("KVM: Let host know whether the guest can handle async PF in non-userspace context.") Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm_host.h | 1 - arch/x86/kvm/x86.c | 12 ++++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index fae1f4aeca5a..2a6906597637 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1038,7 +1038,6 @@ struct kvm_vcpu_arch { u16 vec; u32 id; u32 host_apf_flags; - bool send_always; bool pageready_pending; } apf; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4632222a5d1c..e24877353f17 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3659,16 +3659,12 @@ static int kvm_pv_enable_async_pf(struct kvm_vcpu *vcpu, u64 data) vcpu->arch.apf.msr_en_val = data; - if (!__kvm_pv_async_pf_enabled(data)) { + if (__kvm_pv_async_pf_enabled(data)) { + kvm_async_pf_wakeup_all(vcpu); + } else { kvm_clear_async_pf_completion_queue(vcpu); kvm_async_pf_hash_reset(vcpu); - return 0; } - - vcpu->arch.apf.send_always = (data & KVM_ASYNC_PF_SEND_ALWAYS); - - kvm_async_pf_wakeup_all(vcpu); - return 0; } @@ -14025,7 +14021,7 @@ static bool kvm_can_deliver_async_pf(struct kvm_vcpu *vcpu) if (!kvm_pv_async_pf_enabled(vcpu)) return false; - if (!vcpu->arch.apf.send_always && + if (!(vcpu->arch.apf.msr_en_val & KVM_ASYNC_PF_SEND_ALWAYS) && (vcpu->arch.guest_state_protected || !kvm_x86_call(get_cpl)(vcpu))) return false; -- 2.53.0.1213.gd9a14994de-goog