[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[xen stable-4.15] x86/HVM: clear upper halves of GPRs upon entry from 32-bit code



commit 1a41979210b7d276824dc4660f4f58a1d6dea3ca
Author:     Jan Beulich <jbeulich@xxxxxxxx>
AuthorDate: Wed Mar 27 17:31:38 2024 +0000
Commit:     Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
CommitDate: Fri Apr 5 15:44:25 2024 +0100

    x86/HVM: clear upper halves of GPRs upon entry from 32-bit code
    
    Hypercalls in particular can be the subject of continuations, and logic
    there checks updated state against incoming register values. If the
    guest manufactured a suitable argument register with a non-zero upper
    half before entering compatibility mode and issuing a hypercall from
    there, checks in hypercall_xlat_continuation() might trip.
    
    Since for HVM we want to also be sure to not hit a corner case in the
    emulator, initiate the clipping right from the top of
    {svm,vmx}_vmexit_handler(). Also rename the invoked function, as it no
    longer does only invalidation of fields.
    
    Note that architecturally the upper halves of registers are undefined
    after a switch between compatibility and 64-bit mode (either direction).
    Hence once having entered compatibility mode, the guest can't assume
    the upper half of any register to retain its value.
    
    This is part of XSA-454 / CVE-2023-46842.
    
    Fixes: b8a7efe8528a ("Enable compatibility mode operation for 
HYPERVISOR_memory_op")
    Reported-by: Manuel Andreas <manuel.andreas@xxxxxx>
    Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
    Reviewed-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
    (cherry picked from commit 6a98383b0877bb66ebfe189da43bf81abe3d7909)
---
 xen/arch/x86/hvm/svm/svm.c    |  3 ++-
 xen/arch/x86/hvm/vmx/vmx.c    |  6 +++++-
 xen/include/asm-x86/hvm/hvm.h | 18 +++++++++++++++++-
 3 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index ba17bfe97a..0cc5e07f46 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -2592,7 +2592,8 @@ void svm_vmexit_handler(struct cpu_user_regs *regs)
     regs->rsp = vmcb->rsp;
     regs->rflags = vmcb->rflags;
 
-    hvm_invalidate_regs_fields(regs);
+    hvm_sanitize_regs_fields(
+        regs, !(vmcb_get_efer(vmcb) & EFER_LMA) || !(vmcb->cs.l));
 
     if ( paging_mode_hap(v->domain) )
         v->arch.hvm.guest_cr[3] = v->arch.hvm.hw_cr[3] = vmcb_get_cr3(vmcb);
diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index 54023a9258..8bb4a6e042 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -3932,6 +3932,7 @@ static int vmx_handle_apic_write(void)
 void vmx_vmexit_handler(struct cpu_user_regs *regs)
 {
     unsigned long exit_qualification, exit_reason, idtv_info, intr_info = 0;
+    unsigned long cs_ar_bytes = 0;
     unsigned int vector = 0, mode;
     struct vcpu *v = current;
     struct domain *currd = v->domain;
@@ -3940,7 +3941,10 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs)
     __vmread(GUEST_RSP,    &regs->rsp);
     __vmread(GUEST_RFLAGS, &regs->rflags);
 
-    hvm_invalidate_regs_fields(regs);
+    if ( hvm_long_mode_active(v) )
+        __vmread(GUEST_CS_AR_BYTES, &cs_ar_bytes);
+
+    hvm_sanitize_regs_fields(regs, !(cs_ar_bytes & X86_SEG_AR_CS_LM_ACTIVE));
 
     if ( paging_mode_hap(v->domain) )
     {
diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h
index b8dc445550..8e7a6b922d 100644
--- a/xen/include/asm-x86/hvm/hvm.h
+++ b/xen/include/asm-x86/hvm/hvm.h
@@ -564,8 +564,24 @@ static inline unsigned int hvm_get_insn_bytes(struct vcpu 
*v, uint8_t *buf)
             ? alternative_call(hvm_funcs.get_insn_bytes, v, buf) : 0);
 }
 
-static inline void hvm_invalidate_regs_fields(struct cpu_user_regs *regs)
+static inline void hvm_sanitize_regs_fields(struct cpu_user_regs *regs,
+                                            bool compat)
 {
+    if ( compat )
+    {
+        /* Clear GPR upper halves, to counteract guests playing games. */
+        regs->rbp = (uint32_t)regs->ebp;
+        regs->rbx = (uint32_t)regs->ebx;
+        regs->rax = (uint32_t)regs->eax;
+        regs->rcx = (uint32_t)regs->ecx;
+        regs->rdx = (uint32_t)regs->edx;
+        regs->rsi = (uint32_t)regs->esi;
+        regs->rdi = (uint32_t)regs->edi;
+        regs->rip = (uint32_t)regs->eip;
+        regs->rflags = (uint32_t)regs->eflags;
+        regs->rsp = (uint32_t)regs->esp;
+    }
+
 #ifndef NDEBUG
     regs->error_code = 0xbeef;
     regs->entry_vector = 0xbeef;
--
generated by git-patchbot for /home/xen/git/xen.git#stable-4.15



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.