diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index 25ae7505c3..280dab746d 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -2486,7 +2486,7 @@ void vmx_handle_EOI_induced_exit(struct vlapic *vlapic, int vector) void vmx_vmexit_handler(struct cpu_user_regs *regs) { - unsigned int exit_reason, idtv_info, intr_info = 0, vector = 0; + unsigned int exit_reason, idtv_info, intr_info = 0, vector = 0, mode; unsigned long exit_qualification, inst_len = 0; struct vcpu *v = current; @@ -2998,6 +2998,40 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs) out: if ( nestedhvm_vcpu_in_guestmode(v) ) nvmx_idtv_handling(); + + /* + * VM entry will fail (causing the guest to get crashed) if rIP (and + * rFLAGS, but we don't have an issue there) doesn't meet certain + * criteria. As we must not allow less than fully privileged mode to have + * such an effect on the domain, we correct rIP in that case (accepting + * this not being architecturally correct behavior, as the injected #GP + * fault will then not see the correct [invalid] return address). + * And since we know the guest will crash, we crash it right away if it + * already is in most privileged mode. + */ + mode = vmx_guest_x86_mode(v); + if ( mode == 8 ? !is_canonical_address(regs->rip) + : regs->rip != regs->_eip ) + { + struct segment_register ss; + + gdprintk(XENLOG_WARNING, "Bad rIP %lx for mode %u\n", regs->rip, mode); + + vmx_get_segment_register(v, x86_seg_ss, &ss); + if ( ss.attr.fields.dpl ) + { + if ( !(__vmread(VM_ENTRY_INTR_INFO) & INTR_INFO_VALID_MASK) ) + hvm_inject_hw_exception(TRAP_gp_fault, 0); + /* Need to fix rIP nevertheless. */ + if ( mode == 8 ) + regs->rip = (long)(regs->rip << (64 - VADDR_BITS)) >> + (64 - VADDR_BITS); + else + regs->rip = regs->_eip; + } + else + domain_crash(v->domain); + } } void vmx_vmenter_helper(void)