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". Thanks, Paolo