[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-ia64-devel] [PATCH] Fix vulnerability of copy_to_user in PAL emulation
There is a security vulnerability in PAL emulation since alt-dtlb miss handler of HVM absolutely inserts a identity-mapped TLB when psr.vm=0. HVM guest can access an arbitrary machine physical memory with this security hole. Actually windows 2008 destroys the content of machine physical address 0x108000. This is a serious problem. I tried to support PV domain with the same logic too but it doesn't work well since PV domain can't hold more than one vtlb. Signed-off-by: Kouya Shimura <kouya@xxxxxxxxxxxxxx> 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 14:52:19 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 Mon Dec 10 18:39:23 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,9 @@ 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; + if ((fault = pal_emul(v)) == 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 Tue Dec 11 15:00:36 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,97 @@ 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)) + 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; + if ((maddr = paddr_to_maddr(paddr)) == 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 +697,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 +708,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 +896,22 @@ 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, 128); + 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, 128)) + 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 +933,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 +988,19 @@ 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, 128); + 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, 128)) + status = PAL_STATUS_ERROR; + } + palcomm_done(&comm); } else { status = PAL_STATUS_EINVAL; } @@ -901,7 +1014,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 Mon Dec 10 18:39:23 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 Mon Dec 10 18:39:23 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 Mon Dec 10 18:39:23 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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |