Paolo Bonzini <pbonzini@xxxxxxxxxx> writes: > On 04/06/20 16:31, Vitaly Kuznetsov wrote: >> Syzbot reports the following issue: >> >> WARNING: CPU: 0 PID: 6819 at arch/x86/kvm/x86.c:618 kvm_inject_emulated_page_fault+0x210/0x290 arch/x86/kvm/x86.c:618 >> ... >> Call Trace: >> ... >> RIP: 0010:kvm_inject_emulated_page_fault+0x210/0x290 arch/x86/kvm/x86.c:618 >> ... >> nested_vmx_get_vmptr+0x1f9/0x2a0 arch/x86/kvm/vmx/nested.c:4638 >> handle_vmon arch/x86/kvm/vmx/nested.c:4767 [inline] >> handle_vmon+0x168/0x3a0 arch/x86/kvm/vmx/nested.c:4728 >> vmx_handle_exit+0x29c/0x1260 arch/x86/kvm/vmx/vmx.c:6067 >> >> 'exception' we're trying to inject with kvm_inject_emulated_page_fault() comes from >> nested_vmx_get_vmptr() >> kvm_read_guest_virt() >> kvm_read_guest_virt_helper() >> vcpu->arch.walk_mmu->gva_to_gpa() >> >> but it is only set when GVA to GPA conversion fails. In case it doesn't but >> we still fail kvm_vcpu_read_guest_page(), X86EMUL_IO_NEEDED is returned and >> nested_vmx_get_vmptr() calls kvm_inject_emulated_page_fault() with zeroed >> 'exception'. This happen when e.g. VMXON/VMPTRLD/VMCLEAR argument is MMIO. >> >> KVM could've handled the request correctly by going to userspace and >> performing I/O but there doesn't seem to be a good need for such requests >> in the first place. Sane guests should not call VMXON/VMPTRLD/VMCLEAR with >> anything but normal memory. Just inject #GP to find insane ones. >> >> Reported-by: syzbot+2a7156e11dc199bdbd8a@xxxxxxxxxxxxxxxxxxxxxxxxx >> Signed-off-by: Vitaly Kuznetsov <vkuznets@xxxxxxxxxx> >> --- >> arch/x86/kvm/vmx/nested.c | 19 +++++++++++++++++-- >> 1 file changed, 17 insertions(+), 2 deletions(-) >> >> diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c >> index 9c74a732b08d..05d57c3cb1ce 100644 >> --- a/arch/x86/kvm/vmx/nested.c >> +++ b/arch/x86/kvm/vmx/nested.c >> @@ -4628,14 +4628,29 @@ static int nested_vmx_get_vmptr(struct kvm_vcpu *vcpu, gpa_t *vmpointer) >> { >> gva_t gva; >> struct x86_exception e; >> + int r; >> >> if (get_vmx_mem_address(vcpu, vmx_get_exit_qual(vcpu), >> vmcs_read32(VMX_INSTRUCTION_INFO), false, >> sizeof(*vmpointer), &gva)) >> return 1; >> >> - if (kvm_read_guest_virt(vcpu, gva, vmpointer, sizeof(*vmpointer), &e)) { >> - kvm_inject_emulated_page_fault(vcpu, &e); >> + r = kvm_read_guest_virt(vcpu, gva, vmpointer, sizeof(*vmpointer), &e); >> + if (r != X86EMUL_CONTINUE) { >> + if (r == X86EMUL_PROPAGATE_FAULT) { >> + kvm_inject_emulated_page_fault(vcpu, &e); >> + } else { >> + /* >> + * X86EMUL_IO_NEEDED is returned when kvm_vcpu_read_guest_page() >> + * fails to read guest's memory (e.g. when 'gva' points to MMIO >> + * space). While KVM could've handled the request correctly by >> + * exiting to userspace and performing I/O, there doesn't seem >> + * to be a real use-case behind such requests, just inject #GP >> + * for now. >> + */ >> + kvm_inject_gp(vcpu, 0); >> + } >> + >> return 1; >> } >> >> > > Hi Vitaly, > > looks good but we need to do the same in handle_vmread, handle_vmwrite, > handle_invept and handle_invvpid. Which probably means adding something > like nested_inject_emulation_fault to commonize the inner "if". > Oh true, I've only looked at nested_vmx_get_vmptr() users to fix the immediate issue. Will do v2. -- Vitaly