diff options
author | Jan Beulich <jbeulich@suse.com> | 2013-05-02 16:34:21 +0200 |
---|---|---|
committer | Jan Beulich <jbeulich@suse.com> | 2013-05-02 16:34:21 +0200 |
commit | 6cdc9be2a5f2a87b4504404fbf648d16d9503c19 (patch) | |
tree | 57244e0df782cc38467cf1045314806967c72ba9 /xen/arch/x86/mm.c | |
parent | 9626d1c1fafe2da5af6e59478c9e9db6d03144df (diff) | |
download | xen-6cdc9be2a5f2a87b4504404fbf648d16d9503c19.tar.gz xen-6cdc9be2a5f2a87b4504404fbf648d16d9503c19.tar.bz2 xen-6cdc9be2a5f2a87b4504404fbf648d16d9503c19.zip |
x86: make vcpu_destroy_pagetables() preemptible
... as it may take significant amounts of time.
The function, being moved to mm.c as the better home for it anyway, and
to avoid having to make a new helper function there non-static, is
given a "preemptible" parameter temporarily (until, in a subsequent
patch, its other caller is also being made capable of dealing with
preemption).
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 | 86 |
1 files changed, 84 insertions, 2 deletions
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 0cd4203b62..9a076e9d36 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -2579,6 +2579,79 @@ static void put_superpage(unsigned long mfn) return; } +static int put_old_guest_table(struct vcpu *v) +{ + int rc; + + if ( !v->arch.old_guest_table ) + return 0; + + switch ( rc = put_page_and_type_preemptible(v->arch.old_guest_table, 1) ) + { + case -EINTR: + case -EAGAIN: + return -EAGAIN; + } + + v->arch.old_guest_table = NULL; + + return rc; +} + +int vcpu_destroy_pagetables(struct vcpu *v, bool_t preemptible) +{ + unsigned long mfn = pagetable_get_pfn(v->arch.guest_table); + struct page_info *page; + l4_pgentry_t *l4tab = NULL; + int rc = put_old_guest_table(v); + + if ( rc ) + return rc; + + if ( is_pv_32on64_vcpu(v) ) + { + l4tab = map_domain_page(mfn); + mfn = l4e_get_pfn(*l4tab); + } + + if ( mfn ) + { + page = mfn_to_page(mfn); + if ( paging_mode_refcounts(v->domain) ) + put_page(page); + else + rc = put_page_and_type_preemptible(page, preemptible); + } + + if ( l4tab ) + { + if ( !rc ) + l4e_write(l4tab, l4e_empty()); + unmap_domain_page(l4tab); + } + else if ( !rc ) + { + v->arch.guest_table = pagetable_null(); + + /* Drop ref to guest_table_user (from MMUEXT_NEW_USER_BASEPTR) */ + mfn = pagetable_get_pfn(v->arch.guest_table_user); + if ( mfn ) + { + page = mfn_to_page(mfn); + if ( paging_mode_refcounts(v->domain) ) + put_page(page); + else + rc = put_page_and_type_preemptible(page, preemptible); + } + if ( !rc ) + v->arch.guest_table_user = pagetable_null(); + } + + v->arch.cr3 = 0; + + return rc; +} + int new_guest_cr3(unsigned long mfn) { struct vcpu *curr = current; @@ -2733,12 +2806,21 @@ long do_mmuext_op( unsigned int foreigndom) { struct mmuext_op op; - int rc = 0, i = 0, okay; unsigned long type; - unsigned int done = 0; + unsigned int i = 0, done = 0; struct vcpu *curr = current; struct domain *d = curr->domain; struct domain *pg_owner; + int okay, rc = put_old_guest_table(curr); + + if ( unlikely(rc) ) + { + if ( likely(rc == -EAGAIN) ) + rc = hypercall_create_continuation( + __HYPERVISOR_mmuext_op, "hihi", uops, count, pdone, + foreigndom); + return rc; + } if ( unlikely(count & MMU_UPDATE_PREEMPTED) ) { |