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

Re: [Xen-ia64-devel] [PATCH] Fix vulnerability of copy_to_user in PAL emulation



Hi Alex,

Thank you for reviewing. 
I updated the patch as your comments. please apply.
I also long for a xencomm.

Thanks,
Kouya

Signed-off-by: Kouya Shimura <kouya@xxxxxxxxxxxxxx>

Alex Williamson writes:
>    I think there's a off by one issue as noted below.  Also a few
> cleanups.  Like Tristan, I'd rather see a xencomm for GFW, but that
> might be too intrusive for 3.2 at this point.  So this seems like a
> reasonable temporary fix.  Thanks,

diff -r 4054cd60895b xen/arch/ia64/vmx/pal_emul.c
--- a/xen/arch/ia64/vmx/pal_emul.c      Mon Dec 10 13:49:22 2007 +0000
+++ b/xen/arch/ia64/vmx/pal_emul.c      Tue Dec 11 15:02:02 2007 +0900
@@ -24,11 +24,12 @@
 #include <asm/pal.h>
 #include <asm/sal.h>
 
-void
+IA64FAULT
 pal_emul(struct vcpu *vcpu)
 {
        u64 gr28, gr29, gr30, gr31;
        struct ia64_pal_retval result;
+       IA64FAULT fault;
 
        vcpu_get_gr_nat(vcpu, 28, &gr28);  //bank1
 
@@ -38,12 +39,17 @@ pal_emul(struct vcpu *vcpu)
        vcpu_get_gr_nat(vcpu, 31, &gr31);
 
        perfc_incr(vmx_pal_emul);
-       result = xen_pal_emulator(gr28, gr29, gr30, gr31);
-
+       fault = xen_pal_emulator(&result, gr28, gr29, gr30, gr31);
+       if (fault != IA64_NO_FAULT) {
+               printk(XENLOG_DEBUG "PAL(%ld) TLB-miss 0x%lx 0x%lx 0x%lx\n",
+                      gr28, gr29, gr30, gr31);
+               return fault;
+       }
        vcpu_set_gr(vcpu, 8, result.status, 0);
        vcpu_set_gr(vcpu, 9, result.v0, 0);
        vcpu_set_gr(vcpu, 10, result.v1, 0);
        vcpu_set_gr(vcpu, 11, result.v2, 0);
+       return fault;
 }
 
 void
