diff options
author | Jan Beulich <jbeulich@suse.com> | 2013-05-02 16:39:06 +0200 |
---|---|---|
committer | Jan Beulich <jbeulich@suse.com> | 2013-05-02 16:39:06 +0200 |
commit | a3e049f8e86fe18e3b87f18dc0c7be665026fd9f (patch) | |
tree | 8916d16e86cdefada74e050c0ffaf145f47439eb /xen/arch/x86/mm.c | |
parent | 99d2b149915010e986f4d8778708c5891e7b4635 (diff) | |
download | xen-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>
Diffstat (limited to 'xen/arch/x86/mm.c')
-rw-r--r-- | xen/arch/x86/mm.c | 40 |
1 files changed, 38 insertions, 2 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); |