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
|