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

[Xen-devel] [PATCH] Clean up SVM instruction-fetch code some more



SVM: clean up __get_instruction_length_from_list()

Remove unused arguments, fix its behaviour near page boundaries,
inject appropriate pagefaults, and kill the guest only if the
instruction is not decodable or %eip is not pointing to valid RAM.

Signed-off-by: Tim Deegan <Tim.Deegan@xxxxxxxxxx>

diff -r 483d006cc607 -r c20b8931b598 xen/arch/x86/hvm/svm/emulate.c
--- a/xen/arch/x86/hvm/svm/emulate.c    Fri Apr 25 13:46:27 2008 +0100
+++ b/xen/arch/x86/hvm/svm/emulate.c    Fri May 02 16:28:05 2008 +0100
@@ -28,18 +28,6 @@
 #include <asm/hvm/svm/emulate.h>
 
 #define MAX_INST_LEN 15
-
-static int inst_copy_from_guest(
-    unsigned char *buf, unsigned long guest_eip, int inst_len)
-{
-    struct vmcb_struct *vmcb = current->arch.hvm_svm.vmcb;
-    uint32_t pfec = (vmcb->cpl == 3) ? PFEC_user_mode : 0;
-    if ( (inst_len > MAX_INST_LEN) || (inst_len <= 0) )
-        return 0;
-    if ( hvm_fetch_from_guest_virt_nofault(buf, guest_eip, inst_len, pfec) )
-        return 0;
-    return inst_len;
-}
 
 static unsigned int is_prefix(u8 opc)
 {
@@ -101,29 +89,47 @@ static const u8 *opc_bytes[INSTR_MAX_COU
     [INSTR_INT3]   = OPCODE_INT3
 };
 
+static int fetch(struct vcpu *v, u8 *buf, unsigned long addr, int len)
+{
+    uint32_t pfec = (v->arch.hvm_svm.vmcb->cpl == 3) ? PFEC_user_mode : 0;
+
+    switch ( hvm_fetch_from_guest_virt(buf, addr, len, pfec) )
+    {
+    case HVMCOPY_okay:
+        return 1;
+    case HVMCOPY_bad_gva_to_gfn:
+        /* OK just to give up; we'll have injected #PF already */
+        return 0;
+    case HVMCOPY_bad_gfn_to_mfn:
+    default:
+        /* Not OK: fetches from non-RAM pages are not supportable. */
+        gdprintk(XENLOG_ERR, "Bad instruction fetch at %#lx (%#lx)\n",
+                 (unsigned long) guest_cpu_user_regs()->eip, addr);
+        domain_crash(v->domain);
+        return 0;
+    }
+}
+
 int __get_instruction_length_from_list(struct vcpu *v,
-        enum instruction_index *list, unsigned int list_count, 
-        u8 *guest_eip_buf, enum instruction_index *match)
+        enum instruction_index *list, unsigned int list_count)
 {
     struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
     unsigned int i, j, inst_len = 0;
     int found = 0;
     enum instruction_index instr = 0;
-    u8 buffer[MAX_INST_LEN];
-    u8 *buf;
+    u8 buf[MAX_INST_LEN];
     const u8 *opcode = NULL;
-
-    if ( guest_eip_buf )
-    {
-        buf = guest_eip_buf;
-    }
-    else
-    {
-        if ( inst_copy_from_guest(buffer, svm_rip2pointer(v), MAX_INST_LEN)
-             != MAX_INST_LEN )
-            return 0;
-        buf = buffer;
-    }
+    unsigned long fetch_addr;
+    int fetch_len;
+
+    /* Fetch up to the next page break; we'll fetch from the next page
+     * later if we have to. */
+    fetch_addr = svm_rip2pointer(v);
+    fetch_len = PAGE_SIZE - (fetch_addr & ~PAGE_MASK) ;
+    if ( fetch_len > MAX_INST_LEN ) 
+        fetch_len = MAX_INST_LEN;
+    if ( !fetch(v, buf, fetch_addr, fetch_len) )
+        return 0;
 
     for ( j = 0; j < list_count; j++ )
     {
@@ -134,17 +140,36 @@ int __get_instruction_length_from_list(s
         while ( (inst_len < MAX_INST_LEN) && 
                 is_prefix(buf[inst_len]) && 
                 !is_prefix(opcode[1]) )
+        {
             inst_len++;
+            if ( inst_len >= fetch_len ) 
+            { 
+                if ( !fetch(v, buf + fetch_len,
+                            fetch_addr + fetch_len,
+                            MAX_INST_LEN - fetch_len) )
+                    return 0;
+                fetch_len = MAX_INST_LEN;
+            }
+        }
 
         ASSERT(opcode[0] <= 15);    /* Make sure the table is correct. */
         found = 1;
 
-        for ( i = 0; i < opcode[0]; i++ )
+        for ( i = 0; i < opcode[0] && inst_len + i < MAX_INST_LEN; i++ )
         {
             /* If the last byte is zero, we just accept it without checking */
             if ( (i == (opcode[0]-1)) && (opcode[i+1] == 0) )
                 break;
 
+            if ( inst_len + i >= fetch_len ) 
+            { 
+                if ( !fetch(v, buf + fetch_len, 
+                            fetch_addr + fetch_len, 
+                            MAX_INST_LEN - fetch_len) ) 
+                    return 0;
+                fetch_len = MAX_INST_LEN;
+            }
+
             if ( buf[inst_len+i] != opcode[i+1] )
             {
                 found = 0;
@@ -158,13 +183,12 @@ int __get_instruction_length_from_list(s
 
     printk("%s: Mismatch between expected and actual instruction bytes: "
             "eip = %lx\n",  __func__, (unsigned long)vmcb->rip);
+    domain_crash(v->domain);
     return 0;
 
  done:
     inst_len += opcode[0];
     ASSERT(inst_len <= MAX_INST_LEN);
-    if ( match )
-        *match = instr;
     return inst_len;
 }
 
diff -r 483d006cc607 -r c20b8931b598 xen/arch/x86/hvm/svm/svm.c
--- a/xen/arch/x86/hvm/svm/svm.c        Fri Apr 25 13:46:27 2008 +0100
+++ b/xen/arch/x86/hvm/svm/svm.c        Fri May 02 16:28:05 2008 +0100
@@ -84,7 +84,10 @@ static void inline __update_guest_eip(
 {
     struct vcpu *curr = current;
 
-    if ( unlikely((inst_len == 0) || (inst_len > 15)) )
+    if ( unlikely(inst_len == 0) )
+        return;
+
+    if ( unlikely(inst_len > 15) )
     {
         gdprintk(XENLOG_ERR, "Bad instruction length %u\n", inst_len);
         domain_crash(curr->domain);
@@ -907,7 +910,7 @@ static void svm_vmexit_do_cpuid(struct c
 {
     unsigned int eax, ebx, ecx, edx, inst_len;
 
-    inst_len = __get_instruction_length(current, INSTR_CPUID, NULL);
+    inst_len = __get_instruction_length(current, INSTR_CPUID);
     if ( inst_len == 0 ) 
         return;
 
@@ -1084,12 +1087,12 @@ static void svm_do_msr_access(struct cpu
     if ( vmcb->exitinfo1 == 0 )
     {
         rc = hvm_msr_read_intercept(regs);
-        inst_len = __get_instruction_length(v, INSTR_RDMSR, NULL);
+        inst_len = __get_instruction_length(v, INSTR_RDMSR);
     }
     else
     {
         rc = hvm_msr_write_intercept(regs);
-        inst_len = __get_instruction_length(v, INSTR_WRMSR, NULL);
+        inst_len = __get_instruction_length(v, INSTR_WRMSR);
     }
 
     if ( rc == X86EMUL_OKAY )
@@ -1103,7 +1106,7 @@ static void svm_vmexit_do_hlt(struct vmc
     struct hvm_intack intack = hvm_vcpu_has_pending_irq(curr);
     unsigned int inst_len;
 
-    inst_len = __get_instruction_length(curr, INSTR_HLT, NULL);
+    inst_len = __get_instruction_length(curr, INSTR_HLT);
     if ( inst_len == 0 )
         return;
     __update_guest_eip(regs, inst_len);
@@ -1140,7 +1143,7 @@ static void svm_vmexit_do_invalidate_cac
     svm_wbinvd_intercept();
 
     inst_len = __get_instruction_length_from_list(
-        current, list, ARRAY_SIZE(list), NULL, NULL);
+        current, list, ARRAY_SIZE(list));
     __update_guest_eip(regs, inst_len);
 }
 
@@ -1216,7 +1219,7 @@ asmlinkage void svm_vmexit_handler(struc
         if ( !v->domain->debugger_attached )
             goto exit_and_crash;
         /* AMD Vol2, 15.11: INT3, INTO, BOUND intercepts do not update RIP. */
-        inst_len = __get_instruction_length(v, INSTR_INT3, NULL);
+        inst_len = __get_instruction_length(v, INSTR_INT3);
         __update_guest_eip(regs, inst_len);
         domain_pause_for_debugger();
         break;
@@ -1293,7 +1296,7 @@ asmlinkage void svm_vmexit_handler(struc
         break;
 
     case VMEXIT_VMMCALL:
-        inst_len = __get_instruction_length(v, INSTR_VMCALL, NULL);
+        inst_len = __get_instruction_length(v, INSTR_VMCALL);
         if ( inst_len == 0 )
             break;
         HVMTRACE_1D(VMMCALL, v, regs->eax);
diff -r 483d006cc607 -r c20b8931b598 xen/include/asm-x86/hvm/svm/emulate.h
--- a/xen/include/asm-x86/hvm/svm/emulate.h     Fri Apr 25 13:46:27 2008 +0100
+++ b/xen/include/asm-x86/hvm/svm/emulate.h     Fri May 02 16:28:05 2008 +0100
@@ -34,15 +34,12 @@ enum instruction_index {
 };
 
 int __get_instruction_length_from_list(
-    struct vcpu *v,
-    enum instruction_index *list, unsigned int list_count, 
-    u8 *guest_eip_buf, enum instruction_index *match);
+    struct vcpu *v, enum instruction_index *list, unsigned int list_count);
 
 static inline int __get_instruction_length(struct vcpu *v, 
-        enum instruction_index instr, u8 *guest_eip_buf)
+                                           enum instruction_index instr)
 {
-    return __get_instruction_length_from_list(
-        v, &instr, 1, guest_eip_buf, NULL);
+    return __get_instruction_length_from_list(v, &instr, 1);
 }
 
 #endif /* __ASM_X86_HVM_SVM_EMULATE_H__ */

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

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