diff -r 4054cd60895b xen/arch/ia64/vmx/vmx_fault.c
--- a/xen/arch/ia64/vmx/vmx_fault.c     Mon Dec 10 13:49:22 2007 +0000
+++ b/xen/arch/ia64/vmx/vmx_fault.c     Wed Dec 12 11:47:04 2007 +0900
@@ -174,6 +174,7 @@ vmx_ia64_handle_break (unsigned long ifa
 {
     struct domain *d = current->domain;
     struct vcpu *v = current;
+    IA64FAULT fault;
 
     perfc_incr(vmx_ia64_handle_break);
 #ifdef CRASH_DEBUG
@@ -196,9 +197,10 @@ vmx_ia64_handle_break (unsigned long ifa
                 return IA64_NO_FAULT;
             }
             else if (iim == DOMN_PAL_REQUEST) {
-                pal_emul(v);
-                vcpu_increment_iip(v);
-                return IA64_NO_FAULT;
+                fault = pal_emul(v);
+                if (fault == IA64_NO_FAULT)
+                    vcpu_increment_iip(v);
+                return fault;
             } else if (iim == DOMN_SAL_REQUEST) {
                 sal_emul(v);
                 vcpu_increment_iip(v);
diff -r 4054cd60895b xen/arch/ia64/xen/fw_emul.c
--- a/xen/arch/ia64/xen/fw_emul.c       Mon Dec 10 13:49:22 2007 +0000
+++ b/xen/arch/ia64/xen/fw_emul.c       Wed Dec 12 12:06:17 2007 +0900
@@ -37,6 +37,7 @@
 #include <xen/softirq.h>
 #include <xen/time.h>
 #include <asm/debugger.h>
+#include <asm/vmx_phy_mode.h>
 
 static DEFINE_SPINLOCK(efi_time_services_lock);
 
@@ -82,6 +83,98 @@ static const char * const rec_name[] = {
 #else
 # define IA64_SAL_DEBUG(fmt...)
 #endif
+
+#define MIN_PAGE_SIZE 4096
+
+struct palcomm_ctxt {
+       void *va[2];
+};
+
+static void
+palcomm_done(struct palcomm_ctxt *comm)
+{
+       int i;
+       struct page_info* page;
+
+       for (i = 0; i < 2; i++) {
+               if (comm->va[i]) {
+                       page = virt_to_page(comm->va[i]);
+                       put_page(page);
+                       comm->va[i] = NULL;
+               }
+       }
+}
+
+static IA64FAULT
+palcomm_init(struct palcomm_ctxt *comm, unsigned long buf, long size)
+{
+       IA64FAULT fault = IA64_NO_FAULT;
+       int i;
+       unsigned long paddr, maddr, poff;
+       struct page_info* page;
+
+       BUG_ON((unsigned)size > MIN_PAGE_SIZE);
+
+       /* check for vulnerability. NB - go through in metaphysical mode. */
+       if (IS_VMM_ADDRESS(buf) || IS_VMM_ADDRESS(buf + size - 1))
+               panic_domain(NULL, "copy to bad address:0x%lx\n", buf);
+
+       comm->va[0] = comm->va[1] = NULL;
+
+       /* XXX: not implemented for PV domain */
+       if (!VMX_DOMAIN(current))
+               return fault;
+
+       for (i = 0; i < 2; i++) {
+               if (is_virtual_mode(current)) {
+                       fault = vmx_vcpu_tpa(current, buf, &paddr);
+                       if (fault != IA64_NO_FAULT)
+                               goto fail;
+               } else
+                       paddr = buf;
+               maddr = paddr_to_maddr(paddr);
+               if (maddr == 0)
+                       goto fail;
+               page = maddr_to_page(maddr);
+               if (get_page(page, current->domain) == 0)
+                       goto fail;
+               comm->va[i] = __va(maddr);
+               poff = buf & (MIN_PAGE_SIZE - 1);
+               if (likely(poff + size <= MIN_PAGE_SIZE))
+                       break;
+
+               /* crossing page boundary */
+               buf = buf - poff + MIN_PAGE_SIZE;
+       }
+       return fault;
+fail:
+       palcomm_done(comm);
+       return fault;
+}
+
+static int
+palcomm_copy_to_guest(struct palcomm_ctxt *comm,
+               unsigned long to, void *from, long size)
+{
+       long poff, len;
+
+       if (VMX_DOMAIN(current)) {
+               if (comm->va[0] == NULL)
+                       return -1;
+               poff = to & (MIN_PAGE_SIZE - 1);
+               if (poff + size <= MIN_PAGE_SIZE) {
+                       memcpy(comm->va[0], from, size);
+               } else {
+                       len = MIN_PAGE_SIZE - poff;
+                       memcpy(comm->va[0], from, len);
+                       memcpy(comm->va[1], from + len, size - len);
+               }
+               return 0;
+       } else {
+               return copy_to_user((void __user *)to, from, size);
+       }
+}
+
 
 void get_state_info_on(void *data) {
        struct smp_call_args_t *arg = data;
@@ -605,8 +698,9 @@ remote_pal_mc_drain(void *v)
        ia64_pal_mc_drain();
 }
 
-struct ia64_pal_retval
-xen_pal_emulator(unsigned long index, u64 in1, u64 in2, u64 in3)
+IA64FAULT
+xen_pal_emulator(struct ia64_pal_retval *ret,
+               unsigned long index, u64 in1, u64 in2, u64 in3)
 {
        unsigned long r9  = 0;
        unsigned long r10 = 0;
@@ -615,8 +709,10 @@ xen_pal_emulator(unsigned long index, u6
        unsigned long flags;
        int processor;
 
-       if (unlikely(running_on_sim))
-               return pal_emulator_static(index);
+       if (unlikely(running_on_sim)) {
+               *ret = pal_emulator_static(index);
+               return IA64_NO_FAULT;
+       }
 
        debugger_event(XEN_IA64_DEBUG_ON_PAL);
 
@@ -801,21 +897,23 @@ xen_pal_emulator(unsigned long index, u6
            case PAL_PERF_MON_INFO:
                {
                        unsigned long pm_buffer[16];
+                       struct palcomm_ctxt comm;
+                       IA64FAULT fault;
+
+                       fault = palcomm_init(&comm, in1, sizeof(pm_buffer));
+                       if (fault != IA64_NO_FAULT)
+                               return fault;
+
                        status = ia64_pal_perf_mon_info(
                                        pm_buffer,
                                        (pal_perf_mon_info_u_t *) &r9);
-                       if (status != 0) {
-                               while(1)
-                               printk("PAL_PERF_MON_INFO fails ret=%ld\n", 
status);
-                               break;
+                       if (status == PAL_STATUS_SUCCESS) {
+                               if (palcomm_copy_to_guest(
+                                               &comm, in1,
+                                               pm_buffer, sizeof(pm_buffer)))
+                                       status = PAL_STATUS_ERROR;
                        }
-                       if (copy_to_user((void __user *)in1,pm_buffer,128)) {
-                               while(1)
-                               printk("xen_pal_emulator: PAL_PERF_MON_INFO "
-                                       "can't copy to user!!!!\n");
-                               status = PAL_STATUS_UNIMPLEMENTED;
-                               break;
-                       }
+                       palcomm_done(&comm);
                }
                break;
            case PAL_CACHE_INFO:
@@ -837,10 +935,17 @@ xen_pal_emulator(unsigned long index, u6
                       consumes 10 mW, implemented and cache/TLB coherent.  */
                    unsigned long res = 1000UL | (1000UL << 16) | (10UL << 32)
                            | (1UL << 61) | (1UL << 60);
-                   if (copy_to_user ((void *)in1, &res, sizeof (res)))
-                           status = PAL_STATUS_EINVAL;    
+                   struct palcomm_ctxt comm;
+                   IA64FAULT fault;
+
+                   fault = palcomm_init(&comm, in1, sizeof(res));
+                   if (fault != IA64_NO_FAULT)
+                           return fault;
+                   if (palcomm_copy_to_guest(&comm, in1, &res, sizeof(res)))
+                           status = PAL_STATUS_ERROR;
                    else
                            status = PAL_STATUS_SUCCESS;
+                   palcomm_done(&comm);
                }
                break;
            case PAL_HALT:
@@ -885,9 +990,20 @@ xen_pal_emulator(unsigned long index, u6
            case PAL_BRAND_INFO:
                if (in1 == 0) {
                        char brand_info[128];
+                       struct palcomm_ctxt comm;
+                       IA64FAULT fault;
+
+                       fault = palcomm_init(&comm, in2, sizeof(brand_info));
+                       if (fault != IA64_NO_FAULT)
+                               return fault;
                        status = ia64_pal_get_brand_info(brand_info);
-                       if (status == PAL_STATUS_SUCCESS)
-                               copy_to_user((void *)in2, brand_info, 128);
+                       if (status == PAL_STATUS_SUCCESS) {
+                               if (palcomm_copy_to_guest(
+                                               &comm, in2, brand_info, 
+                                               sizeof(brand_info)))
+                                       status = PAL_STATUS_ERROR;
+                       }
+                       palcomm_done(&comm);
                } else {
                        status = PAL_STATUS_EINVAL;
                }
@@ -901,7 +1017,11 @@ xen_pal_emulator(unsigned long index, u6
                printk("%s: Unimplemented PAL Call %lu\n", __func__, index);
                break;
        }
-       return ((struct ia64_pal_retval) {status, r9, r10, r11});
+       ret->status = status;
+       ret->v0 = r9;
+       ret->v1 = r10;
+       ret->v2 = r11;
+       return IA64_NO_FAULT;
 }
 
 // given a current domain (virtual or metaphysical) address, return the 
virtual address
diff -r 4054cd60895b xen/arch/ia64/xen/hypercall.c
--- a/xen/arch/ia64/xen/hypercall.c     Mon Dec 10 13:49:22 2007 +0000
+++ b/xen/arch/ia64/xen/hypercall.c     Tue Dec 11 15:02:02 2007 +0900
@@ -184,12 +184,12 @@ ia64_hypercall(struct pt_regs *regs)
                        struct ia64_pal_retval y;
 
                        if (regs->r28 >= PAL_COPY_PAL)
-                               y = xen_pal_emulator
-                                       (regs->r28, vcpu_get_gr (v, 33),
+                               xen_pal_emulator
+                                       (&y, regs->r28, vcpu_get_gr (v, 33),
                                         vcpu_get_gr (v, 34),
                                         vcpu_get_gr (v, 35));
                        else
-                               y = xen_pal_emulator(regs->r28,regs->r29,
+                               xen_pal_emulator(&y, regs->r28,regs->r29,
                                                     regs->r30,regs->r31);
                        regs->r8 = y.status; regs->r9 = y.v0;
                        regs->r10 = y.v1; regs->r11 = y.v2;
diff -r 4054cd60895b xen/include/asm-ia64/dom_fw.h
--- a/xen/include/asm-ia64/dom_fw.h     Mon Dec 10 13:49:22 2007 +0000
+++ b/xen/include/asm-ia64/dom_fw.h     Tue Dec 11 15:02:02 2007 +0900
@@ -185,7 +185,8 @@
 
 #ifdef __XEN__
 #include <linux/efi.h>
-extern struct ia64_pal_retval xen_pal_emulator(u64, u64, u64, u64);
+#include <asm/ia64_int.h>
+extern IA64FAULT xen_pal_emulator(struct ia64_pal_retval *, u64, u64, u64, 
u64);
 extern struct sal_ret_values sal_emulator (long index, unsigned long in1, 
unsigned long in2, unsigned long in3, unsigned long in4, unsigned long in5, 
unsigned long in6, unsigned long in7);
 extern struct ia64_pal_retval pal_emulator_static (unsigned long);
 extern efi_status_t efi_emulator (struct pt_regs *regs, unsigned long *fault);
diff -r 4054cd60895b xen/include/asm-ia64/vmx_pal.h
--- a/xen/include/asm-ia64/vmx_pal.h    Mon Dec 10 13:49:22 2007 +0000
+++ b/xen/include/asm-ia64/vmx_pal.h    Tue Dec 11 15:02:02 2007 +0900
@@ -25,6 +25,7 @@
  */
 
 #include <xen/types.h>
+#include <asm/ia64_int.h>
 /* PAL PROCEDURE FOR VIRTUALIZATION */
 #define                PAL_VP_CREATE   265
 /* Stacked Virt. Initializes a new VPD for the operation of
@@ -115,7 +116,7 @@ ia64_pal_vp_save (u64 *vpd, u64 pal_proc
        PAL_CALL_STK(iprv, PAL_VP_SAVE, (u64)vpd, pal_proc_vector, 0);
        return iprv.status;
 }
-extern void pal_emul(struct vcpu *vcpu);
+extern IA64FAULT pal_emul(struct vcpu *vcpu);
 extern void sal_emul(struct vcpu *vcpu);
 #define PAL_PROC_VM_BIT                (1UL << 40)
 #define PAL_PROC_VMSW_BIT      (1UL << 54)
_______________________________________________
Xen-ia64-devel mailing list
Xen-ia64-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-ia64-devel

 


Rackspace

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