$NetBSD: patch-CVE-2014-8866,v 1.1 2014/11/27 15:36:02 bouyer Exp $ x86: limit checks in hypercall_xlat_continuation() to actual arguments HVM/PVH guests can otherwise trigger the final BUG_ON() in that function by entering 64-bit mode, setting the high halves of affected registers to non-zero values, leaving 64-bit mode, and issuing a hypercall that might get preempted and hence become subject to continuation argument translation (HYPERVISOR_memory_op being the only one possible for HVM, PVH also having the option of using HYPERVISOR_mmuext_op). This issue got introduced when HVM code was switched to use compat_memory_op() - neither that nor hypercall_xlat_continuation() were originally intended to be used by other than PV guests (which can't enter 64-bit mode and hence have no way to alter the high halves of 64-bit registers). This is XSA-111. Signed-off-by: Jan Beulich Reviewed-by: Tim Deegan --- xen/arch/x86/domain.c.orig +++ xen/arch/x86/domain.c @@ -1921,7 +1921,8 @@ unsigned long hypercall_create_continuat } #ifdef CONFIG_COMPAT -int hypercall_xlat_continuation(unsigned int *id, unsigned int mask, ...) +int hypercall_xlat_continuation(unsigned int *id, unsigned int nr, + unsigned int mask, ...) { int rc = 0; struct mc_state *mcs = ¤t->mc_state; @@ -1930,7 +1931,10 @@ int hypercall_xlat_continuation(unsigned unsigned long nval = 0; va_list args; - BUG_ON(id && *id > 5); + ASSERT(nr <= ARRAY_SIZE(mcs->call.args)); + ASSERT(!(mask >> nr)); + + BUG_ON(id && *id >= nr); BUG_ON(id && (mask & (1U << *id))); va_start(args, mask); @@ -1939,7 +1943,7 @@ int hypercall_xlat_continuation(unsigned { if ( !test_bit(_MCSF_call_preempted, &mcs->flags) ) return 0; - for ( i = 0; i < 6; ++i, mask >>= 1 ) + for ( i = 0; i < nr; ++i, mask >>= 1 ) { if ( mask & 1 ) { @@ -1967,7 +1971,7 @@ int hypercall_xlat_continuation(unsigned else { regs = guest_cpu_user_regs(); - for ( i = 0; i < 6; ++i, mask >>= 1 ) + for ( i = 0; i < nr; ++i, mask >>= 1 ) { unsigned long *reg; --- xen/common/compat/memory.c.orig +++ xen/common/compat/memory.c @@ -208,7 +208,7 @@ int compat_memory_op(unsigned int cmd, X break; cmd = 0; - if ( hypercall_xlat_continuation(&cmd, 0x02, nat.hnd, compat) ) + if ( hypercall_xlat_continuation(&cmd, 2, 0x02, nat.hnd, compat) ) { BUG_ON(rc != __HYPERVISOR_memory_op); BUG_ON((cmd & MEMOP_CMD_MASK) != op); --- xen/include/xen/compat.h.orig 2013-09-10 08:42:18.000000000 +0200 +++ xen/include/xen/compat.h 2014-11-27 15:29:34.000000000 +0100 @@ -185,7 +185,8 @@ CHECK_FIELD_COMMON_(k, CHECK_NAME_(k, n ## __ ## f1 ## __ ## f2 ## __ ## \ f3, F2), n, f1.f2.f3) -int hypercall_xlat_continuation(unsigned int *id, unsigned int mask, ...); +int hypercall_xlat_continuation(unsigned int *id, unsigned int nr, + unsigned int mask, ...); /* In-place translation functons: */ struct start_info; --- xen/arch/x86/x86_64/compat/mm.c.orig 2013-09-10 08:42:18.000000000 +0200 +++ xen/arch/x86/x86_64/compat/mm.c 2014-11-27 15:21:15.000000000 +0100 @@ -128,7 +128,7 @@ break; if ( rc == __HYPERVISOR_memory_op ) - hypercall_xlat_continuation(NULL, 0x2, nat, arg); + hypercall_xlat_continuation(NULL, 2, 0x2, nat, arg); XLAT_pod_target(&cmp, nat); @@ -333,7 +333,7 @@ left = 1; if ( arg1 != MMU_UPDATE_PREEMPTED ) { - BUG_ON(!hypercall_xlat_continuation(&left, 0x01, nat_ops, + BUG_ON(!hypercall_xlat_continuation(&left, 4, 0x01, nat_ops, cmp_uops)); if ( !test_bit(_MCSF_in_multicall, &mcs->flags) ) regs->_ecx += count - i; @@ -341,7 +341,7 @@ mcs->compat_call.args[1] += count - i; } else - BUG_ON(hypercall_xlat_continuation(&left, 0)); + BUG_ON(hypercall_xlat_continuation(&left, 4, 0)); BUG_ON(left != arg1); } else