Using the full 64-bit register values is wrong in this case; especially
soon after a mode switch from long mode to 32-bit one upper halves of
registers may continue to be non-zero.

Fixes: 09fce8016596 ("Nested VMX: Emulation of guest VMXON/OFF instruction")
Signed-off-by: Jan Beulich <jbeul...@suse.com>
---
Note that the affected VMX insns are invalid to use from compatibility
mode, and hence the more expensive vmx_guest_x86_mode() doesn't need
using here. (VMCALL and VMFUNC, which are permitted in compatibility
mode, aren't taking this path. In fact both aren't dealt with at all
[explicitly] in vvmx.c.)
---
v2: Add code comment.

--- a/xen/arch/x86/hvm/vmx/vvmx.c
+++ b/xen/arch/x86/hvm/vmx/vvmx.c
@@ -360,7 +360,18 @@ enum vmx_insn_errno set_vvmcs_real_safe(
 static unsigned long reg_read(struct cpu_user_regs *regs,
                               unsigned int index)
 {
-    return *decode_gpr(regs, index);
+    unsigned long val = *decode_gpr(regs, index);
+
+    /*
+     * Outside of 64-bit mode, zero-extend the result from 32 bits, like
+     * hardware would.
+     * NB: A long-mode check is sufficient here, as insns this logic is used
+     * for will #UD in compatibility mode (and hence not make it here).
+     */
+    if ( !hvm_long_mode_active(current) )
+        val = (uint32_t)val;
+
+    return val;
 }
 
 static void reg_write(struct cpu_user_regs *regs,


Reply via email to