diff options
author | Jan Beulich <jbeulich@suse.com> | 2013-05-02 17:25:37 +0200 |
---|---|---|
committer | Jan Beulich <jbeulich@suse.com> | 2013-05-02 17:25:37 +0200 |
commit | 02615aaefd5c6c5856b4f81799bb6ec1ca0d89d0 (patch) | |
tree | 25274eb2ba9c70bfefe29d64c764101555517ca2 | |
parent | c6fad967aabeb98da8307e59f73474b23b42f0d1 (diff) | |
download | xen-02615aaefd5c6c5856b4f81799bb6ec1ca0d89d0.tar.gz xen-02615aaefd5c6c5856b4f81799bb6ec1ca0d89d0.tar.bz2 xen-02615aaefd5c6c5856b4f81799bb6ec1ca0d89d0.zip |
x86: make arch_set_info_guest() preemptible
.. as the root page table validation (and the dropping of an eventual
old one) can require meaningful amounts of time.
This is part of CVE-2013-1918 / XSA-45.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Tim Deegan <tim@xen.org>
master commit: 99d2b149915010e986f4d8778708c5891e7b4635
master date: 2013-05-02 16:38:30 +0200
-rw-r--r-- | xen/arch/x86/domain.c | 108 | ||||
-rw-r--r-- | xen/common/compat/domain.c | 4 | ||||
-rw-r--r-- | xen/common/domain.c | 5 | ||||
-rw-r--r-- | xen/common/domctl.c | 4 |
4 files changed, 83 insertions, 38 deletions
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index 696eff3b3b..9a3448867d 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -676,6 +676,7 @@ int arch_set_info_guest( { struct domain *d = v->domain; unsigned long cr3_pfn = INVALID_MFN; + struct page_info *cr3_page; unsigned long flags, cr4; int i, rc = 0, compat; @@ -815,72 +816,103 @@ int arch_set_info_guest( if ( rc != 0 ) return rc; + set_bit(_VPF_in_reset, &v->pause_flags); + if ( !compat ) - { cr3_pfn = gmfn_to_mfn(d, xen_cr3_to_pfn(c.nat->ctrlreg[3])); +#ifdef __x86_64__ + else + cr3_pfn = gmfn_to_mfn(d, compat_cr3_to_pfn(c.cmp->ctrlreg[3])); +#endif + cr3_page = mfn_to_page(cr3_pfn); - if ( !mfn_valid(cr3_pfn) || - (paging_mode_refcounts(d) - ? !get_page(mfn_to_page(cr3_pfn), d) - : !get_page_and_type(mfn_to_page(cr3_pfn), d, - PGT_base_page_table)) ) - { - destroy_gdt(v); - return -EINVAL; - } + if ( !mfn_valid(cr3_pfn) || !get_page(cr3_page, d) ) + { + cr3_page = NULL; + rc = -EINVAL; + } + else if ( paging_mode_refcounts(d) ) + /* nothing */; + else if ( cr3_page == v->arch.old_guest_table ) + { + v->arch.old_guest_table = NULL; + put_page(cr3_page); + } + else + { + /* + * Since v->arch.guest_table{,_user} are both NULL, this effectively + * is just a call to put_old_guest_table(). + */ + if ( !compat ) + rc = vcpu_destroy_pagetables(v); + if ( !rc ) + rc = get_page_type_preemptible(cr3_page, + !compat ? PGT_root_page_table + : PGT_l3_page_table); + if ( rc == -EINTR ) + rc = -EAGAIN; + } + if ( rc ) + /* handled below */; + else if ( !compat ) + { v->arch.guest_table = pagetable_from_pfn(cr3_pfn); #ifdef __x86_64__ if ( c.nat->ctrlreg[1] ) { cr3_pfn = gmfn_to_mfn(d, xen_cr3_to_pfn(c.nat->ctrlreg[1])); + cr3_page = mfn_to_page(cr3_pfn); - if ( !mfn_valid(cr3_pfn) || - (paging_mode_refcounts(d) - ? !get_page(mfn_to_page(cr3_pfn), d) - : !get_page_and_type(mfn_to_page(cr3_pfn), d, - PGT_base_page_table)) ) + if ( !mfn_valid(cr3_pfn) || !get_page(cr3_page, d) ) { - cr3_pfn = pagetable_get_pfn(v->arch.guest_table); - v->arch.guest_table = pagetable_null(); - if ( paging_mode_refcounts(d) ) - put_page(mfn_to_page(cr3_pfn)); - else - put_page_and_type(mfn_to_page(cr3_pfn)); - destroy_gdt(v); - return -EINVAL; + cr3_page = NULL; + rc = -EINVAL; + } + else if ( !paging_mode_refcounts(d) ) + { + rc = get_page_type_preemptible(cr3_page, PGT_root_page_table); + switch ( rc ) + { + case -EINTR: + rc = -EAGAIN; + case -EAGAIN: + v->arch.old_guest_table = + pagetable_get_page(v->arch.guest_table); + v->arch.guest_table = pagetable_null(); + break; + } } - v->arch.guest_table_user = pagetable_from_pfn(cr3_pfn); + if ( !rc ) + v->arch.guest_table_user = pagetable_from_pfn(cr3_pfn); } else if ( !(flags & VGCF_in_kernel) ) { - destroy_gdt(v); - return -EINVAL; + cr3_page = NULL; + rc = -EINVAL; } } else { l4_pgentry_t *l4tab; - cr3_pfn = gmfn_to_mfn(d, compat_cr3_to_pfn(c.cmp->ctrlreg[3])); - - if ( !mfn_valid(cr3_pfn) || - (paging_mode_refcounts(d) - ? !get_page(mfn_to_page(cr3_pfn), d) - : !get_page_and_type(mfn_to_page(cr3_pfn), d, - PGT_l3_page_table)) ) - { - destroy_gdt(v); - return -EINVAL; - } - l4tab = __va(pagetable_get_paddr(v->arch.guest_table)); *l4tab = l4e_from_pfn( cr3_pfn, _PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED); #endif } + if ( rc ) + { + if ( cr3_page ) + put_page(cr3_page); + destroy_gdt(v); + return rc; + } + + clear_bit(_VPF_in_reset, &v->pause_flags); if ( v->vcpu_id == 0 ) update_domain_wallclock_time(d); diff --git a/xen/common/compat/domain.c b/xen/common/compat/domain.c index 67e0e5e316..5fe393f16a 100644 --- a/xen/common/compat/domain.c +++ b/xen/common/compat/domain.c @@ -52,6 +52,10 @@ int compat_vcpu_op(int cmd, int vcpuid, XEN_GUEST_HANDLE(void) arg) rc = boot_vcpu(d, vcpuid, cmp_ctxt); domain_unlock(d); + if ( rc == -EAGAIN ) + rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih", + cmd, vcpuid, arg); + xfree(cmp_ctxt); break; } diff --git a/xen/common/domain.c b/xen/common/domain.c index 4f2efe504e..5fa045bf71 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -842,6 +842,11 @@ long do_vcpu_op(int cmd, int vcpuid, XEN_GUEST_HANDLE(void) arg) domain_unlock(d); xfree(ctxt); + + if ( rc == -EAGAIN ) + rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih", + cmd, vcpuid, arg); + break; case VCPUOP_up: diff --git a/xen/common/domctl.c b/xen/common/domctl.c index 65e30df1d3..c3240db03c 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -318,6 +318,10 @@ long do_domctl(XEN_GUEST_HANDLE(xen_domctl_t) u_domctl) domain_pause(d); ret = arch_set_info_guest(v, c); domain_unpause(d); + + if ( ret == -EAGAIN ) + ret = hypercall_create_continuation( + __HYPERVISOR_domctl, "h", u_domctl); } svc_out: |