aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@suse.com>2013-05-02 16:39:06 +0200
committerJan Beulich <jbeulich@suse.com>2013-05-02 16:39:06 +0200
commita3e049f8e86fe18e3b87f18dc0c7be665026fd9f (patch)
tree8916d16e86cdefada74e050c0ffaf145f47439eb
parent99d2b149915010e986f4d8778708c5891e7b4635 (diff)
downloadxen-a3e049f8e86fe18e3b87f18dc0c7be665026fd9f.tar.gz
xen-a3e049f8e86fe18e3b87f18dc0c7be665026fd9f.tar.bz2
xen-a3e049f8e86fe18e3b87f18dc0c7be665026fd9f.zip
x86: make page table unpinning preemptible
... as it may take significant amounts of time. Since we can't re-invoke the operation in a second attempt, the continuation logic must be slightly tweaked so that we make sure do_mmuext_op() gets run one more time even when the preempted unpin operation was the last one in a batch. This is part of CVE-2013-1918 / XSA-45. Signed-off-by: Jan Beulich <jbeulich@suse.com> Acked-by: Tim Deegan <tim@xen.org>
-rw-r--r--xen/arch/x86/mm.c40
-rw-r--r--xen/arch/x86/x86_64/compat/mm.c23
2 files changed, 56 insertions, 7 deletions
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index 10e921780b..0dc90703ad 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -2859,6 +2859,14 @@ long do_mmuext_op(
return rc;
}
+ if ( unlikely(count == MMU_UPDATE_PREEMPTED) &&
+ likely(guest_handle_is_null(uops)) )
+ {
+ /* See the curr->arch.old_guest_table related
+ * hypercall_create_continuation() below. */
+ return (int)foreigndom;
+ }
+
if ( unlikely(count & MMU_UPDATE_PREEMPTED) )
{
count &= ~MMU_UPDATE_PREEMPTED;
@@ -2889,7 +2897,7 @@ long do_mmuext_op(
for ( i = 0; i < count; i++ )
{
- if ( hypercall_preempt_check() )
+ if ( curr->arch.old_guest_table || hypercall_preempt_check() )
{
rc = -EAGAIN;
break;
@@ -3009,7 +3017,17 @@ long do_mmuext_op(
break;
}
- put_page_and_type(page);
+ switch ( rc = put_page_and_type_preemptible(page, 1) )
+ {
+ case -EINTR:
+ case -EAGAIN:
+ curr->arch.old_guest_table = page;
+ rc = 0;
+ break;
+ default:
+ BUG_ON(rc);
+ break;
+ }
put_page(page);
/* A page is dirtied when its pin status is cleared. */
@@ -3318,9 +3336,27 @@ long do_mmuext_op(
}
if ( rc == -EAGAIN )
+ {
+ ASSERT(i < count);
rc = hypercall_create_continuation(
__HYPERVISOR_mmuext_op, "hihi",
uops, (count - i) | MMU_UPDATE_PREEMPTED, pdone, foreigndom);
+ }
+ else if ( curr->arch.old_guest_table )
+ {
+ XEN_GUEST_HANDLE_PARAM(void) null;
+
+ ASSERT(rc || i == count);
+ set_xen_guest_handle(null, NULL);
+ /*
+ * In order to have a way to communicate the final return value to
+ * our continuation, we pass this in place of "foreigndom", building
+ * on the fact that this argument isn't needed anymore.
+ */
+ rc = hypercall_create_continuation(
+ __HYPERVISOR_mmuext_op, "hihi", null,
+ MMU_UPDATE_PREEMPTED, null, rc);
+ }
put_pg_owner(pg_owner);
diff --git a/xen/arch/x86/x86_64/compat/mm.c b/xen/arch/x86/x86_64/compat/mm.c
index 9adc4b472b..f01f8ff8a6 100644
--- a/xen/arch/x86/x86_64/compat/mm.c
+++ b/xen/arch/x86/x86_64/compat/mm.c
@@ -268,6 +268,13 @@ int compat_mmuext_op(XEN_GUEST_HANDLE_PARAM(mmuext_op_compat_t) cmp_uops,
int rc = 0;
XEN_GUEST_HANDLE_PARAM(mmuext_op_t) nat_ops;
+ if ( unlikely(count == MMU_UPDATE_PREEMPTED) &&
+ likely(guest_handle_is_null(cmp_uops)) )
+ {
+ set_xen_guest_handle(nat_ops, NULL);
+ return do_mmuext_op(nat_ops, count, pdone, foreigndom);
+ }
+
preempt_mask = count & MMU_UPDATE_PREEMPTED;
count ^= preempt_mask;
@@ -370,12 +377,18 @@ int compat_mmuext_op(XEN_GUEST_HANDLE_PARAM(mmuext_op_compat_t) cmp_uops,
guest_handle_add_offset(nat_ops, i - left);
guest_handle_subtract_offset(cmp_uops, left);
left = 1;
- BUG_ON(!hypercall_xlat_continuation(&left, 0x01, nat_ops, cmp_uops));
- BUG_ON(left != arg1);
- if (!test_bit(_MCSF_in_multicall, &mcs->flags))
- regs->_ecx += count - i;
+ if ( arg1 != MMU_UPDATE_PREEMPTED )
+ {
+ BUG_ON(!hypercall_xlat_continuation(&left, 0x01, nat_ops,
+ cmp_uops));
+ if ( !test_bit(_MCSF_in_multicall, &mcs->flags) )
+ regs->_ecx += count - i;
+ else
+ mcs->compat_call.args[1] += count - i;
+ }
else
- mcs->compat_call.args[1] += count - i;
+ BUG_ON(hypercall_xlat_continuation(&left, 0));
+ BUG_ON(left != arg1);
}
else
BUG_ON(err > 0);